diff --git a/assets/js/theme/blocks/timeline.js b/assets/js/theme/blocks/blocksWithGrab.js similarity index 50% rename from assets/js/theme/blocks/timeline.js rename to assets/js/theme/blocks/blocksWithGrab.js index 1cab898f388b1f5a5f0da6f2353673f115a033e1..bf76076708a3cc6fa8a0b89ac1d7fa8c5b87d1d9 100644 --- a/assets/js/theme/blocks/timeline.js +++ b/assets/js/theme/blocks/blocksWithGrab.js @@ -1,14 +1,13 @@ -const timelines = document.querySelectorAll('.block-timeline--horizontal'); +const blocks = document.querySelectorAll('.block-timeline--horizontal, .block-posts--scrollable'); -class BlockTimeline { +class BlockWithGrab { constructor (block) { this.block = block; - this.content = this.block.querySelector('.timeline'); - this.list = this.block.querySelector('.timeline-events ol'); - this.items = this.list.querySelectorAll('.timeline-event'); + this.content = this.block.querySelector('.grab-container'); + this.list = this.block.querySelector('ol, ul'); + this.items = this.list.querySelectorAll('.grab-item'); this.previous = this.block.querySelector('.previous'); this.next = this.block.querySelector('.next'); - this.isManipulated = false; this.index = 0; @@ -39,7 +38,7 @@ class BlockTimeline { this.itemWidth = this.items[0].offsetWidth; this.items.forEach((item) => { - maxTitleHeight = Math.max(item.querySelector('.title').offsetHeight, maxTitleHeight); + maxTitleHeight = Math.max(item.querySelector('.title, [itemprop="headline"]').offsetHeight, maxTitleHeight); }); this.block.style.setProperty('--min-title-height', maxTitleHeight + 'px'); @@ -62,31 +61,83 @@ class BlockTimeline { } handlePointers () { - let endEvents = ['pointerup'], - startX, + let startX, endX, threshold = 30, - isPointerDown = false; + isPointerDown = false, + hasMoved = false; this.content.style.touchAction = 'pan-y'; + const handlePointerMove = (event) => { + this.isGrabbing = true; + this.isManipulated = isPointerDown; + endX = event.clientX; + hasMoved = true; + + if (isPointerDown && hasMoved) { + event.preventDefault(); + } + }; this.content.addEventListener('pointerdown', (event) => { - this.content.classList.add('is-grabbing'); startX = event.clientX; isPointerDown = true; + hasMoved = false; + + window.addEventListener('pointermove', handlePointerMove); + window.addEventListener('pointerup', handlePointerUp); }); - - this.content.addEventListener('pointermove', (event) => { - this.isManipulated = isPointerDown; - endX = event.clientX; - }); - - endEvents.forEach(event => { - this.content.addEventListener(event, (event) => { - isPointerDown = false; - this.onManipulationEnd(startX, endX, threshold); - }); - }); + + const handlePointerUp = (event) => { + window.removeEventListener('pointermove', handlePointerMove); + window.removeEventListener('pointerup', handlePointerUp); + + if (!hasMoved && !this.isGrabbing) { + const pointedElement = document.elementFromPoint(event.clientX, event.clientY); + if (pointedElement && Math.abs(startX - event.clientX) > threshold) { + pointedElement.click(); + } + } + + this.onManipulationEnd(startX, endX, threshold); + this.isGrabbing = false; + }; + + + + // this.content.addEventListener('pointerdown', (event) => { + // this.content.classList.add('is-grabbing'); + // startX = event.clientX; + // isPointerDown = true; + // hasMoved = false; + // }); + + // this.content.addEventListener('pointermove', (event) => { + // this.isManipulated = isPointerDown; + // endX = event.clientX; + // hasMoved = true; + + // if (isPointerDown) { + // event.preventDefault(); + // } + // }); + + + // endEvents.forEach(event => { + // this.content.addEventListener(event, (event) => { + // this.isGrabbing = true; + // this.isManipulated = isPointerDown; + + // if (!hasMoved) { + // const cursorElement = document.elementFromPoint(event.clientX, event.clientY); + + // if (cursorElement) { + // cursorElement.click(); + // } + // } + // this.onManipulationEnd(startX, endX, threshold); + // }); + // }); } onManipulationEnd (start, end, threshold) { @@ -127,6 +178,6 @@ class BlockTimeline { } } -timelines.forEach((timeline) => { - new BlockTimeline(timeline); +blocks.forEach((block) => { + new BlockWithGrab(block); }); diff --git a/assets/js/theme/index.js b/assets/js/theme/index.js index 5dc97bb8ec7e3c3e26fc77abafc75bfff9144365..7618a6e6f5baade00eabe104766cce0084b888f5 100644 --- a/assets/js/theme/index.js +++ b/assets/js/theme/index.js @@ -8,5 +8,5 @@ import './design-system/search'; import './design-system/toc'; import './blocks/keyFigures'; import './blocks/organizations'; -import './blocks/timeline'; +import './blocks/blocksWithGrab.js'; import './blocks/videos.js'; diff --git a/assets/sass/_theme/_utils.sass b/assets/sass/_theme/_utils.sass index 0cc7245e2b45e7b22df87f7b716b13b557cf16a0..3064a2cc2e73428b9a5acc49ba9e50513bf5d20e 100644 --- a/assets/sass/_theme/_utils.sass +++ b/assets/sass/_theme/_utils.sass @@ -8,3 +8,28 @@ @import utils/sizes @import utils/shame + +@mixin block-with-grab + overflow: hidden + .grab-container + &:hover + cursor: grab + &.is-grabbing + cursor: grabbing + .grab-content + margin-left: calc(var(--grid-gutter-negative) / 2) + margin-right: calc(var(--grid-gutter-negative) / 2) + > ul, + > ol + display: flex + flex-flow: row nowrap + list-style: none + padding-left: 0 + transition: margin 0.4s ease-in-out + width: 100% + .grab-item + flex: none + scroll-snap-align: start + transition: 0.3s opacity + &.is-passed + opacity: 0.15 \ No newline at end of file diff --git a/assets/sass/_theme/blocks/posts.sass b/assets/sass/_theme/blocks/posts.sass index 25bfafd717cc7d2e1534129fd505b13b166806bf..3fe58c32a1e2ec47a2a263dcbd773045fe8aac45 100644 --- a/assets/sass/_theme/blocks/posts.sass +++ b/assets/sass/_theme/blocks/posts.sass @@ -286,34 +286,32 @@ .post width: columns(4) &--scrollable + @include block-with-grab .container padding-right: 0 .scrollable - display: flex - gap: var(--grid-gutter) - overflow-x: auto padding-bottom: $spacing-3 - scroll-snap-type: x mandatory - .post - display: inline-flex - scroll-snap-align: start - &:last-of-type - margin-right: var(--grid-gutter) + &:hover + cursor: grab + &.is-grabbing + cursor: grabbing + li + list-style: none + .posts + display: flex + gap: unset + .post + margin: 0 calc(var(--grid-gutter) / 2) @include media-breakpoint-down(desktop) - .top - margin-right: half(var(--grid-gutter)) .scrollable gap: half(var(--grid-gutter)) .post - min-width: columns(10) - &:last-of-type - margin-right: half(var(--grid-gutter)) + width: columns(10) + .grab-item:last-of-type + margin-right: half(var(--grid-gutter)) @include in-page-with-sidebar - &.block - .block-content - padding-left: calc((100% / 12 * (4 / 12 * 12) )) .post - min-width: columns(3) + width: columns(3) @include in-page-without-sidebar .block-content display: flex @@ -321,9 +319,9 @@ .top width: columns(3) .scrollable - flex: 1 + width: columns(9) .post - min-width: columns(4) + width: columns(4) // Move this part to blocks/categories when categories block is ready .block-posts diff --git a/assets/sass/_theme/blocks/timeline.sass b/assets/sass/_theme/blocks/timeline.sass index 08ebd8f9452d78d88def091cdee3f08022b06b8f..a43dc7a271bf6b9173ee18fb2e19daacb453a666 100644 --- a/assets/sass/_theme/blocks/timeline.sass +++ b/assets/sass/_theme/blocks/timeline.sass @@ -61,20 +61,14 @@ margin-left: columns(3) &--horizontal + @include block-with-grab --min-title-height: 0px background: $block-timeline-horizontal-background color: $block-timeline-horizontal-color - overflow: hidden padding-bottom: var(--grid-gutter) padding-top: var(--grid-gutter) &::before display: none - .timeline - &:hover - cursor: grab - &.is-grabbing - cursor: grabbing - .timeline-arrows display: flex padding-left: calc(var(--grid-gutter) / 2) @@ -92,22 +86,8 @@ &:disabled cursor: default opacity: 0.3 - .timeline-events - margin-left: calc(var(--grid-gutter-negative) / 2) - margin-right: calc(var(--grid-gutter-negative) / 2) - ol - display: flex - flex-flow: row nowrap - list-style: none - padding-left: 0 - margin-top: $spacing-2 - transition: margin 0.4s ease-in-out - width: 100% .timeline-event - flex: none - padding: 0 calc(var(--grid-gutter) / 2) - scroll-snap-align: start - transition: 0.3s opacity + margin: 0 calc(var(--grid-gutter) / 2) width: 50% .title display: block @@ -133,8 +113,6 @@ position: relative top: -4px width: 9px - &.is-passed - opacity: 0.15 &:last-child .line background: transparent diff --git a/layouts/partials/blocks/templates/posts/scrollable.html b/layouts/partials/blocks/templates/posts/scrollable.html index 73d64d02d244d1164bb171a35978b6c769341399..4ef4edfffed13649473df738045ab5186ee5e3c2 100644 --- a/layouts/partials/blocks/templates/posts/scrollable.html +++ b/layouts/partials/blocks/templates/posts/scrollable.html @@ -1,12 +1,18 @@ {{ $heading_level := .heading_level | default 3 }} {{ $heading := printf "h%d" $heading_level }} -<div class="scrollable"> - {{ range $post := .posts -}} - {{ with site.GetPage (printf "/posts/%s" $post) }} - {{ partial "posts/post.html" (dict - "post" . - "heading" $heading) }} - {{ end }} - {{ end }} +<div class="scrollable grab-container"> + <div class="scrollable-posts grab-content"> + <ul class="posts"> + {{ range $post := .posts -}} + {{ with site.GetPage (printf "/posts/%s" $post) }} + <li class="grab-item"> + {{ partial "posts/post.html" (dict + "post" . + "heading" $heading) }} + </li> + {{ end }} + {{ end }} + </ul> + </div> </div> diff --git a/layouts/partials/blocks/templates/timeline/horizontal.html b/layouts/partials/blocks/templates/timeline/horizontal.html index d029f0918ec243f4c7170b778dfb1699ddee3e72..19eabb80280f24fed3b10dd43ed2e62954498c39 100644 --- a/layouts/partials/blocks/templates/timeline/horizontal.html +++ b/layouts/partials/blocks/templates/timeline/horizontal.html @@ -4,13 +4,12 @@ "attributes" "class='title'" ) -}} -<div class="timeline"> +<div class="timeline grab-container"> {{ with .block.data }} - <div class="timeline-events"> + <div class="timeline-events grab-content"> <ol> {{ range .events }} - <li class="timeline-event"> - + <li class="timeline-event grab-item"> {{ $heading_tag.open -}} {{ .title | safeHTML }} {{ $heading_tag.close -}}