From ff76d4d496995cb6819be4c47a784aabfd2be970 Mon Sep 17 00:00:00 2001 From: Charles Teague Date: Thu, 16 Nov 2023 15:59:00 -0500 Subject: [PATCH] Add stickythead dependency and inject it --- .../src/common/update-html-dependencies.ts | 28 +++++++++++++++++++ src/format/dashboard/format-dashboard.ts | 13 +++++---- .../formats/dashboard/js/stickythead.js | 2 ++ .../formats/dashboard/quarto-dashboard.js | 6 ++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 src/resources/formats/dashboard/js/stickythead.js diff --git a/package/src/common/update-html-dependencies.ts b/package/src/common/update-html-dependencies.ts index 0fa83b152f..2cf0dd1691 100644 --- a/package/src/common/update-html-dependencies.ts +++ b/package/src/common/update-html-dependencies.ts @@ -489,6 +489,9 @@ export async function updateHtmlDependencies(config: Configuration) { // Cookie-Consent await updateCookieConsent(config, "4.0.0", workingDir); + // Sticky table headers + await updateStickyThead(config, workingDir); + // Clean existing directories [bsThemesDir, bsDistDir].forEach((dir) => { if (existsSync(dir)) { @@ -594,6 +597,31 @@ async function updateCookieConsent( info("Done\n"); } + +async function updateStickyThead( + config: Configuration, + working: string +) { + const fileName = "stickythead.js"; + const url = `https://raw.githubusercontent.com/rohanpujaris/stickythead/master/dist/${fileName}`; + const tempPath = join(working, fileName); + + info(`Downloading ${url}`); + await download(url, tempPath); + + const targetDir = join( + config.directoryInfo.src, + "resources", + "formats", + "dashboard", + "js" + ); + await ensureDir(targetDir); + + await Deno.copyFile(tempPath, join(targetDir, fileName)); + info("Done\n"); +} + async function updateHtmlTools( version: string, working: string, diff --git a/src/format/dashboard/format-dashboard.ts b/src/format/dashboard/format-dashboard.ts index b005cee0e2..939611501d 100644 --- a/src/format/dashboard/format-dashboard.ts +++ b/src/format/dashboard/format-dashboard.ts @@ -35,11 +35,7 @@ import { InternalError } from "../../core/lib/error.ts"; import { formatResourcePath } from "../../core/resources.ts"; import { ProjectContext } from "../../project/types.ts"; import { registerWriterFormatHandler } from "../format-handlers.ts"; -import { - kBootstrapDependencyName, - kPageLayout, - kPageLayoutCustom, -} from "../html/format-html-shared.ts"; +import { kPageLayout, kPageLayoutCustom } from "../html/format-html-shared.ts"; import { htmlFormat } from "../html/format-html.ts"; import { join } from "path/mod.ts"; @@ -60,7 +56,6 @@ import { import { processSidebars } from "./format-dashboard-sidebar.ts"; import { kTemplatePartials } from "../../command/render/template.ts"; import { processPages } from "./format-dashboard-page.ts"; -import { sassLayer } from "../../core/sass.ts"; import { processNavButtons } from "./format-dashboard-navbutton.ts"; import { processNavigation } from "./format-dashboard-website.ts"; import { projectIsWebsite } from "../../project/project-shared.ts"; @@ -165,6 +160,12 @@ export function dashboardFormat() { path: formatResourcePath("dashboard", "quarto-dashboard.js"), }); + // Add the sticky headers script + scripts.push({ + name: "stickythead.js", + path: formatResourcePath("dashboard", join("js", "stickythead.js")), + }); + const componentDir = join( "bslib", "components", diff --git a/src/resources/formats/dashboard/js/stickythead.js b/src/resources/formats/dashboard/js/stickythead.js new file mode 100644 index 0000000000..b6d42b20d2 --- /dev/null +++ b/src/resources/formats/dashboard/js/stickythead.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.stickyThead=t():e.stickyThead=t()}(window,function(){return i={},o.m=n=[function(e,t,n){"use strict";function c(e){var t=e.getBoundingClientRect();return{top:t.top+window.pageYOffset,left:t.left+window.pageXOffset}}function u(e){if(e==window)return window.innerHeight;if(e==document)return Math.max(document.documentElement.clientHeight,document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight);var t=parseFloat(getComputedStyle(e,null).height.replace("px",""));return t||e.offsetHeight}function f(e){if(e==window)return window.innerWidth;if(e==document)return Math.max(document.documentElement.clientWidth,document.body.scrollWidth,document.documentElement.scrollWidth,document.body.offsetWidth,document.documentElement.offsetWidth);var t=parseFloat(getComputedStyle(e,null).width.replace("px",""));return t||e.offsetWidth}function p(e,t){for(var n in t)e.style[n]=t[n]}function g(e,t,n){var i=new CustomEvent(e,n?{}:{details:n});t.dispatchEvent(i)}n.r(t),n.d(t,"apply",function(){return i});var l={_storage:new WeakMap,put:function(e,t,n){this._storage.has(e)||this._storage.set(e,new Map),this._storage.get(e).set(t,n)},get:function(e,t){var n=this._storage.get(e);return n&&n.get(t)},remove:function(e,t){var n=this._storage.get(e);if(n){var i=n.delete(t);return 0===!n.size&&this._storage.delete(e),i}}};function i(e,n){var s="stickyThead",o=0,r={fixedOffset:0,leftOffset:0,marginTop:0,objDocument:document,objHead:document.head,objWindow:window,scrollableArea:window,cacheHeaderHeight:!1,zIndex:3};function i(i,t){var a=this;a.el=i,a.id=o++,a.$clonedHeader=null,a.$originalHeader=null,a.cachedHeaderHeight=null,a.isSticky=!1,a.hasBeenSticky=!1,a.leftOffset=null,a.topOffset=null,a.init=function(){a.setOptions(t),a.el.style.padding="0px",a.$originalHeader=a.el.querySelector("thead"),a.$clonedHeader=a.$originalHeader.cloneNode(!0),g("clonedHeader."+s,a.el,a.$clonedHeader),a.$clonedHeader.setAttribute("class","tableFloatingHeader"),p(a.$clonedHeader,{display:"none",opacity:0}),a.$originalHeader.setAttribute("class","tableFloatingHeaderOriginal"),a.$originalHeader.insertAdjacentElement("afterend",a.$clonedHeader);var e=document.createElement("style");e.setAttribute("type","text/css"),e.setAttribute("media","print"),e.innerHTML=".tableFloatingHeader{display:none !important;}.tableFloatingHeaderOriginal{position:static !important;}",a.$printStyle=e,a.$head.appendChild(a.$printStyle),a.$clonedHeader.querySelectorAll("input, select").forEach(function(e){e.setAttribute("disabled",!0)}),a.updateWidth(),a.toggleHeaders(),a.bind()},a.destroy=function(){a.el&&a.el.removeEventListener("destroyed",a.teardown),a.teardown()},a.teardown=function(){a.isSticky&&p(a.$originalHeader,{position:"static"}),l.remove(a.el,s),a.unbind(),a.$clonedHeader.parentNode.removeChild(a.$clonedHeader),a.$originalHeader.classList.remove("tableFloatingHeaderOriginal"),p(a.$originalHeader,{visibility:"visible"}),a.$printStyle.parentNode.removeChild(a.$printStyle),a.el=null,a.$el=null},a.bind=function(){a.$scrollableArea.addEventListener("scroll",a.toggleHeaders),a.isWindowScrolling||(a.$window.addEventListener("scroll",a.setPositionValues),a.$window.addEventListener("resize",a.toggleHeaders)),a.$scrollableArea.addEventListener("resize",a.toggleHeaders),a.$scrollableArea.addEventListener("resize",a.updateWidth)},a.unbind=function(){a.$scrollableArea.removeEventListener("scroll",a.toggleHeaders),a.isWindowScrolling||(a.$window.removeEventListener("scroll",a.setPositionValues),a.$window.removeEventListener("resize",a.toggleHeaders)),a.$scrollableArea.removeEventListener("resize",a.updateWidth)},a.debounce=function(n,i){var o=null;return function(){var e=this,t=arguments;clearTimeout(o),o=setTimeout(function(){n.apply(e,t)},i)}},a.toggleHeaders=a.debounce(function(){if(a.el){var e,t,n,i=a.isWindowScrolling?isNaN(a.options.fixedOffset)?a.options.fixedOffset.offsetHeight:a.options.fixedOffset:c(a.$scrollableArea).top+(isNaN(a.options.fixedOffset)?0:a.options.fixedOffset),o=c(a.el),r=a.$scrollableArea.pageYOffset+i,l=a.$scrollableArea.pageXOffset,d=a.isWindowScrolling?r>o.top:i>o.top;d&&(t=a.options.cacheHeaderHeight?a.cachedHeaderHeight:u(a.$originalHeader),n=(a.isWindowScrolling?r:0)u(a.$document)||t<0||t+f(a.$window)>f(a.$document)||p(a.$originalHeader,{top:a.topOffset-(a.isWindowScrolling?0:e)+"px",left:a.leftOffset-(a.isWindowScrolling?0:t)+"px"})},0),a.updateWidth=a.debounce(function(){if(a.isSticky){a.$originalHeaderCells||(a.$originalHeaderCells=a.$originalHeader.querySelectorAll("th,td")),a.$clonedHeaderCells||(a.$clonedHeaderCells=a.$clonedHeader.querySelectorAll("th,td"));var e=a.getWidth(a.$clonedHeaderCells);a.setWidth(e,a.$clonedHeaderCells,a.$originalHeaderCells),a.$originalHeader.style.width=f(a.$clonedHeader),a.options.cacheHeaderHeight&&(a.cachedHeaderHeight=u(a.$clonedHeader))}},0),a.getWidth=function(e){var d=[];return e.forEach(function(e,t){var n;if("border-box"===getComputedStyle(e).boxSizing){var i=e.getBoundingClientRect();n=i.width?i.width:i.right-i.left}else{if("collapse"===a.$originalHeader.querySelector("th").style.borderCollapse)if(window.getComputedStyle)n=parseFloat(window.getComputedStyle(e,null).width);else{var o=parseFloat(e.style.paddingLeft),r=parseFloat(e.style.paddingRight),l=parseFloat(e.style.borderWidth);n=e.offsetWidth-o-r-l}else n=f(e)}d[t]=n}),d},a.setWidth=function(i,e,o){e.forEach(function(e,t){var n=i[t];p(o[t],{minWidth:n+"px",maxWidth:n+"px"})})},a.resetWidth=function(e,n){e.forEach(function(e,t){p(n[t],{minWidth:i.style.minWidth,maxWidth:i.style.maxWidth})})},a.setOptions=function(e){var t,n;a.options=(t=e,n=r,Object.keys(t||{}).forEach(function(e){n[e]=t[e]}),n),a.$window=a.options.objWindow,a.$head=a.options.objHead,a.$document=a.options.objDocument,a.$scrollableArea=a.options.scrollableArea,a.isWindowScrolling=a.$scrollableArea===a.$window},a.updateOptions=function(e){a.setOptions(e),a.unbind(),a.bind(),a.updateWidth(),a.toggleHeaders()},a.el.addEventListener("destroyed",a.teardown.bind(a)),a.init()}return e.forEach(function(e){var t=l.get(e,s);t?"string"==typeof n?t[n].apply(t):t.updateOptions(n):"destroy"!==n&&l.put(e,s,new i(e,n))})}window.dataStore=l,"function"!=typeof window.CustomEvent&&(window.CustomEvent=function(e,t){t=t||{bubbles:!1,cancelable:!1,detail:null};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),n})}],o.c=i,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)o.d(n,i,function(e){return t[e]}.bind(null,i));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=0);function o(e){if(i[e])return i[e].exports;var t=i[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,o),t.l=!0,t.exports}var n,i}); +//# sourceMappingURL=stickythead.js.map \ No newline at end of file diff --git a/src/resources/formats/dashboard/quarto-dashboard.js b/src/resources/formats/dashboard/quarto-dashboard.js index a0e4517160..bf05c8a979 100644 --- a/src/resources/formats/dashboard/quarto-dashboard.js +++ b/src/resources/formats/dashboard/quarto-dashboard.js @@ -42,6 +42,12 @@ window.document.addEventListener("DOMContentLoaded", function (_event) { manageOverflow(); + const markdownTables = document.querySelectorAll(".card-body > table"); + for (const markdownTable of markdownTables) { + const scrollableArea = markdownTable.parentElement; + stickyThead.apply([markdownTable], { scrollableArea: scrollableArea }); + } + // Fixup any sharing links that require urls // Append url to any sharing urls const sharingLinks = window.document.querySelectorAll(