Path: blob/main/src/resources/formats/dashboard/quarto-dashboard.js
12926 views
const fillDivClasseses = ["widget-subarea", "lm-Widget", "leaflet-container"];12function requiresFill(el) {3if (el.tagName === "DIV") {4return fillDivClasseses.some((cls) => {5return el.classList.contains(cls);6});7}8return false;9}1011function ensureWidgetFills(el) {12if (!el.classList.contains("html-fill-item")) {13el.classList.add("html-fill-item");14}1516if (!el.classList.contains("html-fill-container")) {17el.classList.add("html-fill-container");18}19}2021function ensureWidgetsFill() {22// Find any jupyter widget containers and keep an eye on them23const widgetNodes = document.querySelectorAll(".widget-subarea");24for (const widgetEl of widgetNodes) {25ensureWidgetFills(widgetEl);26}27}2829function manageOverflow() {30// Don't let vega cells scroll internally31const cellOutputs = document.querySelectorAll(".cell-output-display div");32for (const cellOutput of cellOutputs) {33if (cellOutput.id.startsWith("altair-viz-")) {34cellOutput.parentElement.classList.add("no-overflow-x");35}36}37}3839function refreshStickyHeaders() {40// Deal with markdown tables41const markdownTables = document.querySelectorAll(".card-body > table");42for (const markdownTable of markdownTables) {43const scrollableArea = markdownTable.parentElement;44stickyThead.apply([markdownTable], { scrollableArea: scrollableArea });45}4647// Deal with iTables tables48const cellOutputNodes = document.querySelectorAll(".card-body .cell-output");49for (const cellOutputNode of cellOutputNodes) {50const iTable = cellOutputNode.querySelector(".itables table");51if (iTable) {52stickyThead.apply([iTable], { scrollableArea: cellOutputNode });53cellOutputNode.classList.add("dashboard-data-table");54}55}56}5758function updatePageFlow(scrolling) {59const dashboardContainerEl = document.querySelector(60".quarto-dashboard-content"61);62const tabContainerEl = document.querySelector(63".quarto-dashboard-content > .tab-content"64);6566// update the container and body classes67if (scrolling) {68dashboardContainerEl.classList.add("dashboard-scrolling");69document.body.classList.remove("dashboard-fill");70dashboardContainerEl.classList.remove("bslib-page-fill");7172if (tabContainerEl !== null && tabContainerEl.gridTemplateRows !== null) {73tabContainerEl.style.gridTemplateRows = "minmax(3em, max-content)";74}75} else {76dashboardContainerEl.classList.remove("dashboard-scrolling");77document.body.classList.add("dashboard-fill");78dashboardContainerEl.classList.add("bslib-page-fill");7980if (tabContainerEl !== null && tabContainerEl.gridTemplateRows !== null) {81tabContainerEl.style.gridTemplateRows = "minmax(3em, 1fr)";82}83}84}85window.document.documentElement.classList.add("hidden");86window.document.addEventListener("DOMContentLoaded", function (_event) {87ensureWidgetsFill();8889manageOverflow();90refreshStickyHeaders();9192// Fixup any sharing links that require urls93// Append url to any sharing urls94const sharingLinks = window.document.querySelectorAll(95"a.quarto-dashboard-link"96);97for (let i = 0; i < sharingLinks.length; i++) {98const sharingLink = sharingLinks[i];99const href = sharingLink.getAttribute("href");100if (href) {101sharingLink.setAttribute(102"href",103href.replace("|url|", window.location.href)104);105}106}107108// Try to process the hash and activate a tab109const hash = window.decodeURIComponent(window.location.hash);110if (hash.length > 0) {111QuartoDashboardUtils.showPage(hash, () => {112window.document.documentElement.classList.remove("hidden");113});114} else {115window.document.documentElement.classList.remove("hidden");116}117118// navigate to a tab when the history changes119window.addEventListener("popstate", function (e) {120const hash = window.decodeURIComponent(window.location.hash);121QuartoDashboardUtils.showPage(hash);122});123124// Hook tabs and use that to update history / active tabs125const navItems = document.querySelectorAll(".navbar .nav-item .nav-link");126for (const navItem of navItems) {127const linkHref = navItem.getAttribute("href");128navItem.addEventListener("click", () => {129const baseUrl = QuartoDashboardUtils.urlWithoutHash(window.location.href);130const hash = QuartoDashboardUtils.urlHash(linkHref);131const href = baseUrl + hash;132QuartoDashboardUtils.setLocation(href);133134const scrolling = navItem.getAttribute("data-scrolling");135if (scrolling !== null) {136updatePageFlow(scrolling.toLowerCase() === "true");137}138139return false;140});141}142143// Hook links in the body so users can link to pages144const linkEls = document.querySelectorAll(145".quarto-dashboard-content a:not(.nav-link)"146);147const currentUrl = new URL(window.location.href);148for (const linkEl of linkEls) {149const linkHref = linkEl.getAttribute("href");150// https://github.com/quarto-dev/quarto-cli/issues/9411151// only add the event listener for internal links152try {153const linkUrl = new URL(linkHref);154if (linkUrl.origin !== currentUrl.origin) {155continue;156}157} catch (_e) {158// if the link is not a valid URL, skip it159continue;160}161linkEl.addEventListener("click", () => {162QuartoDashboardUtils.showPage(linkHref);163return false;164});165}166const sidebar = window.document.querySelector(167".quarto-dashboard-content .bslib-sidebar-layout"168);169let prevWidth = window.document.body.clientWidth;170const sidebarCollapseClass = "sidebar-collapsed";171if (sidebar) {172const resizeObserver = new ResizeObserver(173throttle(function () {174const clientWidth = window.document.body.clientWidth;175if (prevWidth !== clientWidth) {176if (clientWidth <= 576) {177// Hide the sidebar178if (!sidebar.classList.contains(sidebarCollapseClass)) {179sidebar.classList.add(sidebarCollapseClass);180}181} else {182// Show the sidebar183if (sidebar.classList.contains(sidebarCollapseClass)) {184sidebar.classList.remove(sidebarCollapseClass);185}186}187prevWidth = clientWidth;188}189}, 2)190);191resizeObserver.observe(window.document.body);192}193194const observer = new MutationObserver(function (mutations) {195mutations.forEach(function (mutation) {196mutation.addedNodes.forEach(function (addedNode) {197if (requiresFill(addedNode)) {198ensureWidgetFills(addedNode);199}200});201});202});203observer.observe(document.body, { childList: true, subtree: true });204});205206// utils207window.QuartoDashboardUtils = {208setLocation: function (href) {209if (history && history.pushState) {210history.pushState({}, null, href);211// post "hashchange" for tools looking for it212if (window.parent?.postMessage) {213window.parent.postMessage(214{215type: "hashchange",216href: window.location.href,217},218"*"219);220}221} else {222window.location.replace(href);223}224setTimeout(function () {225window.scrollTo(0, 0);226}, 10);227},228isPage: function (hash) {229const tabPaneEl = document.querySelector(`.dashboard-page.tab-pane${hash}`);230return tabPaneEl !== null;231},232showPage: function (hash, fnCallback) {233// If the hash is empty, just select the first tab and activate that234if (hash === "") {235const firstTabPaneEl = document.querySelector(".dashboard-page.tab-pane");236if (firstTabPaneEl !== null) {237hash = `#${firstTabPaneEl.id}`;238}239}240241// Find the tab and activate it242const tabNodes = document.querySelectorAll(".navbar .nav-item .nav-link");243for (const tabEl of tabNodes) {244const target = tabEl.getAttribute("data-bs-target");245if (target === hash) {246const scrolling = tabEl.getAttribute("data-scrolling");247if (scrolling !== null) {248updatePageFlow(scrolling.toLowerCase() === "true");249}250251tabEl.classList.add("active");252} else {253tabEl.classList.remove("active");254}255}256257// Find the tabpanes and activate the hash tab258const tabPaneNodes = document.querySelectorAll(".dashboard-page.tab-pane");259for (const tabPaneEl of tabPaneNodes) {260if (`#${tabPaneEl.id}` === hash) {261tabPaneEl.classList.add("active");262} else {263tabPaneEl.classList.remove("active");264}265}266267if (fnCallback) {268fnCallback();269}270},271showLinkedValue: function (href) {272// check for a page link273if (this.isPage(href)) {274this.showPage(href);275} else {276window.open(href);277}278},279urlWithoutHash: function (url) {280const hashLoc = url.indexOf("#");281if (hashLoc != -1) return url.substring(0, hashLoc);282else return url;283},284urlHash: function (url) {285const hashLoc = url.indexOf("#");286if (hashLoc != -1) return url.substring(hashLoc);287else return "";288},289};290291function throttle(func, wait) {292let waiting = false;293return function () {294if (!waiting) {295func.apply(this, arguments);296waiting = true;297setTimeout(function () {298waiting = false;299}, wait);300}301};302}303304305