UNPKG

@3dsource/metabox-modular-configurator-api

Version:

API for Metabox MODULAR configurator

714 lines (688 loc) 23.7 kB
import { fromEvent } from 'rxjs'; /** * Base class for commands sent to the Metabox. */ class CommandBase { } /** * Wrapper helper function to use it with rxJs the same as `fromEvent` to listen to events from the Communicator. * @param {Communicator} target * @param eventName */ function fromCommunicatorEvent(target, eventName) { return fromEvent(target, eventName); } const MetaboxModularConfiguratorActions = { setEnvironment: 'setEnvironment', setEnvironmentMaterialById: 'setEnvironmentMaterialById', setComponent: 'setComponent', setComponentType: 'setComponentType', setComponentMaterialById: 'setComponentMaterialById', getPdf: 'getPdf', getScreenshot: 'getScreenshot', showEmbeddedMenu: 'showEmbeddedMenu', forceSetDeviceType: 'forceSetDeviceType', showOverlayInterface: 'showOverlayInterface', resetCamera: 'resetCamera', applyZoom: 'applyZoom', initShowcase: 'initShowcase', playShowcase: 'playShowcase', pauseShowcase: 'pauseShowcase', stopShowcase: 'stopShowcase', sendCommandToUnreal: 'sendCommandToUnreal', }; /** * Represents a command to get a PDF from Metabox. * @remarks * This class sends a message to the Metabox API to generate a PDF based on the current configuration. * This action does not require any parameters. * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { GetPdf, Communicator } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new GetPdf()); * }; */ class GetPdf extends CommandBase { /** * Creates an instance of GetPdf. * * @remarks * This constructor does not require any parameters. */ constructor() { super(); this.data = { action: MetaboxModularConfiguratorActions.getPdf }; } } /** * Represents a command to send to unreal and reset camera to initial. * @remarks * This class sends a message to the Metabox API to reset the camera on a scene. * This action does not require any parameters. * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { ResetCamera, Communicator } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new ResetCamera()); * }; */ class ResetCamera extends CommandBase { /** * Creates an instance of ResetCamera. * * @remarks * This constructor does not require any parameters. */ constructor() { super(); this.data = { action: MetaboxModularConfiguratorActions.resetCamera }; } } /** * Represents a command to ApplyZoom and change zoom camera on a scene. * @remarks * This class sends a command to the Metabox API to change zoom on a scene. * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { ApplyZoom, Communicator } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new ApplyZoom(10)); * api.sendCommandToMetaBox(new ApplyZoom(-10)); * }; */ class ApplyZoom extends CommandBase { /** * Creates an instance of ApplyZoom. * * @param {...number} zoom */ constructor(zoom) { super(); this.data = { action: MetaboxModularConfiguratorActions.applyZoom, payload: { zoom }, }; } } /** * Represents a command to Init Showcase for a product in a component on a scene and start a sequence for the product. * @remarks * This class sends a command to the Metabox API to init showcase if the product in a component has a sequence. * For check this needs to find a showcase property in current product in a component * If this property exist you can send init showcase command * @internal Use {@link UniversalConfiguratorProduct.product.showcase} * @remarks * You can send init showcase command only for root components, otherwise we will ignore this command * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { InitShowcase, Communicator } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new InitShowcase('ffea6b5c-3a8a-4f56-9417-e605acb5cca3'); * }; */ class InitShowcase extends CommandBase { /** * Creates an instance of InitShowcase. * * @param {string} productId */ constructor(productId) { super(); this.data = { action: MetaboxModularConfiguratorActions.initShowcase, payload: { productId }, }; } } /** * @internal * @hidden * Represents a command to send Unreal command. */ class UnrealCommand extends CommandBase { constructor(payload) { super(); this.data = { payload, action: MetaboxModularConfiguratorActions.sendCommandToUnreal, }; } } /** * Represents a command to Play Showcase for a product when it already init, and you call pause, for example, before it. * @remarks * This class sends a command to the Metabox API to play showcase for a product if it is already initialized and pause. * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { PlayShowcase, Communicator } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new PlayShowcase()); * }; */ class PlayShowcase extends CommandBase { /** * Creates an instance of PlayShowcase. * * @remarks * This constructor does not require any parameters. */ constructor() { super(); this.data = { action: MetaboxModularConfiguratorActions.playShowcase }; } } /** * Represents a command to Pause Showcase for a product when it already init and play, and you call pause. * @remarks * This class sends a command to the Metabox API to pause showcase for a product if it is already initialized and play. * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { PauseShowcase, Communicator } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new PauseShowcase()); * }; */ class PauseShowcase extends CommandBase { /** * Creates an instance of PauseShowcase. * * @remarks * This constructor does not require any parameters. */ constructor() { super(); this.data = { action: MetaboxModularConfiguratorActions.pauseShowcase }; } } /** * Represents a command to Stop Showcase for a product when it already init, and you want to destroy it. * @remarks * This class sends a command to the Metabox API to Stop showcase for a product if it is already initialized. * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { StopShowcase, Communicator } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new StopShowcase()); * }; */ class StopShowcase extends CommandBase { /** * Creates an instance of StopShowcase. * * @remarks * This constructor does not require any parameters. */ constructor() { super(); this.data = { action: MetaboxModularConfiguratorActions.stopShowcase }; } } /** * Represents a command to get a screenshot. * * **Important**: Invoking this command will buffer it until the viewport is ready. Only then will the request be processed. * * @example * import { GetScreenshot, Communicator, saveImage } from '@3dsource/metabox-modular-configurator-api'; * window.env3DSource.apiReady = (api: Communicator) => { * // Listen for events from to the Metabox API * api.addEventListener('screenshot', (data) => { * // Process the get screenshot response * saveImage(data, 'Render.png'); * }); * api.sendCommandToMetaBox(new GetScreenshot('image/png', { x: 1024, y: 1024 })); * }; */ class GetScreenshot extends CommandBase { /** * Constructs an instance of GetScreenshot. * @param {MimeType} mimeType - The output format. * @param {{ x: number; y: number }} [size] - Optional size in pixels. */ constructor(mimeType, size) { super(); this.data = { payload: { format: mimeType, size }, action: MetaboxModularConfiguratorActions.getScreenshot, }; } } /** * Represents a command to set a component by its data (ID, typeId, isRoot). * * @remarks * This action sends a message to the Metabox API to set a component using the provided component ID and its component type data. * * @example * import { SetComponent, Communicator } from '@3dsource/metabox-modular-configurator-api'; * * //...Assume that the integration is already implemented * window.env3DSource.apiReady = (api: Communicator) => { * api.setCommandToMetaBox(new SetComponent( * 'ffea6b5c-3a8a-4f56-9417-e605acb5cca3', 'typeId', true * )); * }; */ class SetComponent extends CommandBase { /** * Creates an instance of SetComponent. * * @param {string} id * @param {string} typeId * @param {boolean} isRoot */ constructor(id, typeId, isRoot) { super(); this.data = { payload: { id, typeId, isRoot }, action: MetaboxModularConfiguratorActions.setComponent, }; } } /** * Represents a command to set component material by its slot ID and Material ID. * * @example * import { SetComponentMaterial, Communicator } from '@3dsource/metabox-modular-configurator-api'; * * //...Assume that the integration is already implemented * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new SetComponentMaterial( * 'carpaint', * 'dd829d6e-9200-47a7-8d5b-af5df89b7e91', * )); * }; */ class SetComponentMaterial extends CommandBase { /** * Creates an instance of SetComponentMaterial. * * @param {string} slotId - The slot ID. * @param {string} materialId - The material ID. */ constructor(slotId, materialId) { super(); this.data = { payload: { slotId, materialId }, action: MetaboxModularConfiguratorActions.setComponentMaterialById, }; } } /** * Represents a command to set a component type by its ID. * * @remarks * This action sends a message to the Metabox API to set a component type using the provided component type ID. * * @example * import { SetComponentType, Communicator } from '@3dsource/metabox-modular-configurator-api'; * * //...Assume that the integration is already implemented * window.env3DSource.apiReady = (api: Communicator) => { * api.setCommandToMetaBox(new SetComponentType( * 'ffea6b5c-3a8a-4f56-9417-e605acb5cca3' * )); * }; */ class SetComponentType extends CommandBase { /** * Creates an instance of SetComponentType. * * @param {string} typeId */ constructor(typeId) { super(); this.data = { payload: { typeId }, action: MetaboxModularConfiguratorActions.setComponentType, }; } } /** * Represents a command to set the environment by its ID. * * @example * import { SetEnvironment, Communicator } from '@3dsource/metabox-modular-configurator-api'; * * //...Assume that the integration is already implemented * window.env3DSource.apiReady = (api: Communicator) => { * // Change the environment to '55555555-1234-1234-1234-01234567890' * api.sendCommandToMetaBox(new SetEnvironment('55555555-1234-1234-1234-01234567890')); * }; */ class SetEnvironment extends CommandBase { /** * Creates an instance of SetEnvironment. * * @param {string} environmentId - The environment ID. */ constructor(environmentId) { super(); this.data = { payload: { id: environmentId }, action: MetaboxModularConfiguratorActions.setEnvironment, }; } } /** * Represents a command to set an environment material by its slot ID and Material ID. * * @example * import { SetEnvironmentMaterial, Communicator } from '@3dsource/metabox-modular-configurator-api'; * * //...Assume that the integration is already implemented * window.env3DSource.apiReady = (api: Communicator) => { * api.sendCommandToMetaBox(new SetEnvironmentMaterial( * 'carpaint', * 'dd829d6e-9200-47a7-8d5b-af5df89b7e91', * )); * }; */ class SetEnvironmentMaterial extends CommandBase { /** * Creates an instance of SetEnvironmentMaterial. * * @param {string} slotId - The slot ID. * @param {string} materialId - The material ID. */ constructor(slotId, materialId) { super(); this.data = { payload: { slotId, materialId }, action: MetaboxModularConfiguratorActions.setEnvironmentMaterialById, }; } } /** * Represents a command to toggle the Embedded to the Metabox Menu. * * @example * import { ShowEmbeddedMenu, Communicator } from '@3dsource/metabox-modular-configurator-api'; * * //...Assume that the integration is already implemented * window.env3DSource.apiReady = (api: Communicator) => { * api.setCommandToMetaBox(new ShowEmbeddedMenu(true)); * }; */ class ShowEmbeddedMenu extends CommandBase { /** * Creates an instance of ShowEmbeddedMenu. * * @param {boolean} visible - A flag indicating whether the embedded menu should be visible. */ constructor(visible) { super(); this.data = { action: MetaboxModularConfiguratorActions.showEmbeddedMenu, payload: { visible }, }; } } /** * Represents a command to toggle the Unreal Overlay Interface Menu. * * @remarks * This action sends a message to the Metabox API to toggle the visibility of the Unreal overlay UI. * * @example * import { ShowOverlayInterface, Communicator } from '@3dsource/metabox-modular-configurator-api'; * * //...Assume that the integration is already implemented * window.env3DSource.apiReady = (api: Communicator) => { * api.setCommandToMetaBox(new ShowOverlayInterface(true)); * }; */ class ShowOverlayInterface extends CommandBase { /** * Creates an instance of ShowOverlayInterface. * * @param {boolean} visible - A flag indicating whether the Unreal overlay UI should be visible. */ constructor(visible) { super(); this.data = { action: MetaboxModularConfiguratorActions.showOverlayInterface, payload: { visible }, }; } } /** * EventDispatcher is a class that manages event listeners and dispatches events to them. */ class EventDispatcher { constructor() { /** * Storage for callback listeners by message type. */ this.listeners = []; } destroy() { this.listeners = []; } /** * Adds an event listener for receiving specific types of messages. * * @param {string} messageType - The message type to listen for. * @param callback - The callback function to execute when a message is received. */ addEventListener(messageType, callback) { this.listeners.push({ messageType, callback }); return this; } /** * Dispatches an event to all listeners of a specific message type. * * @param {string} messageType - The message type. * @param data - The data associated with the event. */ dispatchEvent(messageType, data) { this.listeners .filter((listener) => listener.messageType === messageType) .forEach((listener) => listener.callback(data)); return this; } /** * Removes an event listener for a specific type of message. * * @param {string} messageType - The message type. * @param callback - The callback function to remove. */ removeEventListener(messageType, callback) { this.listeners = this.listeners.filter((listener) => !(listener.messageType === messageType && listener.callback === callback)); return this; } } /** * Handles messaging between the host page and embedded to the Metabox content. * @internal Use {@link Communicator.createInstance} or the {@link integrateMetabox} helper to instantiate. */ class Communicator extends EventDispatcher { /** * Singleton reference to the current communicator instance. */ static { this.instance = null; } /** * Constructs a Communicator, replacing any existing instance, and begins listening for messages. * @internal */ constructor() { super(); /** * Bound handler for incoming postMessage events. */ this.binder = this.handleMessageReceived.bind(this, 'metaboxData'); Communicator.instance?.destroy(); Communicator.instance = this; window.addEventListener('message', this.binder); } /** * Listens for to the Metabox to signal readiness, then initializes communicator. * @param apiReadyCallback - Called with the new Communicator once to the Metabox is loaded. */ static createInstance(apiReadyCallback) { const startHandler = (event) => { const message = event.data; if (message?.envelope?.action === 'appLoaded') { apiReadyCallback(new Communicator()); window.removeEventListener('message', startHandler); } }; window.addEventListener('message', startHandler); } /** * Cleans up resources and stops listening for messages. */ destroy() { super.destroy(); window.removeEventListener('message', this.binder); } /** * Posts a command to the Metabox iframe. * @param command - An action command containing data to send. */ sendCommandToMetaBox(command) { const { data } = command; const iframe = document.getElementById('embeddedContent'); if (!iframe?.contentWindow) { console.warn('to the Metabox IFrame not found or not ready.'); return; } const message = { host: 'metaBoxHost', payload: { ...data }, }; // Replace '*' with a specific origin in production for better security. iframe.contentWindow.postMessage(message, '*'); } /** * Registers an event listener for messages dispatched by Metabox. * @override */ addEventListener(messageType, callback) { return super.addEventListener(messageType, callback); } /** * Dispatches a typed event to all registered listeners. * @override */ dispatchEvent(messageType, data) { return super.dispatchEvent(messageType, data); } /** * Removes a previously registered event listener. * @override */ removeEventListener(messageType, callback) { return super.removeEventListener(messageType, callback); } /** * Filters and dispatches incoming messages from to the Metabox. * @param messageType - Expected message type for filtering ('metaboxData'). * @param event - The postMessage event received on a window. */ handleMessageReceived(messageType, event) { // Optionally validate event.origin for security. if (messageType !== 'metaboxData' || event.data?.host !== 'metabox') { return; } const { eventType, payload } = event.data.envelope; this.dispatchEvent(eventType, payload); } } /** * Integrates Metabox Modular Configurator into the provided layout. * * @remarks * This function injects an iframe into a specified container element in the DOM and initializes * the communicator for interacting with the Metabox embedded content. * * @param iframeSrc - The source URL of the Metabox iframe. * Additional url parameters: * ```javascript * sidebar=false - disables the internal Metabox menu * introImage=https://example.com/image.png - sets the intro image * ``` * i.e., a final link will look like https://metabox.3dsource.com/metabox-configurator/modular/your-configurator-id?sidebar=false&introImage=https://example.com/image.png * * @param {string} containerId - The ID of the layout element. Defaults to 'embed3DSource'. * @param apiReadyCallback * @throws Error if the container element with the specified ID is not found. * * @example * import { * integrateMetabox, * } from '@3dsource/metabox-modular-configurator-api'; * * integrateMetabox( * 'https://metabox.3dsource.com/metabox-configurator/modular/12345678-1234-1234-1234-01234567890?introImage=https://example.com/intro.webp&sidebar=false', * 'embed3DSource' * ); */ function integrateMetabox(iframeSrc, containerId = 'embed3DSource', apiReadyCallback) { const container = document.getElementById(containerId); if (!container) { throw new Error(`Container element with id ${containerId} not found`); } const existingIframe = document.getElementById('embeddedContent'); if (existingIframe) { existingIframe.remove(); } Communicator.createInstance(apiReadyCallback); const style = document.createElement('style'); style.innerHTML = ` #${containerId} { width: 100%; height: 100%; overflow: hidden; position: relative; } `; document.head.appendChild(style); const iframe = document.createElement('iframe'); iframe.setAttribute('referrerPolicy', 'no-referrer-when-downgrade'); iframe.setAttribute('id', 'embeddedContent'); iframe.style.border = '0'; iframe.style.width = '100%'; iframe.style.height = '100%'; iframe.style.overflow = 'hidden'; iframe.src = iframeSrc; container.appendChild(iframe); } /** * Saves an image by triggering a download. * * @remarks * This function creates an anchor element, sets its `href` attribute to the provided image URL, * and triggers a click event to initiate a download with the specified filename. * * @param {string} imageUrl - The URL of the image to save. * @param {string} filename - The name of the file to save. */ function saveImage(imageUrl, filename) { const a = document.createElement('a'); a.href = imageUrl; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); } /** * Generated bundle index. Do not edit. */ export { ApplyZoom, CommandBase, Communicator, EventDispatcher, GetPdf, GetScreenshot, InitShowcase, MetaboxModularConfiguratorActions, PauseShowcase, PlayShowcase, ResetCamera, SetComponent, SetComponentMaterial, SetComponentType, SetEnvironment, SetEnvironmentMaterial, ShowEmbeddedMenu, ShowOverlayInterface, StopShowcase, UnrealCommand, fromCommunicatorEvent, integrateMetabox, saveImage }; //# sourceMappingURL=3dsource-metabox-modular-configurator-api.mjs.map