From 943028ca151a5b6bb1100e2dec51f44e40d4742b Mon Sep 17 00:00:00 2001 From: korial29 Date: Fri, 5 May 2023 12:15:12 +0200 Subject: [PATCH] feat: add custom data support --- CHANGELOG.md | 5 ++ src/host.ts | 134 ++++++++++++++++++++++++++++++++++++-------- src/iframe.ts | 16 +++++- src/shared/types.ts | 1 + 4 files changed, 133 insertions(+), 23 deletions(-) create mode 100644 src/shared/types.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 351e26a..f9b66d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.0] + +### Changed + +- Add html custom data support ## [1.0.1] diff --git a/src/host.ts b/src/host.ts index 01483ec..0299d88 100644 --- a/src/host.ts +++ b/src/host.ts @@ -1,3 +1,5 @@ +import { CustomDataValues } from './shared/types'; + type IAdvizeBoxedInterfaceParams = { method: string; args: unknown[] }; type IAdvizeBoxedInterface = Array; @@ -27,23 +29,74 @@ export function resizeIFrame( iAdvizeSandbox: HTMLIFrameElement, data: IframePositioning, ) { - if (data.width !== undefined && data.height !== undefined) { - const iframe = iAdvizeSandbox; - const { width, height, left, right, bottom } = data; - const shouldReset = width === 0 && height === 0; - if (shouldReset) { - iframe.style.pointerEvents = 'none'; - iframe.style.width = '100vw'; - iframe.style.height = '100vh'; - return; - } - iframe.style.pointerEvents = 'inherit'; - iframe.style.width = `${width}px`; - iframe.style.height = `${height}px`; - iframe.style.bottom = `${bottom}px`; - if (left) iframe.style.left = `${left}px`; - else iframe.style.right = `${right}px`; + const iframe = iAdvizeSandbox; + const { width, height, left, right, bottom } = data; + const shouldReset = width === 0 && height === 0; + if (shouldReset) { + iframe.style.pointerEvents = 'none'; + iframe.style.width = '100vw'; + iframe.style.height = '100vh'; + return; } + iframe.style.pointerEvents = 'inherit'; + iframe.style.width = `${width}px`; + iframe.style.height = `${height}px`; + iframe.style.bottom = `${bottom}px`; + if (left) iframe.style.left = `${left}px`; + else iframe.style.right = `${right}px`; +} + +let observers: Record = {}; +let listeners: Record void> = {}; + +function observe( + toObserve: Element, + selector: string, + callback: (values: CustomDataValues) => void, +) { + // create an observer instance + const observer = new MutationObserver((mutations) => { + mutations.forEach(({ addedNodes, removedNodes, target }) => { + // Find textContent mutation + if ( + (addedNodes.length && addedNodes[0].nodeType === Node.TEXT_NODE) || + (removedNodes.length && removedNodes[0].nodeType === Node.TEXT_NODE) + ) { + callback({ [selector]: target.textContent }); + } + }); + }); + + observer.observe(toObserve, { + childList: true, + }); + observers[selector] = observer; +} + +function listenInput( + toListen: HTMLInputElement, + selector: string, + callback: (values: CustomDataValues) => void, +) { + let oldValue = toListen.value; + + const listener = (e: Event) => { + const input = e.target as HTMLInputElement; + if (oldValue !== input.value) { + callback({ [selector]: input.value }); + oldValue = input.value; + } + }; + toListen.addEventListener('input', listener); + + listeners[selector] = () => toListen.removeEventListener('input', listener); +} + +function unObserve() { + Object.values(observers).forEach((observer) => observer.disconnect()); + Object.values(listeners).forEach((removeListener) => removeListener()); + observers = {}; + listeners = {}; } export function initIAdvizeHost(sandboxId: string): void { @@ -69,6 +122,18 @@ export function initIAdvizeHost(sandboxId: string): void { ); } + function forwardCustomDataValues(customDataValues: CustomDataValues): void { + if (!iAdvizeSandbox.contentWindow) { + return; + } + iAdvizeSandbox.contentWindow.postMessage( + { + customDataValues, + }, + '*', + ); + } + iAdvizeSandbox.onload = () => { // Internal methods forwarding const buffer = [...hostWindow.iAdvizeBoxedInterface]; @@ -93,10 +158,35 @@ export function initIAdvizeHost(sandboxId: string): void { }; window.addEventListener('resize', forwardWindowDimensions); - window.addEventListener('message', (e) => { - if (e.source !== iAdvizeSandbox.contentWindow) { - return; - } - resizeIFrame(iAdvizeSandbox, e.data); - }); + window.addEventListener( + 'message', + ({ source, data: { width, height, customDataSelectors } }) => { + if (source !== iAdvizeSandbox.contentWindow) { + return; + } + + if (width !== undefined && height !== undefined) { + resizeIFrame(iAdvizeSandbox, { width, height }); + } + + if (customDataSelectors !== undefined) { + customDataSelectors?.forEach((selector: string) => { + const element = window.document.querySelector(selector); + if ((element)?.value !== undefined) { + listenInput( + element as HTMLInputElement, + selector, + forwardCustomDataValues, + ); + } else if (element) { + observe(element, selector, forwardCustomDataValues); + } + }); + + if (customDataSelectors.length === 0) { + unObserve(); + } + } + }, + ); } diff --git a/src/iframe.ts b/src/iframe.ts index 9914b8b..8b8b7c1 100644 --- a/src/iframe.ts +++ b/src/iframe.ts @@ -1,3 +1,5 @@ +import { CustomDataValues } from './shared/types'; + type IAdvizeGlobal = { [key: string]: Function; }; @@ -8,6 +10,7 @@ type iAdvizeInterfaceParameters = { args: unknown; hostHeight: number; hostWidth: number; + customDataValues: CustomDataValues; }; type iAdvizeInterfaceParametersInternals = { @@ -142,13 +145,20 @@ export function initIAdvizeIframe( } // Sharing the main context dimension, for sizing and positionning - const { hostWidth, hostHeight } = data; + const { hostWidth, hostHeight, customDataValues } = data; if (hostWidth && hostHeight) { context.host = { width: hostWidth, height: hostHeight, }; } + + // Send custom data values to the iAdvize tag + if (customDataValues) { + context.iAdvizeInterface.push(function (iAdvize: IAdvizeGlobal) { + iAdvize.set('app:customDataValues', customDataValues); + }); + } }, ); @@ -157,6 +167,10 @@ export function initIAdvizeIframe( iAdvize.on('app:boundariesChange', (boundaries: unknown) => { context.parent.postMessage(boundaries, '*'); }); + // Get HTML custom data + iAdvize.on('app:customDataSelectorsChange', (selectors: string[]) => { + context.parent.postMessage({ customDataSelectors: selectors }, '*'); + }); }); // Script insertion diff --git a/src/shared/types.ts b/src/shared/types.ts new file mode 100644 index 0000000..0a95c88 --- /dev/null +++ b/src/shared/types.ts @@ -0,0 +1 @@ +export type CustomDataValues = Record;