"": {
"devDependencies": {
"@alpinejs/intersect": "^3.4.2",
+ "@tailwindcss/line-clamp": "^0.2.2",
"alpinejs": "^3.4.2",
"autoprefixer": "^10.3.7",
"axios": "^0.21",
"isotope-layout": "^3.0.6",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
+ "minisearch": "^3.1.0",
"plyr": "^3.6.9",
"postcss": "^8.3.10",
"postcss-import": "^14.0.2",
"node": ">= 8"
}
},
+ "node_modules/@tailwindcss/line-clamp": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.2.2.tgz",
+ "integrity": "sha512-NgA4Ds+/eCiO+6O3SooRsfJ8m7M2+QvNvHwOjBQq7FIYoWwAV4I4Wu4fjHeuO9Yi6p47ceHUKEGGEBh0ozQodg==",
+ "dev": true,
+ "peerDependencies": {
+ "tailwindcss": ">=2.0.0 || >=3.0.0-alpha.1"
+ }
+ },
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
+ "node_modules/minisearch": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-3.1.0.tgz",
+ "integrity": "sha512-Lm1YCI3O1BGZFm4RG0LSHCslR4DlW/idmv/QuLdBfI7toDkI8NPcFbo1673tkq4Vz3KqwfnS/6500qSJckcy6A==",
+ "dev": true
+ },
"node_modules/mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"fastq": "^1.6.0"
}
},
+ "@tailwindcss/line-clamp": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.2.2.tgz",
+ "integrity": "sha512-NgA4Ds+/eCiO+6O3SooRsfJ8m7M2+QvNvHwOjBQq7FIYoWwAV4I4Wu4fjHeuO9Yi6p47ceHUKEGGEBh0ozQodg==",
+ "dev": true,
+ "requires": {}
+ },
"@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
+ "minisearch": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-3.1.0.tgz",
+ "integrity": "sha512-Lm1YCI3O1BGZFm4RG0LSHCslR4DlW/idmv/QuLdBfI7toDkI8NPcFbo1673tkq4Vz3KqwfnS/6500qSJckcy6A==",
+ "dev": true
+ },
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
},
"devDependencies": {
"@alpinejs/intersect": "^3.4.2",
+ "@tailwindcss/line-clamp": "^0.2.2",
"alpinejs": "^3.4.2",
"autoprefixer": "^10.3.7",
"axios": "^0.21",
"isotope-layout": "^3.0.6",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
+ "minisearch": "^3.1.0",
"plyr": "^3.6.9",
"postcss": "^8.3.10",
"postcss-import": "^14.0.2",
@import "tailwindcss/base";
@import 'common/fonts.css';
@import 'common/menu.css';
+@import 'common/plyr.css';
@import 'common/global.css';
@import "tailwindcss/components";
h1 { @apply text-6xl; }
h2 { @apply text-4xl; }
-[x-cloak] { display: none !important; }
+[x-cloak] { visibility: hidden !important; }
.circle-arrow {
display: inline-block;
--- /dev/null
+:root {
+ --plyr-color-main: theme('colors.blue-dark');
+ --plyr-range-fill-background: #fff;
+ --plyr-control-radius: 50%;
+ --plyr-control-spacing: 21.45px;
+}
+
+.plyr--audio {
+ --plyr-range-fill-background: #000;
+}
+
+.plyr__time {
+ @apply font-secondary;
+ font-feature-settings: "tnum";
+ font-variant-numeric: tabular-nums;
+}
// AlpineJS setup - https://alpinejs.dev/
import Alpine from 'alpinejs';
+import intersect from '@alpinejs/intersect';
+import media_library from './media-library';
+import search from './search';
+
window.Alpine = Alpine;
// Plugins
-import intersect from '@alpinejs/intersect';
Alpine.plugin(intersect);
// Components
-import media_library from './media-library';
Alpine.data('media_library', media_library);
+Alpine.data('search', search);
Alpine.start();
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: true,
+ debug: false,
},
/***********\
--- /dev/null
+//=======================//
+// Search functionality //
+//=====================//
+
+import MiniSearch from 'minisearch';
+
+export default (searchData = []) => ({
+
+ miniSearch: {}, // Holds the MiniSearch instance
+ setup: { // MiniSearch options: https://lucaong.github.io/minisearch/#search-options
+ fields: ['title', 'text'], // fields to index for full-text search
+ storeFields: ['id', 'title', 'text', 'type', 'url', 'thumb'], // fields to return with search results
+ searchOptions: {
+ prefix: true, // Allow partial matches
+ },
+ },
+ query: '', // The search query
+
+ init() {
+ this.miniSearch = new MiniSearch(this.setup);
+ this.miniSearch.addAll(searchData);
+ },
+
+ get results() {
+ if (this.query === '') {
+ return [];
+ }
+
+ // console.log(`search results: ${this.query}`);
+ // console.table(this.miniSearch.search(this.query))
+
+ return this.miniSearch.search(this.query)
+ },
+
+ get resultCount() {
+ if (this.results.length === 1) {
+ return '1 résultat'
+ }
+
+ return `${this.results.length} résultats`;
+ },
+
+});
{{-- Standard Link --}}
@php
- if (Illuminate\Support\Str::startsWith($href, '/')) {
+ use Illuminate\Support\Str;
+
+ if (!Str::startsWith($href, 'http') && !Str::startsWith($href, '/tools/')) {
// TODO: add adapt local URLs based on environment (whether dynamic or static HTML output)
$href = '/front' . $href; // Prefix URLs for frontend preview
}
<h2 class="text-6xl uppercase">Filtres</h2>
{{-- Filter Options --}}
- <div class="flex mt-15">
+ <div class="flex flex-col lg:flex-row mt-15">
{{-- Media Type Filters --}}
- <div class="w-1/3">
+ <div class="lg:w-1/3">
<h3 class="font-medium text-4xl mb-5">Média</h3>
<div class="grid grid-cols-1 gap-5">
@foreach($types as $type_id => $type)
</div>
{{-- Theme Filters --}}
- <div class="w-2/3">
+ <div class="mt-15 lg:mt-0 lg:w-2/3">
<h3 class="font-medium text-4xl mb-5">Thème</h3>
<div class="grid grid-cols-2 gap-5">
@foreach($themes as $theme_id => $theme)
{{-- 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 mt-10 -mb-16 -mx-2.5" x-init="initIsotope($el)" x-cloak>
@foreach ($media as $item)
<div @click="openPlayer({
type: '{{ $item['type'] }}',
})"
class="media-item
{{-- Width is 25% minus the gutters (2 * 0.625rem that comes from mx-2.5) --}}
- float-left w-[calc(25%-1.25rem)] mx-2.5 mb-16
+ float-left w-[calc(33%-1.25rem)] lg:w-[calc(25%-1.25rem)] mx-2.5 mb-16
media-{{ $item['type'] }}
theme-{{ $item['theme']['id'] }}"
>
{{-- Right side --}}
<div class="flex items-center">
{{-- Time codes --}}
- <div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div>
+ <div class="plyr__time plyr__time--current mr-2" aria-label="Current time">00:00</div>
<div class="plyr__time plyr__time--duration" aria-label="Duration">00:00</div>
</div>
--- /dev/null
+{{-- SEARCH (full screen overlay) --}}
+
+@push('before_scripts')
+ <script>
+ @php
+ // TODO: Consider fetching this via JS only when search is used
+ echo file_get_contents(storage_path('search.js'));
+ @endphp
+ </script>
+@endpush
+
+<div class="search-overlay
+ fixed top-0 left-0 w-screen h-screen
+ flex flex-col
+ bg-white text-black
+ px-30
+ z-20
+ transition ease-out-cubic duration-500"
+ {{-- 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-cloak>
+
+ <x-close @click.prevent="searchOpen = false" />
+
+ {{-- 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
+ x-ref="searchField"
+ x-model="query"
+ type="search"
+ placeholder="Recherche"
+ class="appearance-none outline-none
+ mt-8 w-full
+ font-semibold text-6xl uppercase
+ border-b-2 border-black">
+ </div>
+
+ {{-- Search Results --}}
+ <div class="flex-1 max-h-full overflow-y-auto">
+ <div x-show="query !== ''" class="font-secondary font-medium">
+ <p x-text="resultCount" class="mt-15 opacity-50"></p>
+
+ <div class="h-full max-h-full overflow-y-auto">
+ <template x-for="result in results" :key="result.id">
+ <div class="py-8 border-b border-black border-opacity-20">
+ <a :href="result.url" x-text="result.title" class="text-2xl text-blue"></a>
+ <p x-text="result.text" class="mt-2 line-clamp-1"></p>
+ </div>
+ </template>
+ </div>
+ </div>
+ </div>
+
+</div>
$logo = 'https://odl.paris.cubedesigners.com/storage/102/groupe-84.svg';
$subtitle = "Découvrez le coeur de l'offre sous forme de feuilleteur interactif";
$button = "Découvrir l'offre";
+ $button_link = '/tools/fluidbookpreview/';
$illustration = 'https://odl.paris.cubedesigners.com/storage/137/home-magazine.jpg';
$shortcuts = [
[
@endif
@if($button)
<a class="bg-blue text-white py-4 px-10 rounded-full transition transform hover:scale-105"
- href="#"
+ href="{{ $button_link }}"
x-show="shown" x-transition.opacity.scale.80.origin.bottom.duration.500ms.delay.300ms>
{{ $button }}
</a>
</div>
{{-- Link blocks --}}
- <div class="grid grid-cols-3 gap-6" x-data="{ shown: false }" x-intersect="shown = true">
+ <div class="grid grid-cols-3 gap-6 text-center lg:text-left" x-data="{ shown: false }" x-intersect="shown = true">
@foreach($shortcuts as $shortcut)
- <div class="relative bg-blue flex items-center text-white p-6 rounded-lg overflow-hidden">
- <img class="flex-shrink mr-8 w-[40%]" src="{{ $shortcut['image'] }}" alt="">
+ <div class="relative bg-blue flex flex-col lg:flex-row items-center text-white p-6 rounded-lg overflow-hidden max-h-[22vh] lg:max-h-none">
+ <img class="flex-shrink mr-8 max-h-[40%] lg:max-h-none lg:w-[40%]" src="{{ $shortcut['image'] }}" alt="">
<div class="flex-1 flex-shrink-0 space-y-2">
<h3 class="font-semibold uppercase">{{ $shortcut['title'] }}</h3>
<p>{{ $shortcut['content'] }}</p>
<h2 class="font-medium text-2xl mb-14 w-1/2">{{ $subtitle }}</h2>
{{-- DOCUMENTS --}}
- <div class="grid grid-cols-2 gap-6">
+ <div class="grid lg:grid-cols-2 gap-6">
@foreach($documents as $doc)
<div class="bg-blue flex p-10 text-white rounded-md">
<img class="w-1/2 pr-6" src="{{ $doc['document_image'] }}" alt="{{ $doc['document_title'] }}">
@stack('after_css')
<title>{{ config('app.name') }}</title>
</head>
-<body class="font-primary p-8" :class="menuOpen && 'menu-open'" x-data="{ menuOpen: false }">
+<body x-data="{ menuOpen: false, searchOpen: false }"
+ :class="{ 'menu-open': menuOpen, 'overflow-hidden': searchOpen }"
+ class="font-primary p-8">
<header class="site-header relative flex justify-between z-20 transition-colors duration-500">
{{-- Menu Toggle --}}
<a href="#" @click.prevent="menuOpen = !menuOpen">
{{-- Search Icon --}}
- <a href="#">
+ <a href="#" @click.prevent="searchOpen = true; $dispatch('searchOpen')">
<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"/>
@php
$menu_links = [
'/' => 'Accueil',
- '/fluidbook' => 'Cœur de l’offre',
+ '/tools/fluidbookpreview/' => 'Cœur de l’offre',
'/ressources' => 'Ressources',
'/mediatheque' => 'Médiathèque',
'/tour' => 'Visite guidée',
</ul>
</div>
+ {{-- Search Overlay --}}
+ <x-search />
+
@stack('before_scripts')
<script src="{{ asset('js/app.js') }}"></script>
@stack('after_scripts')
fontSize: {
'6xl': ['3.375rem', 1], // 54px, line-height: 1
},
+ maxHeight: {
+ 'none': 'none',
+ '1/2': '50%',
+ },
spacing: {
'15': '3.75rem', // 60px
'22': '5.5rem', // 88px
variants: {
extend: {},
},
- plugins: [],
+ plugins: [
+ require('@tailwindcss/line-clamp')
+ ],
}