Compare commits

...
2 Commits
Author SHA1 Message Date
bachir 9a601b79da burger menu improved 2026-08-24 17:44:15 +02:00
bachir b3db746273 improved projet page, created projet archives page 2026-08-24 14:20:12 +02:00
19 changed files with 732 additions and 105 deletions
@@ -0,0 +1,20 @@
uuid: d2e39686-e4ae-46ee-ab94-81edb6da2e16
langcode: fr
status: true
dependencies:
module:
- language
theme:
- leshed
id: leshed_selecteurdelangue
theme: leshed
region: header_right
weight: 0
provider: null
plugin: 'language_block:language_interface'
settings:
id: 'language_block:language_interface'
label: 'Sélecteur de langue'
label_display: '0'
provider: language
visibility: { }
@@ -0,0 +1,91 @@
uuid: 682395cd-d791-41ab-ac31-4199911aa22f
langcode: fr
status: true
dependencies:
config:
- core.entity_view_mode.node.tableau_liste
- field.field.node.projet.body
- field.field.node.projet.field_artiste_s_invite_s
- field.field.node.projet.field_dates
- field.field.node.projet.field_fichiers
- field.field.node.projet.field_images
- field.field.node.projet.field_invite_accompagne_curate_p
- field.field.node.projet.field_partenaire_s
- field.field.node.projet.field_portage
- field.field.node.projet.field_projet_s_lie_s
- field.field.node.projet.field_type_de_projet
- image.style.large
- node.type.projet
module:
- datetime_range
- image_delta_formatter
- layout_builder
- text
- user
third_party_settings:
layout_builder:
enabled: false
allow_custom: false
id: node.projet.tableau_liste
targetEntityType: node
bundle: projet
mode: tableau_liste
content:
body:
type: text_summary_or_trimmed
label: hidden
settings:
trim_length: 100
third_party_settings: { }
weight: 4
region: content
field_artiste_s_invite_s:
type: entity_reference_label
label: hidden
settings:
link: false
third_party_settings: { }
weight: 1
region: content
field_dates:
type: daterange_default
label: hidden
settings:
timezone_override: ''
format_type: d_m_y
from_to: both
separator: '-'
third_party_settings: { }
weight: 2
region: content
field_images:
type: image_delta_formatter
label: hidden
settings:
deltas:
- 1
deltas_reversed: false
image_link: content
image_style: large
image_loading:
attribute: lazy
third_party_settings: { }
weight: 0
region: content
field_type_de_projet:
type: entity_reference_label
label: hidden
settings:
link: false
third_party_settings: { }
weight: 3
region: content
hidden:
field_fichiers: true
field_invite_accompagne_curate_p: true
field_partenaire_s: true
field_portage: true
field_projet_s_lie_s: true
langcode: true
links: true
search_api_excerpt: true
@@ -0,0 +1,11 @@
uuid: ac968411-2b96-4643-b24a-4e77735f024c
langcode: fr
status: true
dependencies:
module:
- node
id: node.tableau_liste
label: 'Tableau liste'
description: ''
targetEntityType: node
cache: true
+9
View File
@@ -0,0 +1,9 @@
uuid: ab111706-74d5-452b-994f-053c2d6219d4
langcode: fr
status: true
dependencies: { }
id: en
label: English
direction: ltr
weight: 1
locked: false
+2
View File
@@ -7,6 +7,8 @@ url:
source: path_prefix
prefixes:
fr: ''
en: en
domains:
fr: ''
en: ''
selected_langcode: site_default
@@ -0,0 +1 @@
label: English
@@ -0,0 +1,5 @@
label: 'Node view'
description: 'When enabled, this overrides the default Drupal behavior for displaying nodes at <em>/node/{node}</em>. If you add variants, you may use selection criteria such as node type or language or user access to provide different views of nodes. If no variant is selected, the default Drupal node view will be used. This page only affects nodes viewed as pages, it will not affect nodes viewed in lists or at other locations.'
parameters:
node:
label: Node
@@ -0,0 +1 @@
label: 'Delete redirect'
@@ -0,0 +1,71 @@
label: Redirect
description: 'List of redirects'
display:
default:
display_title: Master
display_options:
exposed_form:
options:
submit_button: Filter
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
sort_asc_label: Asc
sort_desc_label: Desc
pager:
options:
tags:
previous: '‹ previous'
next: 'next ›'
first: '« first'
last: 'last »'
expose:
items_per_page_label: 'Items per page'
items_per_page_options_all_label: '- All -'
offset_label: Offset
fields:
redirect_bulk_form:
action_title: 'With selection'
redirect_source__path:
label: From
separator: ', '
enabled:
label: Enabled
separator: ', '
created:
label: Created
filters:
redirect_source__path:
expose:
label: From
redirect_redirect__uri:
expose:
label: To
status_code:
expose:
label: 'Status code'
group_info:
label: 'Status code'
group_items:
1:
title: '300 Multiple Choices'
2:
title: '301 Moved Permanently'
3:
title: '302 Found'
4:
title: '303 See Other'
5:
title: '304 Not Modified'
6:
title: '305 Use Proxy'
7:
title: '307 Temporary Redirect'
language:
expose:
label: 'Original language'
title: Redirect
empty:
area_text_custom:
content: 'There is no redirect yet.'
page_1:
display_title: Page
+14 -1
View File
@@ -8,7 +8,20 @@ menus:
description: null
enabled: true
expanded: false
weight: 0
weight: -50
langcode: fr
uuid: 75910d9e-f8a0-4dbd-930f-e37fb47de597
options: { }
-
menu_name: main
title: 'À propos'
parent: null
uri: 'entity:node/605'
link_title: ''
description: null
enabled: true
expanded: false
weight: -49
langcode: fr
uuid: 4077ca97-8030-4b12-a10a-f1ddd903c720
options: { }
+187
View File
@@ -0,0 +1,187 @@
uuid: ad48e5a0-36b0-4e30-82d1-0644e114c98c
langcode: fr
status: true
dependencies:
config:
- core.entity_view_mode.node.tableau_liste
- node.type.projet
- system.menu.main
module:
- datetime
- node
- user
id: archives
label: Archives
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
id: default
display_title: Default
display_plugin: default
position: 0
display_options:
title: Archives
fields:
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: title
plugin_id: field
label: ''
exclude: false
alter:
alter_text: false
make_link: false
absolute: false
word_boundary: false
ellipsis: false
strip_tags: false
trim: false
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
pager:
type: none
options:
offset: 0
exposed_form:
type: basic
options:
submit_button: Appliquer
reset_button: false
reset_button_label: Réinitialiser
exposed_sorts_label: 'Trier par'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
empty: { }
sorts:
field_dates_value:
id: field_dates_value
table: node__field_dates
field: field_dates_value
relationship: none
group_type: group
admin_label: ''
plugin_id: datetime
order: DESC
expose:
label: ''
field_identifier: ''
exposed: false
granularity: second
arguments: { }
filters:
status:
id: status
table: node_field_data
field: status
entity_type: node
entity_field: status
plugin_id: boolean
value: '1'
group: 1
expose:
operator: ''
type:
id: type
table: node_field_data
field: type
entity_type: node
entity_field: type
plugin_id: bundle
value:
projet: projet
style:
type: default
row:
type: 'entity:node'
options:
relationship: none
view_mode: tableau_liste
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
relationships: { }
css_class: views-archives-projets
header: { }
footer: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }
page_1:
id: page_1
display_title: Page
display_plugin: page
position: 1
display_options:
display_extenders: { }
path: archives
menu:
type: normal
title: Archives
description: ''
weight: -48
expanded: false
menu_name: main
parent: ''
context: '0'
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+88 -41
View File
@@ -55,8 +55,9 @@ gsap.registerPlugin(ScrollTrigger);
initTitles();
initBurgerMenu();
initSmoothScroll();
initColumnScrollSync();
initHomeColumnScrollSync();
initFsLightbox();
initPageFull();
// initVues()
}
@@ -89,7 +90,7 @@ gsap.registerPlugin(ScrollTrigger);
};
function initTitles() {
const titles = document.querySelectorAll('#block-leshed-identitedusite>a, article.node-type-projet>header>h2, article.node-type-projet>.cols>.left>h2');
const titles = document.querySelectorAll('#block-leshed-identitedusite>a, article.node-type-projet>header>h2, article.node-type-projet>.cols>.left>h2, article.node-type-projet.view-mode-full h2.node-title');
for (const txt of titles) {
txt.classList.add("variable-title");
const splitted = Splitting({ target: txt, by: 'chars' });
@@ -100,43 +101,6 @@ gsap.registerPlugin(ScrollTrigger);
}
}
const pageTitle = document.querySelector('article.node-type-projet.view-mode-full h2.node-title');
console.log('pageTitle', pageTitle);
if (pageTitle) {
// needs requestAnimationFrame to get the real titleHeight after splitting
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const initTitleH = pageTitle.offsetHeight;
// console.log(`initTitleH: ${initTitleH}`);
const titleScaleTarget = 0.4;
const end = initTitleH - initTitleH * titleScaleTarget;
// console.log(`end: ${end}`);
gsap_mm.add('(min-width: 768px)', () => {
gsap.to(pageTitle, {
scale: titleScaleTarget,
y: end,
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: 0,
end: end, // scroll maximum du conteneur (robuste au resize)
scrub: true,
invalidateOnRefresh: true,
},
});
})
});
});
}
}
/**
@@ -301,8 +265,8 @@ gsap.registerPlugin(ScrollTrigger);
* `main[role="main"]`, pas dans la fenêtre : GSAP utilise donc ce conteneur
* comme `scroller`.
*/
function initColumnScrollSync() {
console.log('initColumnScrollSync');
function initHomeColumnScrollSync() {
console.log('initHomeColumnScrollSync');
const firstColumn = document.querySelector('.layout--twocol-section--50-50 .layout__region--first');
const secondColumn = document.querySelector('.layout--twocol-section--50-50 .layout__region--second');
@@ -344,6 +308,89 @@ gsap.registerPlugin(ScrollTrigger);
window.addEventListener('load', () => ScrollTrigger.refresh());
}
function initPageFull(){
console.log('initPageFull');
// PAGE TITLE SCALING DOWN
const pageTitle = document.querySelector('article.node-type-projet.view-mode-full h2.node-title');
console.log('pageTitle', pageTitle);
if (pageTitle) {
// needs requestAnimationFrame to get the real titleHeight after splitting
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const initTitleH = pageTitle.offsetHeight;
// console.log(`initTitleH: ${initTitleH}`);
const titleScaleTarget = 0.4;
const end = initTitleH - initTitleH * titleScaleTarget;
// console.log(`end: ${end}`);
gsap_mm.add('(min-width: 768px)', () => {
gsap.to(pageTitle, {
scale: titleScaleTarget,
y: end,
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: 0,
end: end, // scroll maximum du conteneur (robuste au resize)
scrub: true,
invalidateOnRefresh: true,
},
});
})
});
});
}
// BLOCAGE DE LA COLONNE GAUCHE
// La gauche défile normalement puis se fige (pin) quand le bas de son
// contenu atteint le bas du viewport, pendant que la colonne droite (plus
// haute) continue. Piloté en GSAP plutôt qu'en `position: sticky` (qui ne
// prend pas dans ce contexte scroller custom + GSAP). `.cols` est en
// `align-items: flex-start`, donc .col.left a bien la hauteur de son
// contenu (sinon `bottom bottom` tomberait à la fin du scroll).
const cols = document.querySelector('article.node-type-projet.view-mode-full .cols');
const leftColumn = cols && cols.querySelector('.col.left');
if (cols && leftColumn) {
// Doit rester identique au scale-down du titre (voir plus haut).
const titleScaleTarget = 0.4;
// Seuil de scroll (px) après lequel la colonne gauche se fige :
// - gauche plus haute que le viewport : quand son bas atteint le bas ;
// - gauche plus courte : quand le scale-down du titre est terminé, pour
// qu'elle remonte AVEC le titre puis s'arrête (sans disparaître en haut).
const leftFreezeStart = () => {
const overflow = leftColumn.offsetHeight - scroller.clientHeight;
if (overflow > 0) return overflow;
return pageTitle ? pageTitle.offsetHeight * (1 - titleScaleTarget) : 0;
};
gsap_mm.add('(min-width: 768px)', () => {
// On NE pin PAS (le pin sort l'élément du flux et casse le flex .cols).
// On translate la colonne : défilement natif jusqu'à `leftFreezeStart`,
// puis contre-translation 1:1 du scroll → elle se fige, tout en restant
// un flex-item normal (la colonne droite garde sa place).
gsap.fromTo(leftColumn,
{ y: 0 },
{
y: () => (scroller.scrollHeight - scroller.clientHeight) - leftFreezeStart(),
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: leftFreezeStart, // px de scroll où la gauche se fige
end: 'max',
scrub: true,
invalidateOnRefresh: true,
},
}
);
});
}
}
function initFsLightbox(){
console.log('initFsLightbox');
// console.log(window.FsLightbox);
+125 -54
View File
@@ -43,7 +43,7 @@ div.layout-container{
flex-direction: column;
height: 100vh;
overflow: hidden;
max-width:1080px;
max-width:$layout_width;
margin: 0 auto;
header[role="banner"]{
display: block;
@@ -146,39 +146,65 @@ header[role="banner"]{
&>div.wrapper{
position:absolute;
top:$header_height; left:0;
// z-index: 99;
background-color: rgba(0,0,0,0.75);
// fixed (et non absolute) pour échapper à l'overflow:hidden de
// .layout-container : sinon le débord pleine largeur (et donc le
// backdrop-filter) est rogné aux bords du conteneur centré.
position:fixed;
top:$header_height;
left: 0;
width:100vw;
padding: 0 calc((100vw - $layout_width) / 2) 0 calc((100vw - $layout_width) / 2);
background-color: rgba(255,255,255,0.3);
backdrop-filter: blur(7px);
height: 0;
overflow: hidden;
transition-duration: 0.5s;
&[opened]{
height: calc(100vh - $header_height);
}
&>nav{
nav#block-leshed-navigationprincipale{
// width: $layout_width;
// margin: 0 auto;
padding-top: 10px;
background-color: #fff;
// background-color: #fff;
>ul{
margin: 0;
padding:0;
li{
list-style: none;
a{
font-size: 4em;
font-weight: 200;
filter: drop-shadow(0 0 2px #fff) drop-shadow(0 0 5px #fff) drop-shadow(0 0 15px #fff);
}
}
}
}
nav#block-mathallo-navigationprincipale{
padding: 2em 1em;
div#block-leshed-selecteurdelangue{
ul{
display: flex;
justify-content: flex-end;
list-style: none;
margin:0;
padding:0;
li{
margin-right: 1em;
// background-color: $bleu_site;
color: #fff;
padding:0.5em 1em;
&:not(:last-child){
list-style: none;
padding:0;
display: inline-block;
font-size: 2em;
&:first-child{
&:after{
content: " /";
font-weight: 200;
}
}
&:last-child{
padding-left: 0.3em;
}
&.is-active{
font-weight: 400;
}
}
}
}
@@ -205,6 +231,50 @@ header[role="banner"]{
// }
}
@mixin projetListe{
>header{
>h2{
font-size: 5em;
}
}
// only with images, title overlap the image
div.field_artiste_s_invite_s.field-items.multiple{
// display: flex;
// flex-direction: row;
.field-item{
display: inline-block;
&:not(:last-of-type):after{
content: ", ";
}
}
}
&.has-image{
>header{
>h2{
position: relative;
max-height: 1em;
overflow: visible;
z-index: 10;
// text-shadow: 0 0 10px #fff;
// box-shadow: 0 0 15px #f00;
// filter: drop-shadow(0 0 5px #fff);
}
div.field_images{
position:relative;
// width: 100%;
z-index: 5;
a{
display:block;
img{
width:100%;
height: auto;
}
}
}
}
}
}
// _____ _____ __ ____ ___ _ ____
// |_ _/ _ \ \ / / / ___/ _ \| | / ___|
// | || | | \ \ /\ / / | | | | | | | \___ \
@@ -228,40 +298,7 @@ div.layout.layout--twocol-section--50-50{
margin-bottom: 2em;
max-width: 100%;
article.node-type-projet{
>header{
>h2{
font-size: 5em;
}
}
>div{
// margin-top: 3em;
}
// only with images, title overlap the image
&.has-image{
>header{
>h2{
position: relative;
max-height: 1em;
overflow: visible;
z-index: 10;
// text-shadow: 0 0 10px #fff;
// box-shadow: 0 0 15px #f00;
// filter: drop-shadow(0 0 5px #fff);
}
div.field_images{
position:relative;
// width: 100%;
z-index: 5;
a{
display:block;
img{
width:100%;
height: auto;
}
}
}
}
}
@include projetListe();
}
}
@@ -279,12 +316,15 @@ article.node-type-projet.view-mode-full{
display: flex;
flex-direction: row;
gap: $colsgap;
// column-gap: 2em;
align-items: flex-start;
div.col{
flex:0 0 calc(50% - $colsgap / 2);
max-width: calc(50% - $colsgap / 2);
&.left{
z-index: 10;
// padding-bottom: 5em;
// box-sizing: border-box;
>h2.node-title{
position: relative;
font-size: 12em;
@@ -313,6 +353,11 @@ article.node-type-projet.view-mode-full{
font-weight: 400;
}
}
>div.spacer{
display: block;
height: 5em;
}
}
&.right{
// Étire .field_images sur toute la hauteur de la colonne (= colonne la
@@ -389,3 +434,29 @@ article.node-type-projet.view-mode-full{
}
// __ ___ _ _ _ ____ _ _
// \ \ / (_) _____ _____ / \ _ __ ___| |__ (_)_ _____ ___ | _ \ _ __ ___ (_) ___| |_
// \ \ / /| |/ _ \ \ /\ / / __| / _ \ | '__/ __| '_ \| \ \ / / _ \/ __| | |_) | '__/ _ \| |/ _ \ __|
// \ V / | | __/\ V V /\__ \ / ___ \| | | (__| | | | |\ V / __/\__ \ | __/| | | (_) | | __/ |_
// \_/ |_|\___| \_/\_/ |___/ /_/ \_\_| \___|_| |_|_| \_/ \___||___/ |_| |_| \___// |\___|\__|
// |__/
div.views-archives-projets{
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 2em;
gap: $colsgap;
div.views-row{
flex: 1 0 calc(25% - $colsgap);
width: calc(25% - $colsgap);
article.node-type-projet.view-mode-tableau_liste{
@include projetListe();
>header{
>h2{
font-size: 3em;
}
}
}
}
}
@@ -1,3 +1,4 @@
$layout_width: 1080px;
$default_fwght: 200;
$header_height: 70px;
$colsgap: 2em;
+9 -2
View File
@@ -84,13 +84,13 @@ function leshed_preprocess_node(&$variables) {
$variables["attributes"]['class'][] = "view-mode-{$variables['view_mode']}";
if ($variables['view_mode'] === "teaser") {
// if ($variables['view_mode'] === "teaser") {
/** @var \Drupal\file\Plugin\\Field\FieldType\FileFieldItemList $field_images */
$field_images = $node->get('field_images');
if (!$field_images->isEmpty()) {
$variables["attributes"]['class'][] = "has-image";
}
}
// }
}
@@ -112,3 +112,10 @@ function leshed_preprocess_field(&$variables) {
}
$variables['attributes']['class'][] = $variables['field_name'];
}
function leshed_preprocess_links__language_block(&$variables){
foreach ($variables['links'] as $langcode => $link) {
$variables['links'][$langcode]['text'] = $langcode;
$variables['links'][$langcode]['link']['#title'] = $langcode;
}
}
@@ -70,6 +70,7 @@
{{ label }}
</h2>
{{ content|without('field_images') }}
<div class="spacer"></div>
</div>
<div class="col right">
{{ content.field_images }}
@@ -0,0 +1,89 @@
{#
/**
* @file
* Default theme implementation to display a node.
*
* Available variables:
* - node: The node entity with limited access to object properties and methods.
* Only method names starting with "get", "has", or "is" and a few common
* methods such as "id", "label", and "bundle" are available. For example:
* - node.getCreatedTime() will return the node creation timestamp.
* - node.hasField('field_example') returns TRUE if the node bundle includes
* field_example. (This does not indicate the presence of a value in this
* field.)
* - node.isPublished() will return whether the node is published or not.
* Calling other methods, such as node.delete(), will result in an exception.
* See \Drupal\node\Entity\Node for a full list of public properties and
* methods for the node object.
* - label: (optional) The title of the node.
* - content: All node items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
* {{ content|without('field_example') }} to temporarily suppress the printing
* of a given child element.
* - author_picture: The node author user entity, rendered using the "compact"
* view mode.
* - metadata: Metadata for this node.
* - date: (optional) Themed creation date field.
* - author_name: (optional) Themed author name field.
* - url: Direct URL of the current node.
* - display_submitted: Whether submission information should be displayed.
* - attributes: HTML attributes for the containing element.
* The attributes.class element may contain one or more of the following
* classes:
* - node: The current template type (also known as a "theming hook").
* - node--type-[type]: The current node type. For example, if the node is an
* "Article" it would result in "node--type-article". Note that the machine
* name will often be in a short form of the human readable label.
* - node--view-mode-[view_mode]: The View Mode of the node; for example, a
* teaser would result in: "node--view-mode-teaser", and
* full: "node--view-mode-full".
* The following are controlled through the node publishing options.
* - node--promoted: Appears on nodes promoted to the front page.
* - node--sticky: Appears on nodes ordered above other non-sticky nodes in
* teaser listings.
* - node--unpublished: Appears on unpublished nodes visible only to site
* admins.
* - title_attributes: Same as attributes, except applied to the main title
* tag that appears in the template.
* - content_attributes: Same as attributes, except applied to the main
* content tag that appears in the template.
* - author_attributes: Same as attributes, except applied to the author of
* the node tag that appears in the template.
* - title_prefix: Additional output populated by modules, intended to be
* displayed in front of the main title tag that appears in the template.
* - title_suffix: Additional output populated by modules, intended to be
* displayed after the main title tag that appears in the template.
* - view_mode: View mode; for example, "teaser" or "full".
* - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
* - page: Flag for the full page state. Will be true if view_mode is 'full'.
*
* @see template_preprocess_node()
*
* @ingroup themeable
*/
#}
{#
{% if content.field_images.getvalue|length %}
{% set attributes = attributes.addClass('has-image') %}
{% endif %} #}
<article{{ attributes }}>
{# {{ title_prefix }} #}
{# {% if label and not page %} #}
<header>
<h2{{ title_attributes }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h2>
{{ content.field_images }}
</header>
{# {% endif %} #}
{# {{ title_suffix }} #}
<div{{ content_attributes }}>
{{ content|without('field_images') }}
</div>
</article>