improved projet page, created projet archives page
This commit is contained in:
+6
-6
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -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);
|
||||
|
||||
@@ -205,6 +205,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 +272,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 +290,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 +327,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 +408,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user