UNPKG

@tableflow/js

Version:

The JavaScript SDK for TableFlow. Embed an importer to collect and transform CSV files in your application.

196 lines (167 loc) 6.25 kB
import { TableFlowImporterProps } from "./types"; import "./index.css"; import postMessage from "./utils/postMessage"; let postMessages: string[] = []; type MessageType = "start" | "complete" | "close" | "reload" | "postMessageSubscribe"; type MessageData = { source: string; id: string; type: MessageType; importerId: string; data?: any; }; export default function createTableFlowImporter({ elementId = "tableflow-importer", importerId, hostUrl, isModal = true, modalOnCloseTriggered = (importComplete?: boolean) => null, modalCloseOnOutsideClick, template, darkMode = false, primaryColor = "#7a5ef8", metadata = "{}", onComplete, waitOnComplete, customStyles, className, showDownloadTemplateButton, skipHeaderRowSelection, cssOverrides, schemaless, schemalessReadOnly, schemalessDataTypes, trimSpaces, language, customTranslations, showUploadAnotherFileButton, }: TableFlowImporterProps) { // CSS classes const baseClass = "TableFlowImporter"; const themeClass = darkMode && `${baseClass}-dark`; const domElementClass = [`${baseClass}-${isModal ? "dialog" : "div"}`, themeClass, className].filter((i) => i).join(" "); // domElement element let domElement = document.getElementById(elementId) as HTMLDialogElement | HTMLDivElement; let isImportComplete = false; const backdropClick = () => { if (modalCloseOnOutsideClick) { modalOnCloseTriggered(isImportComplete); isImportComplete = false; } }; if (domElement === null) { domElement = isModal ? (document.createElement("dialog") as HTMLDialogElement) : (document.createElement("div") as HTMLDivElement); document.body.appendChild(domElement); domElement.setAttribute("id", elementId); if (isModal) domElement.addEventListener("click", backdropClick); } domElement.setAttribute("class", domElementClass); importerId = importerId === undefined || importerId?.trim() === "" ? "0" : importerId; // iframe element let urlParams = {}; let messageParams = { importerId, isModal: isModal ? "true" : "false", modalIsOpen: "true", template: parseObjectOrStringJSON("template", template), darkMode: darkMode.toString(), primaryColor, metadata: parseObjectOrStringJSON("metadata", metadata), onComplete: onComplete ? "true" : "false", waitOnComplete: parseOptionalBoolean(waitOnComplete), customStyles: parseObjectOrStringJSON("customStyles", customStyles), cssOverrides: parseObjectOrStringJSON("cssOverrides", cssOverrides), showDownloadTemplateButton: parseOptionalBoolean(showDownloadTemplateButton), skipHeaderRowSelection: parseOptionalBoolean(skipHeaderRowSelection), schemaless: parseOptionalBoolean(schemaless), schemalessReadOnly: parseOptionalBoolean(schemalessReadOnly), schemalessDataTypes: parseOptionalBoolean(schemalessDataTypes), trimSpaces: parseOptionalBoolean(trimSpaces), language: language || "en", customTranslations: parseObjectOrStringJSON("customTranslations", customTranslations), showUploadAnotherFileButton: parseOptionalBoolean(showUploadAnotherFileButton), }; const uploaderUrl = getUploaderUrl(urlParams, hostUrl); const postHandlers = { start: (messageData: MessageData) => { if (messageParams.modalIsOpen !== "true") { isImportComplete = false; const iframe = domElement.querySelector("iframe"); postMessage(iframe, { ...messageParams, modalIsOpen: "true" }); } }, complete: (messageData: MessageData) => { isImportComplete = true; onComplete?.(messageData?.data); postMessages.push(messageData?.id); }, close: (messageData: MessageData) => { modalOnCloseTriggered?.(isImportComplete); postMessages.push(messageData?.id); if (messageParams.modalIsOpen !== "false") { const iframe = domElement.querySelector("iframe"); postMessage(iframe, { ...messageParams, modalIsOpen: "false" }); } }, reload: (messageData: MessageData) => { isImportComplete = false; postMessages.push(messageData?.id); }, postMessageSubscribe: () => { const iframe = domElement.querySelector("iframe"); postMessage(iframe, { params: messageParams }); }, }; function messageListener(e: { data: MessageData } | MessageEvent<MessageData> | null) { if (!e || !e?.data) return; const messageData = e.data; if ( !messageData || messageData?.source !== "tableflow-importer" || // checks if there is importerId and if it is different from the current importerId (messageData?.importerId && messageData?.importerId !== importerId) || !messageData?.id || postMessages.includes(messageData.id) ) { return; } postHandlers[messageData.type]?.(messageData); } window.addEventListener("message", messageListener); domElement.innerHTML = `<iframe src="${uploaderUrl}" />`; return domElement; } function getUploaderUrl(urlParams: any, hostUrl?: string) { const searchParams = new URLSearchParams(urlParams); const defaultImporterUrl = "https://importer.tableflow.com"; return `${hostUrl ? hostUrl : defaultImporterUrl}?${searchParams}`; } // Allows for the user to pass in JSON as either an object or a string const parseObjectOrStringJSON = (name: string, param?: Record<string, unknown> | string): string => { if (typeof param === "undefined") { return ""; } let parsedObj: Record<string, unknown> = {}; if (typeof param === "string") { try { parsedObj = JSON.parse(param); } catch (e) { console.error( `The '${name}' prop is not a valid JSON string. This prop can either be a JSON string or JSON object. Please check the documentation for more details.` ); return ""; } } else { parsedObj = param; } // Replace % symbols with %25 for (const key in parsedObj) { if (typeof parsedObj[key] === "string") { parsedObj[key] = (parsedObj[key] as string).replace(/%(?!25)/g, "%25"); } } return JSON.stringify(parsedObj); }; const parseOptionalBoolean = (val?: boolean) => { return typeof val === "undefined" || val === null ? "" : val ? "true" : "false"; };