UNPKG

@discoveryjs/discovery

Version:

Frontend framework for rapid data (JSON) analysis, shareable serverless reports and dashboards

131 lines (130 loc) 4.33 kB
import { hasOwn } from "./core/utils/object-utils.js"; import { randomId } from "./core/utils/id.js"; import { resolveColorSchemeValue } from "./core/color-scheme.js"; import { applyContainerStyles } from "./core/utils/container-styles.js"; import { injectStyles } from "./core/utils/inject-styles.js"; import { dataSource, syncLoaderWithProgressbar } from "./core/utils/load-data.js"; import { Progressbar } from "./core/utils/progressbar.js"; function createProgressbar(domReady) { return new Progressbar({ domReady, onTiming: ({ title, duration }) => console.log(`[Discovery/preloader] ${title} \u2013 ${duration}ms`) }); } function validateDataSourceType(dataSourceType) { if (dataSourceType && !hasOwn(dataSource, dataSourceType)) { throw new Error(`dataSource "${dataSourceType}" is not supported`); } } function applyStyles(el, container, options) { const { // legacy options, for backward compatibility darkmode, darkmodePersistent, // new options colorScheme = darkmode, colorSchemePersistent = darkmodePersistent } = options; const colorSchemeValue = resolveColorSchemeValue(colorScheme, colorSchemePersistent); if (applyContainerStyles(container, colorSchemeValue)) { el.setAttribute("darkmode", ""); } } preloader.colorScheme = true; export function preloader(options) { options = options || {}; const container = options.container || document.body; const el = document.createElement("div"); const shadowRoot = el.attachShadow({ mode: "open" }); const dataSourceType = options.dataSource; validateDataSourceType(dataSourceType); applyStyles(el, container, options); const optionsData = options.data; const loading = optionsData ? dataSourceType === "push" ? dataSource.push(options.loadDataOptions) : dataSource[dataSourceType || "url"](options.data, options.loadDataOptions) : { dataset: Promise.resolve(), state: void 0 }; if (optionsData && dataSourceType === "push") { const { start, push, finish } = loading; globalThis.discoveryLoader = { start, push, finish(...args) { delete globalThis.discoveryLoader; finish(...args); } }; } const domReady = injectStyles(shadowRoot, options.styles); const progressbar = options.progressbar || createProgressbar(domReady); const disposeEmbed = options.embed ? initPreloadEmbedApi(loading.state) : () => { }; if (loading.state) { syncLoaderWithProgressbar(loading, progressbar).catch(() => { }); } shadowRoot.append(progressbar.el); container.append(el); return Object.assign( loading.dataset, { el, shadowRoot, progressbar, disposeEmbed } ); } function initPreloadEmbedApi(loadingState) { const hostId = randomId(); const parentWindow = window.parent; const postponeMessages = []; const sendMessage = (type, payload) => { const message = { from: "discoveryjs-app", id: hostId, type, payload }; parentWindow.postMessage(message, "*"); }; const sendDestroyMessage = () => sendMessage("destroy", null); const processIncomingMessage = (event) => { const { id, type } = event.data || {}; if (id === hostId) { switch (type) { case "defineAction": case "setPageHash": case "setRouterPreventLocationUpdate": { postponeMessages.push(event.data); break; } default: console.error(`[Discovery/preloader] Unknown preload message type "${type}"`); } } }; if (parentWindow === window) { return; } addEventListener("message", processIncomingMessage, false); addEventListener("unload", sendDestroyMessage, false); sendMessage("preinit", { page: { hash: location.hash || "#" } }); const unsubscribeLoading = loadingState ? loadingState.subscribeSync((loadDataState) => { const { stage } = loadDataState; if (stage === "error" || stage === "received") { unsubscribeLoading(); } return sendMessage("loadingState", loadDataState); }) : () => { }; return () => { removeEventListener("message", processIncomingMessage, false); removeEventListener("unload", sendDestroyMessage, false); unsubscribeLoading(); sendDestroyMessage(); return { hostId, postponeMessages }; }; }