@discoveryjs/discovery
Version:
Frontend framework for rapid data (JSON) analysis, shareable serverless reports and dashboards
131 lines (130 loc) • 4.33 kB
JavaScript
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
};
};
}