imagekit-media-library-widget
Version:
Javascript plugin for using Imagekit Media Library Widget
252 lines (216 loc) • 9.41 kB
text/typescript
import {
MediaLibraryWidgetOptions,
MediaLibraryWidgetCallback,
MediaLibraryWidgetOptionsExtended,
InitialViewParameterEnum,
} from './interfaces/index';
export class ImagekitMediaLibraryWidget {
private IK_HOST: string = 'https://eml.imagekit.io';
private IK_FRAME_TITLE: string = 'ImageKit Embedded Media Library';
private callbackFunction: MediaLibraryWidgetCallback;
private widgetHost: string;
private view: string | undefined;
private options: MediaLibraryWidgetOptionsExtended;
private modal: HTMLDivElement | undefined;
private ikFrame!: HTMLDivElement;
private styleEl: HTMLStyleElement | undefined;
private windowClickHandler: (event: MouseEvent) => void;
private messageHandler: (event: MessageEvent) => void;
private getDefaultOptions(): MediaLibraryWidgetOptionsExtended {
return {
className: "",
container: "",
containerDimensions: {
height: '100%',
width: '100%'
},
dimensions: {
height: '100%',
width: '100%'
},
style: {
border: 'none'
},
view: 'modal',
renderOpenButton: true,
};
}
constructor(options: MediaLibraryWidgetOptions, callback: MediaLibraryWidgetCallback) {
// Create global element references
this.widgetHost = window.location.href;
// Define option defaults
this.options = this.getDefaultOptions();
// Create options by extending defaults with the passed in arguments
if (options && typeof options === 'object') {
Object.assign(this.options, options);
}
// Set callback function
this.callbackFunction = callback && typeof callback === "function" ? callback : () => {};
this.view = this.options.view;
// Initialize event handlers for later removal
this.windowClickHandler = (event: MouseEvent) => {
if (this.modal && event.target === this.modal) {
this.close();
}
};
this.messageHandler = (event: MessageEvent) => {
if (event.origin !== this.IK_HOST) {
return;
}
if (event.data.eventType === "CLOSE_MEDIA_LIBRARY_WIDGET" || event.data.eventType === "INSERT") {
this.callbackFunction(event.data);
this.close();
}
};
this.registerStyles();
this.buildOut();
this.setListeners();
}
private registerStyles(): void {
this.styleEl = document.createElement('style');
this.styleEl.innerHTML = `
/* The Modal (background) */
.ik-media-library-widget-modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 2%; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.ik-media-library-widget-modal-content {
background-color: #fefefe;
margin: auto;
border: 1px solid #888;
width: 96%;
height: 94%;
}
`;
document.head.appendChild(this.styleEl);
}
private buildOut(): void {
let container: Element | null, docFragment: DocumentFragment, mainFrame: HTMLIFrameElement, button: HTMLButtonElement | undefined;
// If container is an HTML string, find the DOM node, else use it directly.
if (typeof this.options.container === "string") {
container = document.querySelector(this.options.container);
} else {
container = this.options.container;
}
// Create a DocumentFragment to build with
docFragment = document.createDocumentFragment();
// Create ikFrame element
this.ikFrame = document.createElement("div") as HTMLDivElement & { callback: MediaLibraryWidgetCallback };
this.ikFrame.className = this.options.className || ""; // Assign an empty string as the default value
this.ikFrame.style.height = this.options?.containerDimensions?.height || "100%";
this.ikFrame.style.width = this.options?.containerDimensions?.width || "100%";
mainFrame = document.createElement("iframe");
mainFrame.title = this.IK_FRAME_TITLE;
mainFrame.src = this.generateInitialUrl();
mainFrame.setAttribute('sandbox', 'allow-top-navigation allow-same-origin allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-downloads');
mainFrame.height = this.options?.dimensions?.height || "100%";
mainFrame.width = this.options?.dimensions?.width || "100%";
mainFrame.style.border = this.options.style.border;
this.ikFrame.appendChild(mainFrame);
if (this.view?.toLowerCase() !== 'modal') {
// Append ikFrame to DocumentFragment
docFragment.appendChild(this.ikFrame);
// Append DocumentFragment to body
if (container) container.appendChild(docFragment);
} else {
if (this.options.renderOpenButton) {
// create button
button = document.createElement("button");
button.innerHTML = "Open Media Library";
button.onclick = () => {
if (this.modal) {
this.modal.style.display = "block";
}
}
}
// create modal
const modal = document.createElement("div");
const modalContent = document.createElement("div");
modal.classList.add("ik-media-library-widget-modal");
modalContent.classList.add("ik-media-library-widget-modal-content");
modalContent.appendChild(this.ikFrame);
modal.appendChild(modalContent);
this.modal = modal;
// append button and modal to docFragment
if (button && this.options.renderOpenButton) {
docFragment.appendChild(button);
}
docFragment.appendChild(modal);
// append docFragment to container
if (container) container.appendChild(docFragment);
}
const iframe = document.querySelector(`[title="${this.IK_FRAME_TITLE}"]`) as HTMLIFrameElement;
if (iframe) {
this.postMessageOnLoad(iframe, this.options, this.IK_HOST);
}
}
private generateInitialUrl(): string {
if (this.options?.mlSettings?.initialView) {
// check if key exists in InitialViewParameterEnum
const key = Object.keys(this.options.mlSettings.initialView)[0];
if (Object.values(InitialViewParameterEnum).includes(key as InitialViewParameterEnum)) {
return `${this.IK_HOST}/media-library-widget?redirectTo=media-library-widget&isMediaLibraryWidget=true&widgetHost=${this.widgetHost}&mlWidgetInitialView=${btoa(JSON.stringify(this.options.mlSettings.initialView))}`;
}
}
return `${this.IK_HOST}/media-library-widget?redirectTo=media-library-widget&isMediaLibraryWidget=true&widgetHost=${this.widgetHost}`;
}
private postMessageOnLoad(iframe: HTMLIFrameElement, options: MediaLibraryWidgetOptionsExtended, IK_HOST: string) {
iframe.onload = function () {
if (iframe.contentWindow) {
iframe.contentWindow.postMessage(JSON.stringify({
mlSettings: options.mlSettings,
}), IK_HOST);
}
};
}
public open(): void {
if (this.view?.toLowerCase() === 'modal' && this.modal) {
this.modal.style.display = "block";
}
}
private close(): void {
if (this.view?.toLowerCase() === 'modal') {
this.closeModal();
}
}
private closeModal(): void {
if (this.modal) {
this.modal.style.display = "none";
}
}
public destroy(): void {
window.removeEventListener("click", this.windowClickHandler);
window.removeEventListener("message", this.messageHandler);
if (this.modal) {
this.modal.remove();
this.modal = undefined;
} else if (this.ikFrame && this.ikFrame.parentNode) {
this.ikFrame.parentNode.removeChild(this.ikFrame);
}
if (this.styleEl) {
this.styleEl.remove();
this.styleEl = undefined;
}
}
private setListeners(): void {
window.addEventListener("click", this.windowClickHandler);
window.addEventListener("message", this.messageHandler);
}
}
declare global {
interface Window {
IKMediaLibraryWidget: typeof ImagekitMediaLibraryWidget;
}
}
window.IKMediaLibraryWidget = ImagekitMediaLibraryWidget;
export * from "./interfaces/index"