diff --git a/packages/ui-default/components/sticky/sticky.page.js b/packages/ui-default/components/sticky/sticky.page.js index f08b664b7..ba41385c2 100644 --- a/packages/ui-default/components/sticky/sticky.page.js +++ b/packages/ui-default/components/sticky/sticky.page.js @@ -1,33 +1,47 @@ +import 'sticky-kit/dist/sticky-kit'; + import $ from 'jquery'; import _ from 'lodash'; import responsiveCutoff from 'vj/breakpoints.json'; import { AutoloadPage } from 'vj/misc/Page'; import { isAbove, isBelow } from 'vj/utils/mediaQuery'; +const navHeight = isBelow(responsiveCutoff.mobile) + ? 0 + : $('.nav').height(); + +function updateStickies($stickies) { + $stickies.get().forEach((element) => { + const $sticky = $(element); + const shouldEnableSticky = (isAbove($sticky.data('sticky-cutoff-min'))); + const stickyEnabled = $sticky.data('sticky-enabled'); + if (shouldEnableSticky && !stickyEnabled) { + const stickyOptions = {}; + const $stickyParent = $sticky.closest('[data-sticky-parent]'); + if ($stickyParent.length > 0) { + stickyOptions.parent = $stickyParent; + } + stickyOptions.offset_top = 10 + navHeight; + $sticky.stick_in_parent(stickyOptions); + $sticky.data('sticky-enabled', true); + } else if (!shouldEnableSticky && stickyEnabled) { + $sticky.trigger('sticky_kit:detach'); + $sticky.data('sticky-enabled', false); + } + }); +} + function getCutoff(str) { - if (str === 'medium') return responsiveCutoff.mobile; - if (str === 'large') return responsiveCutoff.desktop; + if (str === 'medium') { + return responsiveCutoff.mobile; + } if (str === 'large') { + return responsiveCutoff.desktop; + } return 0; } -function updateStickies($stickies) { - const navHeight = isBelow(responsiveCutoff.mobile) ? 0 : ($('.nav').height() || 0); - $stickies.get().forEach((element) => { - const $sticky = $(element); - const shouldEnableSticky = isAbove($sticky.data('sticky-cutoff-min')); - const topOffset = 10 + navHeight; - if (shouldEnableSticky) { - element.style.position = 'sticky'; - element.style.top = `${topOffset}px`; - element.style.maxHeight = `calc(100vh - ${topOffset + 10}px)`; - element.style.overflowY = 'auto'; - } else { - element.style.position = ''; - element.style.top = ''; - element.style.maxHeight = ''; - element.style.overflowY = ''; - } - }); +function stickyRelayout() { + $('body').trigger('sticky_kit:recalc'); } const stickyPage = new AutoloadPage('stickyPage', () => { @@ -40,11 +54,13 @@ const stickyPage = new AutoloadPage('stickyPage', () => { shouldListenResize = true; } $sticky.data('sticky-cutoff-min', getCutoff(minEnabledSize)); + $sticky.data('sticky-enabled', false); }); updateStickies($stickies); if (shouldListenResize) { $(window).on('resize', _.throttle(() => updateStickies($stickies), 300)); } + $(document).on('vjLayout', _.throttle(stickyRelayout, 100)); }); export default stickyPage; diff --git a/packages/ui-default/components/sticky/sticky.page.styl b/packages/ui-default/components/sticky/sticky.page.styl deleted file mode 100644 index 8814ec861..000000000 --- a/packages/ui-default/components/sticky/sticky.page.styl +++ /dev/null @@ -1,11 +0,0 @@ -[data-sticky-parent] - display: flex - flex-wrap: wrap - - &::before, - &::after - display: none - - > .columns, - > .column - float: none diff --git a/packages/ui-default/components/table/StyledTable.js b/packages/ui-default/components/table/StyledTable.js index 576effe32..6b06781d1 100644 --- a/packages/ui-default/components/table/StyledTable.js +++ b/packages/ui-default/components/table/StyledTable.js @@ -1,11 +1,14 @@ +import 'sticky-kit/dist/sticky-kit'; + import $ from 'jquery'; +import _ from 'lodash'; import responsiveCutoff from 'vj/breakpoints.json'; import DOMAttachedObject from 'vj/components/DOMAttachedObject'; import { isBelow } from 'vj/utils/mediaQuery'; const navHeight = isBelow(responsiveCutoff.mobile) ? 0 - : ($('.nav').height() || 0); + : $('.nav').height(); export default class StyledTable extends DOMAttachedObject { static DOMAttachKey = 'vjStyledTableInstance'; @@ -13,63 +16,36 @@ export default class StyledTable extends DOMAttachedObject { static DOMAttachSelector = '.data-table'; constructor($dom) { - const $body = $dom.closest('.section__body'); - if ($body.length === 0) { + if ($dom.closest('.section__body').length === 0) { super(null); return; } super($dom); - // Scroll container only wraps the data table this.$container = $('
').addClass('section__table-container'); this.$container.insertBefore(this.$dom); - this.$container.append(this.$dom); - if ($body[0].style.overflow === 'hidden') return; - // Sentinel to detect when header becomes stuck - this.$sentinel = $('
').css({ height: 0, margin: 0, padding: 0 }); - - // Header sits outside the scroll container so sticky works against viewport this.$header = $(''); this.$header.attr('class', `${this.$dom.attr('class')} section__table-header`); - this.$header.css({ - position: 'sticky', - top: `${navHeight}px`, - }); - this.$sentinel.insertBefore(this.$container); - this.$header.insertBefore(this.$container); + this.$container + .append(this.$header) + .append(this.$dom); this.$header.empty(); this.$dom.children('colgroup').clone().appendTo(this.$header); this.$dom.children('thead').appendTo(this.$header); - // Sync horizontal scroll between header and body - this.$container.on('scroll', () => { - this.$header.css('transform', `translateX(-${this.$container.scrollLeft()}px)`); - }); - - // Detect sticky state via IntersectionObserver - const header = this.$header[0]; - const sentinel = this.$sentinel[0]; - if (window.IntersectionObserver) { - this._observer = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - header.classList.remove('is_stuck'); - } else { - header.classList.add('is_stuck'); - } - }, - { threshold: [0], rootMargin: `-${navHeight}px 0px 0px 0px` }, - ); - this._observer.observe(sentinel); - } + const stickyOptions = { + parent: this.$container, + offset_top: navHeight, + }; + _.defer(() => this.$header.stick_in_parent(stickyOptions)); } detach() { - if (this._observer) this._observer.disconnect(); super.detach(); + this.$header.trigger('sticky_kit:detach'); } } diff --git a/packages/ui-default/components/table/table.page.styl b/packages/ui-default/components/table/table.page.styl index 590db36f6..fe6d1f007 100644 --- a/packages/ui-default/components/table/table.page.styl +++ b/packages/ui-default/components/table/table.page.styl @@ -46,21 +46,22 @@ font-size: rem($font-size-small) .section__table-container + overflow: hidden overflow-x: auto .section__table-header background: $table-header-bg-color box-shadow: $table-header-shadow + position: relative z-index: 100 - overflow: hidden transition: box-shadow .2s linear - &.is_stuck - box-shadow: $table-header-shadow-floating - thead > tr border-bottom: 0 + &.is_stuck + box-shadow: $table-header-shadow-floating + .section__table-container, .section__body & > .data-table diff --git a/packages/ui-default/misc/structure.styl b/packages/ui-default/misc/structure.styl index 361fa2568..87a3e23d4 100644 --- a/packages/ui-default/misc/structure.styl +++ b/packages/ui-default/misc/structure.styl @@ -153,3 +153,6 @@ html .flex-cross-stretch align-items: stretch + +.overflow-hidden-horizontal + overflow-x: hidden diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 24b3c7198..bc967a2b4 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -103,6 +103,7 @@ "sass-loader": "^16.0.7", "schemastery": "^3.18.0", "shorty.js": "^1.0.1", + "sticky-kit": "^1.1.3", "style-loader": "^4.0.0", "stylus": "^0.64.0", "stylus-loader": "7.1.2", diff --git a/packages/ui-default/templates/partials/scoreboard.html b/packages/ui-default/templates/partials/scoreboard.html index 41f6edcb5..7f3343f54 100644 --- a/packages/ui-default/templates/partials/scoreboard.html +++ b/packages/ui-default/templates/partials/scoreboard.html @@ -18,7 +18,7 @@ {% endif %} -
+
{%- for column in rows[0] -%}