@import "tailwindcss/base";
@import 'common/fonts.css';
-@import 'common/menu.css';
+@import 'common/animations.css';
+@import 'common/overlays.css';
@import 'common/plyr.css';
@import 'common/global.css';
--- /dev/null
+/* Common values */
+.animate {
+ animation-duration: var(--animation-duration, 0.5s);
+ animation-delay: var(--animation-delay, 0s);
+ animation-fill-mode: var(--animation-fill-mode, both);
+}
+
+/*============================================================*/
+
+.animate-in-up {
+ @apply ease-out-quint;
+ animation-name: animate-in-up;
+ transform: translateY(var(--start-translate-y, 100%));
+}
+
+@keyframes animate-in-up {
+ 0% {
+ transform: translateY(var(--start-translate-y, 100%));
+ }
+ 100% {
+ transform: translateY(var(--end-translate-y, 0));
+ }
+}
+
+/*============================================================*/
+
+.animate-scale-x {
+ @apply ease-out-quart;
+ animation-name: animate-scale-x;
+ transform: scaleX(var(--start-scale-x, 0));
+}
+
+@keyframes animate-scale-x {
+ 0% {
+ transform: scaleX(var(--start-scale-x, 0));
+ }
+ 100% {
+ transform: scaleX(var(--start-scale-x, 1));
+ }
+}
+
+/*============================================================*/
+
+.animate-fade-in {
+ @apply ease-out-quart;
+ animation-name: animate-fade-in;
+ opacity: 0;
+}
+
+@keyframes animate-fade-in {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+/*============================================================*/
min-height: 100%;
}
+body.overlay-open {
+ /* Prevent scrollbars and scrolling when overlays are open */
+ overflow: hidden;
+}
+
b, strong {
@apply font-semibold;
}
+++ /dev/null
-body.menu-open {
- /* Prevent scrollbars and scrolling when menu is open */
- max-height: 100vh;
- overflow: hidden;
-}
-
-.menu-open .site-header {
- /* Header becomes fixed so that all elements are positioned at top, regardless of scroll position */
- @apply text-white;
- position: fixed;
- width: calc(100% - 4rem); /* Full width minus 2rem padding on each side */
-}
-
-.menu-open .header-logo {
- @apply text-white;
-}
--- /dev/null
+.overlay {
+ @apply fixed top-0 left-0 w-screen h-screen;
+ @apply flex flex-col items-center justify-center;
+ @apply px-30;
+ @apply transition ease-out-cubic duration-500;
+}
export default (options = {}) => ({
filters: options.filters || {}, // Filters JSON array is passed in from HTML
- filtersOpen: false, // Visibility of the filters overlay
activeTypeFilters: [],
activeThemeFilters: [],
isotope: {}, // Holds the Isotope instance
percentPosition: true,
},
- playerOpen: false, // Visibility of the media player overlay
player: {}, // Holds the Plyr instance
playerOptions: { // Settings used for Plyr instantiation (https://github.com/sampotts/plyr#options)
debug: false,
// Initialise Isotope
this.isotope = new Isotope(element, this.isotopeOptions);
+ // Entry animation: unhide grid and then tell Isotope to reveal items that were hidden during init
+ element.classList.remove('opacity-0');
+ this.isotope.revealItemElements(document.querySelectorAll('.media-item'));
+
// Update Isotope whenever active filters change
this.$watch('activeTypeFilters', () => this.applyFilters());
this.$watch('activeThemeFilters', () => this.applyFilters());
init() {
this.miniSearch = new MiniSearch(this.setup);
- this.miniSearch.addAll(searchData);
+ this.miniSearch.addAllAsync(searchData); // Load asynchronously for better performance
},
get results() {
--- /dev/null
+@props([
+ 'main' => false,
+ 'class' => '',
+])
+
+
+<header style="width: var(--content-width)"
+ class="{{ $main ? 'site-header relative' : 'absolute top-8 left-8 right-8' }}
+ {{ $class }}
+ flex justify-between
+ transition-colors duration-500
+ z-20">
+
+ {{-- LEFT SIDE --}}
+ {{-- Menu Toggle --}}
+ <a href="#" @click.prevent="menuOpen = !menuOpen">
+
+ {{-- Burger Menu Icon --}}
+ <svg x-show="!menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 33.494 26.495">
+ <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+ <path d="M1 1h31.494"/>
+ <path d="M1 13.248h31.494"/>
+ <path d="M1 25.495h31.494"/>
+ </g>
+ </svg>
+
+ {{-- Close Icon --}}
+ {{-- This icon is given the same width and height as the burger icon so it aligns properly (paths will be centred in the canvas) --}}
+ <svg x-cloak x-show="menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 25.098 25.098">
+ <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+ <path d="m1.414 23.683 22.27-22.27"/>
+ <path d="m1.414 1.414 22.27 22.27"/>
+ </g>
+ </svg>
+
+ <span class="ml-14">Menu</span>
+ </a>
+
+ {{-- CENTRE --}}
+ <div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
+ @if (isset($center))
+ {{ $center }}
+ @else
+ {{-- VEOLIA Logo --}}
+ <x-link class="header-logo {{ $logoClass ?? 'text-red' }}" href="/accueil">
+ <svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="130" height="31.878" viewBox="0 0 130 31.878"><path d="M13.704 31.795A16.13 16.13 0 0 1 .278 19.079a19.191 19.191 0 0 1-.189-5.063A16.014 16.014 0 0 1 13.598.139a19.613 19.613 0 0 1 5.536.127 16.063 16.063 0 0 1 11.993 10.7 15.252 15.252 0 0 1 .794 4.983 16.007 16.007 0 0 1-12.536 15.626 23.994 23.994 0 0 1-5.681.22Zm1.658-7.262a19.68 19.68 0 0 1-2.509-3.99 4.469 4.469 0 0 1-.4-2.247 3.587 3.587 0 0 1 .346-2.02 3.386 3.386 0 0 1 3.156-2.1c2.061-.012 3.576 1.758 3.567 4.166q-.009 2.367-2.468 5.525l-.947 1.215.914-.083a11.289 11.289 0 0 0 7.03-3.344 9.91 9.91 0 0 0 2.146-3.057 9.655 9.655 0 0 0 1.066-4.821 9.577 9.577 0 0 0-.262-2.748 11.391 11.391 0 0 0-12.229-8.536 11.425 11.425 0 0 0-9.762 8.536 13.065 13.065 0 0 0 .01 5.46 11.419 11.419 0 0 0 8.977 8.38 11.273 11.273 0 0 0 1.33.167l.468.011Zm63.629.811a12.141 12.141 0 0 1-3.307-1.111 7.234 7.234 0 0 1-3.274-5.455 16.528 16.528 0 0 1 .2-4.7 7.168 7.168 0 0 1 2.883-4.3 11.8 11.8 0 0 1 6.823-1.261c2.5.255 4.008.871 5.278 2.153a6.778 6.778 0 0 1 1.844 3.475 16.166 16.166 0 0 1 .051 5.776 6.754 6.754 0 0 1-5.2 5.254 16.019 16.019 0 0 1-5.298.17Zm4.092-3.055a3.074 3.074 0 0 0 1.737-1.57 7.571 7.571 0 0 0 .736-3.75c0-3.9-1.372-5.6-4.516-5.6a4.05 4.05 0 0 0-3.041.95c-1.035.93-1.478 2.323-1.478 4.647 0 3.219 1 5.013 3.1 5.541a7.933 7.933 0 0 0 3.461-.221Zm-39.092 2.622c-.062-.158-1.636-3.728-3.5-7.932s-3.387-7.689-3.387-7.744a6.066 6.066 0 0 1 1.548-.1c1.387 0 1.6.029 2.079.28a2.662 2.662 0 0 1 .841.753c.171.26 1.2 2.562 2.278 5.114s2.006 4.715 2.053 4.8a40.742 40.742 0 0 0 2.044-4.732c1.078-2.693 2.076-5.068 2.218-5.278a2.56 2.56 0 0 1 .789-.661c.484-.255.684-.28 2.246-.28h1.715l-3.31 7.323c-1.821 4.027-3.445 7.5-3.609 7.715-.506.666-1.126.9-2.6.966-1.278.068-1.298.062-1.406-.223Zm17.04-.007a6.749 6.749 0 0 1-4.9-5.126 12.989 12.989 0 0 1 0-5.393 7.238 7.238 0 0 1 3.253-4.507c1.366-.728 1.823-.787 6.226-.8l3.978-.008v2.893l-3.438.06-3.438.06-.794.391a2.765 2.765 0 0 0-1.13.877 4.562 4.562 0 0 0-.661 2.012v.274l4.55.032 4.55.032v2.772l-4.55.032-4.55.032v.266a5.392 5.392 0 0 0 .2.964 3.036 3.036 0 0 0 1.826 2.043c.647.257.9.276 4.094.318l3.405.045v2.873l-4.008-.013c-2.2-.007-4.279-.065-4.611-.128Zm35.54-.1a8.52 8.52 0 0 1-1.373-.486 5.251 5.251 0 0 1-1.992-2.091c-.544-1.188-.585-1.718-.59-7.609V9.073h1.266c1.094 0 1.323.036 1.688.264.806.505.777.3.844 5.943l.06 5.123.3.536a1.956 1.956 0 0 0 .9.844c.586.3.662.309 3.526.347l2.927.04v2.874l-3.345-.007a16.7 16.7 0 0 1-4.207-.233Zm9.869-7.2c.032-7.081.045-7.46.263-7.781.382-.563.851-.73 2.212-.785l1.235-.05v7.433c0 8.2.026 7.911-.759 8.39-.32.195-.609.235-1.685.236h-1.3Zm5.7 7.3c.034-.088 1.464-3.387 3.178-7.331 3.49-8.032 3.517-8.08 4.711-8.388a7.507 7.507 0 0 1 3.108-.019c.1.119 6.534 14.861 6.847 15.676.071.186-.066.2-1.505.2-2.346 0-2.719-.236-3.635-2.308l-.552-1.248-3.217-.032-3.217-.032-.124.325c-.068.179-.361.82-.652 1.426a3.114 3.114 0 0 1-1.039 1.455c-.5.345-.552.355-2.238.393-1.4.032-1.716.008-1.666-.121Zm11.231-6.518c-.92-2.207-2.181-5.116-2.249-5.19a38.91 38.91 0 0 0-2.253 4.949l-.164.392h2.365c1.844 0 2.35-.034 2.301-.151Z"/></svg>
+ </x-link>
+ @endif
+ </div>
+
+ {{-- RIGHT SIDE --}}
+ <div>
+ @if (isset($right))
+ {{ $right }}
+ @else
+ {{-- Search Icon --}}
+ {{-- Main header icon is hidden when overlay opens because it shifts position --}}
+ {{-- when body scrolling is disabled (due to being position: fixed) --}}
+ <a href="#" @click.prevent="openSearch()" @if($main) x-show="!overlayOpen" @endif>
+ <svg xmlns="http://www.w3.org/2000/svg"
+ class="stroke-current {{ $main ? 'fixed top-8 right-8' : 'absolute top-0 right-0' }}"
+ width="30"
+ height="30"
+ viewBox="0 0 29.414 29.414">
+ <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
+ <path d="M1 12.25A11.25 11.25 0 1 0 12.25 1 11.25 11.25 0 0 0 1 12.25Z"/>
+ <path d="m28 28-7.794-7.794"/>
+ </g>
+ </svg>
+ <span class="mr-14">Recherche</span>
+ </a>
+ @endif
+ </div>
+
+</header>
@aware(['types', 'themes'])
-<div class="filters-overlay
- fixed top-0 left-0 w-screen h-screen
- flex items-center
+<div class="overlay filters-overlay
bg-blue text-white
- px-30
- z-20
- transition ease-out-cubic duration-500"
+ z-20"
x-show="filtersOpen"
- x-transition:enter-start="opacity-0"
- x-transition:enter-end="opacity-100"
- x-transition:leave-start="opacity-100"
- x-transition:leave-end="opacity-0"
+ x-transition:enter.opacity.duration.500ms
+ x-transition:leave.opacity.duration.200ms
x-cloak>
{{-- Close --}}
- <x-close @click.prevent="filtersOpen = false" />
+ <div x-show="filtersOpen"
+ x-transition:enter.opacity.duration.500ms.delay.300ms
+ x-transition:leave.opacity.duration.0ms.delay.0ms
+ class="absolute top-8 right-8">
+ <x-close @click.prevent="filtersOpen = false" class="flex" />
+ </div>
<div class="w-full">
{{-- FILTERS --}}
-<div class="mt-10 space-x-2 space-y-2">
+<div class="mt-8 space-x-2 space-y-2">
{{-- FILTER INTERFACE BUTTON --}}
<a @click.prevent="filtersOpen = true"
href="#"
class="inline-block py-4 px-6 rounded-full
- bg-black hover:bg-blue
- mt-2 {{-- Margin top added here so layout doesn't shift when last filter is removed --}}
- border border-black hover:border-blue {{-- Needs border so it matches the height of filter buttons --}}
- text-white font-secondary font-medium leading-none">
+ bg-black hover:bg-blue
+ mt-2 {{-- Margin top added here so layout doesn't shift when last filter is removed --}}
+ border border-black hover:border-blue {{-- Needs border so it matches the height of filter buttons --}}
+ text-white font-secondary font-medium leading-none
+ animate animate-fade-in"
+ style="--animation-duration: 0.5s; --animation-delay: 0.5s;">
Filtrer
</a>
{{-- MEDIA LIBRARY GRID --}}
{{-- Negative margins applied here to offset margins used in Isotope grid --}}
- <div class="media-grid mt-10 -mb-16 -mx-2.5" x-init="initIsotope($el)">
+ <div class="media-grid opacity-0 mt-10 -mb-16 -mx-2.5" x-init="initIsotope($el)">
@foreach ($media as $item)
- <div @click="openPlayer({
+
+ {{-- TODO: deep-linking - is it actually needed here? Probably won't be linking directly to media... Could use replaceState() to update URL with querystring when opening / closing media. Check this on init / page load so that player can open the overlay already. Since the player needs several details, it's better to store this all in a data attribute and load the player based on the ID of the media / element that contains the data --}}
+
+ <div id="media_{{ $item['id'] }}"
+ @click="openPlayer({
player: {
type: '{{ $item['type'] }}', // video / audio
sources: [{
})"
class="media-item group cursor-pointer
{{-- Width is 25% minus the gutters (2 * 0.625rem that comes from mx-2.5) --}}
- float-left w-[calc(33%-1.25rem)] lg:w-[calc(25%-1.25rem)] mx-2.5 mb-16
+ float-left w-[calc(100%/2-1.25rem)] md:w-[calc(100%/3-1.25rem)] lg:w-[calc(100%/4-1.25rem)] mx-2.5 mb-16
media-{{ $item['type'] }}
theme-{{ $item['theme']['id'] }}"
>
{{-- Plyr custom icon SVG sprite --}}
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="hidden">
<symbol id="plyr-airplay" viewBox="0 0 18 18"><path d="M16 1H2a1 1 0 00-1 1v10a1 1 0 001 1h3v-2H3V3h12v8h-2v2h3a1 1 0 001-1V2a1 1 0 00-1-1z"/><path d="M4 17h10l-5-6z"/></symbol>
{{-- Embed SVG sprite directly --}}
<x-media-library.player-icons />
-<div class="player-overlay
- fixed top-0 left-0 w-screen h-screen
- flex items-center
+<div class="overlay player-overlay
bg-white text-black
- px-30
- z-20
- transition ease-out-cubic duration-500"
+ z-20"
x-show="playerOpen"
- x-transition:enter-start="opacity-0"
- x-transition:enter-end="opacity-100"
- x-transition:leave-start="opacity-100"
- x-transition:leave-end="opacity-0"
+ x-transition:enter.opacity.duration.500ms
+ x-transition:leave.opacity.duration.200ms
x-cloak>
<x-close @click.prevent="closePlayer()" />
<script>
@php
// TODO: Consider fetching this via JS only when search is used
+
+ // TODO: need to change Cubist/minisearch to output data differently but until then, can test it by copying the output manually into a .json file. Then use await / fetch to get data only when needed?
echo file_get_contents(storage_path('search.js'));
+ //echo 'var documents = [];';
@endphp
</script>
@endpush
-<div class="search-overlay
- fixed top-0 left-0 w-screen h-screen
- flex flex-col
+<div class="overlay search-overlay
bg-white text-black
- px-30
- z-20
- transition ease-out-cubic duration-500"
+ z-20"
{{-- Pass search index [documents] to component for setup --}}
{{-- See js/search.js --}}
x-data="search(documents)"
x-show="searchOpen"
- x-transition:enter-start="opacity-0"
- x-transition:enter-end="opacity-100"
- x-transition:leave-start="opacity-100"
- x-transition:leave-end="opacity-0"
+ x-transition:enter.opacity.duration.500ms
+ x-transition:leave.opacity.duration.200ms
x-cloak>
- <x-close @click.prevent="closeSearch()" />
+ <x-header>
+ <x-slot name="right">
+ <div x-show="searchOpen && !menuOpen" x-transition:enter.opacity.duration.500ms.delay.300ms x-transition:leave.opacity.duration.0ms.delay.0ms>
+ <x-close
+ x-show="searchOpen && !menuOpen"
+ @click.prevent="closeSearch()"
+ class="flex"
+ />
+ </div>
+ </x-slot>
+ </x-header>
+
{{-- Search Field --}}
<div class="w-full pt-30 flex-grow-0">
- <p class="font-medium text-2xl">Tapez ici le ou les mots-clé de votre recherche</p>
-
- <input
- id="searchField"
- x-model="query"
- @keyup.enter="$el.blur()" {{-- Remove focus on enter in order to hide virtual keyboard --}}
- type="search"
- placeholder="Recherche"
- class="appearance-none outline-none
- mt-8 w-full
- font-semibold text-6xl uppercase
- border-b-2 border-black">
+ <div class="overflow-hidden font-medium text-2xl">
+ <p class="animate"
+ :class="{ 'animate-in-up': searchOpen }"
+ style="--animation-delay: 0.25s; --animation-duration: 0.7s;">
+ Tapez ici le ou les mots-clé de votre recherche
+ </p>
+ </div>
+
+ <div class="overflow-hidden">
+ <input
+ id="searchField"
+ type="search"
+ placeholder="Recherche"
+ @keyup.enter="$el.blur()" {{-- Remove focus on enter in order to hide virtual keyboard --}}
+ x-model="query"
+ class="appearance-none outline-none
+ mt-8 w-full
+ font-semibold text-6xl uppercase
+ animate"
+ :class="{ 'animate-in-up': searchOpen }"
+ style="--animation-delay: 0.35s; --animation-duration: 0.7s;"
+ >
+ </div>
+ <div class="bg-black h-[2px]
+ transform origin-left scale-x-0
+ animate"
+ :class="{ 'animate-scale-x': searchOpen }"
+ style="--animation-delay: 0.5s; --animation-duration: 1.5s;"></div>
</div>
{{-- Search Results --}}
- <div class="flex-1 max-h-full overflow-y-auto">
+ <div class="flex-1 max-h-full overflow-y-auto"
+ x-show="searchOpen"
+ x-transition:enter.opacity.duration.500ms.delay.1500ms
+ x-transition:leave.opacity.delay.0ms>
<div x-show="query !== ''" class="font-secondary font-medium">
<p x-text="resultCount" class="mt-15 opacity-50"></p>
];
@endphp
- <div class="flex flex-col flex-1 -mx-22 -mt-22">
+ <div x-data="{ ready: true }"
+ :class="{ 'opacity-0': !ready }" {{-- opacity-0 will be removed as soon as Alpine fires --}}
+ class="flex flex-col flex-1 -mx-22 -mt-22 opacity-0">
{{-- Main home content --}}
<div x-cloak x-data="{ shown: false }" x-intersect="shown = true"
<div class="home-left flex flex-1 flex-col justify-center items-center p-6 max-w-[360px]">
@if($logo)
<img class="mb-10" src="{{ $logo }}" alt="Logo"
- x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.800ms.delay.200ms>
+ x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.800ms>
@endif
@if($subtitle)
<p class="text-center mb-5 max-w-[280px]"
- x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.300ms>
+ x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.100ms>
{{ $subtitle }}
</p>
@endif
@if($button)
<a class="bg-blue text-white py-4 px-10 rounded-full transition transform hover:scale-105"
href="{{ $button_link }}"
- x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.300ms>
+ x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.100ms>
{{ $button }}
</a>
@endif
bg-contain bg-center bg-no-repeat
transform transition duration-300"
x-show="shown"
- x-transition.opacity.scale.50.duration.1000ms.delay.300ms
+ x-transition.opacity.scale.50.duration.1000ms.delay.100ms
style="@if($illustration)background-image:url({{ $illustration }})@endif">
</div>
</div>
// TEMPORARY DATA MOCKUP
$media = [
[
+ 'id' => 123,
'title' => "Qu’est ce que la gouvernance ?",
'type' => 'video',
'duration' => '78',
],
],
[
+ 'id' => 545,
'title' => 'Les outils de communication',
'type' => 'video',
'duration' => '192',
],
],
[
+ 'id' => 785,
'title' => "Une organisation à plusieurs niveaux",
'type' => 'audio',
'duration' => '144',
],
],
[
+ 'id' => 65,
'title' => 'Système d’information',
'type' => 'audio',
'duration' => '987',
],
],
[
+ 'id' => 146,
'title' => "Qu’est ce que la gouvernance ?",
'type' => 'video',
'duration' => '614',
],
],
[
+ 'id' => 370,
'title' => 'Les outils de communication',
'type' => 'audio',
'duration' => '45',
];
@endphp
- <h1 class="uppercase">Médiathèque</h1>
+ <div class="overflow-hidden py-2">{{-- vertical padding so accents don't get cropped --}}
+ <h1 class="uppercase animate animate-in-up"
+ style="--animation-duration: 0.7s;">
+ Médiathèque
+ </h1>
+ </div>
{{-- Media library (components/media-library/index.blade.php) --}}
<x-media-library :themes="$themes" :types="$media_types" :media="$media"></x-media-library>
@extends('layouts.app')
-@section('content')
-
- @php
+@push('extra_body_classes')
+ overflow-y-scroll {{-- We already know this will be a long page so this avoids the header jumping on load --}}
+@endpush
- @endphp
+@section('content')
{{-- RESOURCES --}}
<div class="resources" x-data="{
- PDFOpen: false, // Tracks if PDF viewer overlay is open
viewerURL: '/tools/fluidbookpreview/pdfjs/web/viewer.html?file=', // Base URL for viewer
shown: false,
- openPDF(URL) {
- this.overlayOpen = true; // Stops page scrolling in the background
+ init() {
+ // Open PDF viewer if querystring is already set
+ let querystring = new URLSearchParams(location.search);
+ if (querystring.get('file')) {
+ this.openPDF(querystring.get('file'), false);
+ }
+ },
+
+ openPDF(PDF_URL, updateQuerystring = true) {
+ if (updateQuerystring) {
+ const location = new URL(window.location.href);
+ location.searchParams.set('file', PDF_URL);
+ history.replaceState(null, document.title, location.toString());
+ }
+
this.PDFOpen = true;
$nextTick(() => { $refs.PDFViewer.setAttribute('src', this.viewerURL + URL) });
},
closePDF() {
this.PDFOpen = false;
- this.overlayOpen = false;
+ $refs.PDFViewer.setAttribute('src', '');
+
+ // Update the page URL to remove querystring
+ const location = new URL(window.location.href);
+ location.searchParams.delete('file');
+ history.replaceState(null, document.title, location.toString());
},
}" x-intersect="shown = true" x-cloak>
{{-- DOCUMENTS --}}
<div class="grid lg:grid-cols-2 gap-6">
@foreach($resources['documents'] as $doc)
- <a href="#" @click.prevent="openPDF('/storage/242/fluidbook.pdf')" class="group">
- <div class="bg-blue flex items-center p-10 text-white rounded-md">
+ {{--<a href="{{ $doc['document_pdf'] }}" @click.prevent="openPDF($el.attributes.href.value)" class="group">--}}
+ <a href="/storage/242/fluidbook.pdf" @click.prevent="openPDF($el.attributes.href.value)" class="group">
+ <div class="bg-blue h-full flex items-center p-10 text-white rounded-md">
<img class="w-1/2 pr-6" src="/storage/107/groupe-133-at-2x.png" alt="{{ $doc['document_title'] }}">
{{-- <img class="w-1/2 pr-6" src="{{ $doc['document_image'] }}" alt="{{ $doc['document_title'] }}">--}}
<div class="-mr-5 space-y-4">
{{-- MEMOS --}}
<div class="grid grid-cols-2 gap-5 mt-8">
@foreach ($subchapter['subchapter_memos'] as $memo)
- <a href="#" @click.prevent="openPDF('/storage/242/fluidbook.pdf')" class="group">
+ {{--<a href="{{ $memo['memo_pdf'] }}" @click.prevent="openPDF($el.attributes.href.value)" class="group">--}}
+ <a href="/storage/242/fluidbook.pdf" @click.prevent="openPDF($el.attributes.href.value)" class="group">
<div class="bg-grey-50 flex items-center p-8 rounded-md">
<img class="w-1/4 pr-4"
{{-- src="{{ $memo['memo_image'] }}"--}}
</div>
{{-- PDF Viewer Overlay --}}
- <div class="pdf-overlay
- fixed top-0 left-0 w-screen h-screen
- flex flex-col
+ <div class="overlay pdf-overlay
bg-white text-black
- px-30 pt-20
- z-10
- transition ease-out-cubic duration-500"
+ pt-20
+ z-20"
x-show="PDFOpen"
- x-transition:enter-start="opacity-0"
- x-transition:enter-end="opacity-100"
- x-transition:leave-start="opacity-100"
- x-transition:leave-end="opacity-0"
+ x-transition:enter.opacity.duration.500ms
+ x-transition:leave.opacity.duration.200ms
x-cloak>
{{-- PDF Viewer iframe --}}
<iframe x-ref="PDFViewer" src="" frameborder="0"
class="absolute left-0 w-full top-[90px] h-[calc(100vh-90px)]"></iframe>
+ <x-header>
+ <x-slot name="right">
+ <div x-show="PDFOpen && !menuOpen" x-transition:enter.opacity.duration.500ms.delay.300ms x-transition:leave.opacity.duration.0ms.delay.0ms>
+ <x-close
+ x-show="PDFOpen"
+ @click.prevent="closePDF()"
+ class="flex"
+ />
+ </div>
+ </x-slot>
+ </x-header>
</div>
- {{-- Close button sits outside overlay so it can sit above and cover header search button --}}
- {{-- TODO: Rework this so it's less awkward when the main menu is opened on top of the PDF viewer --}}
- {{-- TODO: instead of using the same main element, consider having a header widget that can be used to redisplay header inside overlays (with option to disable / replace items via slots --}}
- <x-close @click.prevent="closePDF()"
- x-show="PDFOpen && !menuOpen" {{-- Don't show close button if menu gets opened --}}
- x-transition:enter="delay-300 duration-300"
- x-transition:enter-start="opacity-0"
- x-transition:enter-end="opacity-100"
- x-transition:leave-start="opacity-100"
- x-transition:leave-end="opacity-0"
- class="fixed top-8 right-8 flex z-30 bg-white pb-4 pl-4"
- x-cloak />
-
-
</div>
@endsection
@extends('layouts.base')
+@section('body_tag')
+ {{-- Make sure no scrollbars are present because they affect the background scaling --}}
+ <body class="overflow-hidden">
+@endsection
+
@section('main')
{{-- Title + Illustration --}}
<img x-show="shown"
x-transition.opacity.scale.95.origin.center.duration.1200ms.delay.800ms
class="max-w-[520px]"
+ style="backface-visibility: hidden"
src="{{ asset('images/splash-illustration.png') }}">
</x-link>
@extends('layouts.base')
+@push('before_scripts')
+ <script>
+ function app() {
+ return {
+ menuOpen: false,
+ searchOpen: false,
+ PDFOpen: false, // Resources PDF overlay
+ filtersOpen: false, // Media libary filters overlay
+ playerOpen: false, // Media player overlay
+
+ init() {
+ setTimeout(this.measureWidth, 2000);
+
+ // When an overlay opens, freeze the scrolling on the body
+ this.$watch('overlayOpen', (isOpen) => {
+ if (isOpen) {
+ document.body.classList.add('overlay-open');
+ } else {
+ document.body.classList.remove('overlay-open');
+ }
+ });
+ },
+
+ get overlayOpen() {
+ return this.menuOpen || this.searchOpen || this.PDFOpen || this.filtersOpen || this.playerOpen;
+ },
+
+ measureWidth() {
+ // Measure available space for header after padding and scrollbars are accounted for
+ let body = getComputedStyle(document.body);
+ let width = parseFloat(body.width);
+ width -= parseFloat(body.paddingLeft) + parseFloat(body.paddingRight);
+ document.body.style.setProperty('--content-width', `${width}px`)
+ },
+
+ openSearch() {
+ this.menuOpen = false;
+ this.searchOpen = true;
+ this.$nextTick(() => {
+ // Delay focusing on the search field, otherwise it will interfere with animations
+ setTimeout(() => document.getElementById('searchField').focus(), 1500);
+ });
+ },
+
+ closeSearch() {
+ this.searchOpen = false;
+ },
+ }
+ }
+ </script>
+@endpush
+
@section('body_tag')
- <body x-data="{
- overlayOpen: false, // Tracks when a modal is open so background scroll can be frozen
- menuOpen: false,
- searchOpen: false,
- openSearch() {
- this.overlayOpen = true;
- this.searchOpen = true;
- $nextTick(() => {
- document.getElementById('searchField').focus();
- });
- },
- closeSearch() {
- this.overlayOpen = false;
- this.searchOpen = false;
- },
- }"
- :class="{
- 'menu-open': menuOpen,
- 'overflow-hidden': overlayOpen
- }"
- class="font-primary p-8">
+ <body x-data="app()"
+ @resize.window.debounce="measureWidth()"
+ class="font-primary p-8 @stack('extra_body_classes')">
@endsection
@section('main')
-
- <header class="site-header relative flex justify-between z-20 transition-colors duration-500">
- {{-- Menu Toggle --}}
- <a href="#" @click.prevent="menuOpen = !menuOpen">
-
- {{-- Burger Menu Icon --}}
- <svg x-show="!menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 33.494 26.495">
- <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
- <path d="M1 1h31.494"/>
- <path d="M1 13.248h31.494"/>
- <path d="M1 25.495h31.494"/>
- </g>
- </svg>
-
- {{-- Close Icon --}}
- {{-- This icon is given the same width and height as the burger icon so it aligns properly (paths will be centred in the canvas) --}}
- <svg x-cloak x-show="menuOpen" class="stroke-current fixed" xmlns="http://www.w3.org/2000/svg" width="33" height="26" viewBox="0 0 25.098 25.098">
- <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
- <path d="m1.414 23.683 22.27-22.27"/>
- <path d="m1.414 1.414 22.27 22.27"/>
- </g>
- </svg>
-
- <span class="ml-14">Menu</span>
- </a>
-
- {{-- VEOLIA Logo --}}
- <x-link class="header-logo text-red" href="/accueil">
- <svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="130" height="31.878" viewBox="0 0 130 31.878"><path d="M13.704 31.795A16.13 16.13 0 0 1 .278 19.079a19.191 19.191 0 0 1-.189-5.063A16.014 16.014 0 0 1 13.598.139a19.613 19.613 0 0 1 5.536.127 16.063 16.063 0 0 1 11.993 10.7 15.252 15.252 0 0 1 .794 4.983 16.007 16.007 0 0 1-12.536 15.626 23.994 23.994 0 0 1-5.681.22Zm1.658-7.262a19.68 19.68 0 0 1-2.509-3.99 4.469 4.469 0 0 1-.4-2.247 3.587 3.587 0 0 1 .346-2.02 3.386 3.386 0 0 1 3.156-2.1c2.061-.012 3.576 1.758 3.567 4.166q-.009 2.367-2.468 5.525l-.947 1.215.914-.083a11.289 11.289 0 0 0 7.03-3.344 9.91 9.91 0 0 0 2.146-3.057 9.655 9.655 0 0 0 1.066-4.821 9.577 9.577 0 0 0-.262-2.748 11.391 11.391 0 0 0-12.229-8.536 11.425 11.425 0 0 0-9.762 8.536 13.065 13.065 0 0 0 .01 5.46 11.419 11.419 0 0 0 8.977 8.38 11.273 11.273 0 0 0 1.33.167l.468.011Zm63.629.811a12.141 12.141 0 0 1-3.307-1.111 7.234 7.234 0 0 1-3.274-5.455 16.528 16.528 0 0 1 .2-4.7 7.168 7.168 0 0 1 2.883-4.3 11.8 11.8 0 0 1 6.823-1.261c2.5.255 4.008.871 5.278 2.153a6.778 6.778 0 0 1 1.844 3.475 16.166 16.166 0 0 1 .051 5.776 6.754 6.754 0 0 1-5.2 5.254 16.019 16.019 0 0 1-5.298.17Zm4.092-3.055a3.074 3.074 0 0 0 1.737-1.57 7.571 7.571 0 0 0 .736-3.75c0-3.9-1.372-5.6-4.516-5.6a4.05 4.05 0 0 0-3.041.95c-1.035.93-1.478 2.323-1.478 4.647 0 3.219 1 5.013 3.1 5.541a7.933 7.933 0 0 0 3.461-.221Zm-39.092 2.622c-.062-.158-1.636-3.728-3.5-7.932s-3.387-7.689-3.387-7.744a6.066 6.066 0 0 1 1.548-.1c1.387 0 1.6.029 2.079.28a2.662 2.662 0 0 1 .841.753c.171.26 1.2 2.562 2.278 5.114s2.006 4.715 2.053 4.8a40.742 40.742 0 0 0 2.044-4.732c1.078-2.693 2.076-5.068 2.218-5.278a2.56 2.56 0 0 1 .789-.661c.484-.255.684-.28 2.246-.28h1.715l-3.31 7.323c-1.821 4.027-3.445 7.5-3.609 7.715-.506.666-1.126.9-2.6.966-1.278.068-1.298.062-1.406-.223Zm17.04-.007a6.749 6.749 0 0 1-4.9-5.126 12.989 12.989 0 0 1 0-5.393 7.238 7.238 0 0 1 3.253-4.507c1.366-.728 1.823-.787 6.226-.8l3.978-.008v2.893l-3.438.06-3.438.06-.794.391a2.765 2.765 0 0 0-1.13.877 4.562 4.562 0 0 0-.661 2.012v.274l4.55.032 4.55.032v2.772l-4.55.032-4.55.032v.266a5.392 5.392 0 0 0 .2.964 3.036 3.036 0 0 0 1.826 2.043c.647.257.9.276 4.094.318l3.405.045v2.873l-4.008-.013c-2.2-.007-4.279-.065-4.611-.128Zm35.54-.1a8.52 8.52 0 0 1-1.373-.486 5.251 5.251 0 0 1-1.992-2.091c-.544-1.188-.585-1.718-.59-7.609V9.073h1.266c1.094 0 1.323.036 1.688.264.806.505.777.3.844 5.943l.06 5.123.3.536a1.956 1.956 0 0 0 .9.844c.586.3.662.309 3.526.347l2.927.04v2.874l-3.345-.007a16.7 16.7 0 0 1-4.207-.233Zm9.869-7.2c.032-7.081.045-7.46.263-7.781.382-.563.851-.73 2.212-.785l1.235-.05v7.433c0 8.2.026 7.911-.759 8.39-.32.195-.609.235-1.685.236h-1.3Zm5.7 7.3c.034-.088 1.464-3.387 3.178-7.331 3.49-8.032 3.517-8.08 4.711-8.388a7.507 7.507 0 0 1 3.108-.019c.1.119 6.534 14.861 6.847 15.676.071.186-.066.2-1.505.2-2.346 0-2.719-.236-3.635-2.308l-.552-1.248-3.217-.032-3.217-.032-.124.325c-.068.179-.361.82-.652 1.426a3.114 3.114 0 0 1-1.039 1.455c-.5.345-.552.355-2.238.393-1.4.032-1.716.008-1.666-.121Zm11.231-6.518c-.92-2.207-2.181-5.116-2.249-5.19a38.91 38.91 0 0 0-2.253 4.949l-.164.392h2.365c1.844 0 2.35-.034 2.301-.151Z"/></svg>
- </x-link>
-
-
- {{-- Search Icon --}}
- <a href="#" @click.prevent="openSearch()">
- <svg class="stroke-current fixed top-8 right-8" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 29.414 29.414">
- <g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
- <path d="M1 12.25A11.25 11.25 0 1 0 12.25 1 11.25 11.25 0 0 0 1 12.25Z"/>
- <path d="m28 28-7.794-7.794"/>
- </g>
- </svg>
- <span class="mr-14">Recherche</span>
- </a>
- </header>
-
- {{-- Header spacer: same height as the header in the normal flow --}}
- {{-- This is needed because the header switches to position:fixed when menu is open and therefore no longer --}}
- {{-- takes any space in the document flow. This spacer is the replacement and prevents content from shifting. --}}
- <div class="h-8" x-show="menuOpen" x-cloak></div>
-
- <div class="min-h-content flex flex-col mx-22 pt-22">
- @yield('content')
+ {{--
+ Content has constrained width so that nothing shifts when overlay is opened and scrolling is disabled.
+ The body width changes when scrollbars are removed, which causes alignment problems, especially with
+ fixed-position elements.
+ --}}
+ <div style="width: var(--content-width)">
+ <x-header main="true" />
+
+ <div class="min-h-content flex flex-col mx-22 pt-22">
+ @yield('content')
+ </div>
</div>
{{-- MENU OVERLAY --}}
- <div class="menu-overlay
- fixed top-0 left-0 w-screen h-screen
- flex items-center
+ <div class="overlay menu-overlay
bg-blue text-white
- px-30
- z-10
- transition ease-out-cubic duration-500"
+ z-30"
x-show="menuOpen"
- x-transition:enter-start="opacity-0"
- x-transition:enter-end="opacity-100"
- {{--x-transition:leave="delay-1000"--}}
- x-transition:leave-start="opacity-100"
- x-transition:leave-end="opacity-0"
+ x-transition.opacity.duration.500ms
+ x-transition:leave.opacity.duration.500ms.delay.500ms
x-cloak>
+ <x-header logo-class="text-white" />
+
<ul class="w-full font-medium text-6xl">
@php
$menu_links = [
{{-- Extra padding and negative margin added so hover scale effect isn't clipped --}}
<li class="overflow-hidden pl-4 -ml-4">
<div x-show="menuOpen"
+ @click.prevent="menuOpen = false; setTimeout(() => window.location = $event.target.href, 200);"
class="py-8
transition transform ease-out-quint"
style="transition-delay: {{ 250 + (50 * $loop->index) }}ms"
x-transition:enter="duration-1000"
x-transition:enter-start="translate-y-[100px]"
x-transition:enter-end="translate-x-0"
- {{--x-transition:leave="duration-500"
+ x-transition:leave="duration-500"
x-transition:leave-start="translate-x-0"
- x-transition:leave-end="translate-y-[100px]"--}}>
- <x-link class="inline-block text-current transition-transform transform duration-200 hover:scale-105"
- href="{{ $link }}">
+ x-transition:leave-end="translate-y-[100px]">
+ <x-link
+ href="{{ $link }}"
+ class="inline-block text-current
+ transform origin-bottom-left
+ transition-transform duration-200
+ hover:scale-105">
{{ $text }}
</x-link>
</div>
x-transition:enter="duration-2000"
x-transition:enter-start="scale-x-0"
x-transition:enter-end="scale-x-100"
- {{--x-transition:leave="duration-1000"
+ x-transition:leave="duration-500"
x-transition:leave-start="scale-x-100"
- x-transition:leave-end="scale-x-0"--}}>
+ x-transition:leave-end="scale-x-0">
</li>
@endforeach
</ul>
<title>{{ config('app.name') }}</title>
</head>
@section('body_tag')
-<body>
+<body class="@stack('extra_body_classes')">
@show
@yield('main')
},
transitionTimingFunction: {
'out-cubic': 'cubic-bezier(0.33, 1, 0.68, 1)',
+ 'out-quart': 'cubic-bezier(0.25, 1, 0.5, 1)',
'out-quint': 'cubic-bezier(0.22, 1, 0.36, 1)',
},
transitionDuration: {
+ '1500': '1500ms',
'2000': '2000ms',
},
zIndex: {