import type { SchemaTypes } from '@datocms/cma-client'; import { FullConnectParameters } from './connect'; import { Ctx } from './ctx/base'; import { SizingUtilities } from './ctx/commonExtras/sizing'; import { ImposedSizePluginFrameCtx, SelfResizingPluginFrameCtx, } from './ctx/pluginFrame'; type Field = SchemaTypes.Field; type ItemType = SchemaTypes.ItemType; export type AwaitedReturnType any> = Awaited< ReturnType >; type ModeForPluginFrameCtx = T extends ImposedSizePluginFrameCtx< infer Mode, any, any > ? Mode : never; export type MaybePromise = T | Promise; export function pick( obj: T, keys: readonly K[], ): Pick { const result = {} as Pick; for (const key of keys) { if (key in obj) { result[key] = obj[key]; } } return result; } export function omit( obj: T, keys: readonly K[], ): Omit { const result = { ...obj } as T; for (const key of keys) { delete result[key]; } return result as Omit; } export function fromOneFieldIntoMultipleAndResultsById( fn: | (( field: Field, ctx: Ctx & { itemType: ItemType; }, ) => Result) | undefined, ) { return ( fields: Field[], ctx: Ctx, ): Record | undefined => { if (!fn) { return undefined; } const result: Record = {}; for (const field of fields) { const itemType = ctx.itemTypes[ field.relationships.item_type.data.id ] as ItemType; result[field.id] = fn(field, { ...ctx, itemType }); } return result; }; } export type Methods = { getSettings: () => Promise; }; export type Properties = { mode: Mode }; type OnChangeListenerFn = (newSettings: any) => void; export type ExtractRenderHooks> = { [K in keyof T as K extends `render${string}` ? K : never]: T[K]; }; export type Bootstrapper< H extends keyof ExtractRenderHooks, > = { ( connectConfiguration: Partial>, methods: Methods, initialProperties: Properties, ): undefined | OnChangeListenerFn; mode: H; }; export function containedRenderModeBootstrapper< Ctx extends SelfResizingPluginFrameCtx, >( mode: ModeForPluginFrameCtx, callConfigurationMethod: ( connectConfiguration: Partial>, ctx: Ctx, ) => void, ): Bootstrapper { const bootstrapper: Bootstrapper = ( connectConfiguration, methods, initialProperties, ) => { if (initialProperties.mode !== mode) { return undefined; } const sizingUtilities = buildSizingUtilities(methods); const render = (properties: Record) => { callConfigurationMethod(connectConfiguration, { ...methods, ...properties, ...sizingUtilities, } as unknown as Ctx); }; render(initialProperties); return render; }; bootstrapper.mode = mode; return bootstrapper; } export function fullScreenRenderModeBootstrapper< Ctx extends ImposedSizePluginFrameCtx, >( mode: ModeForPluginFrameCtx, callConfigurationMethod: ( connectConfiguration: Partial>, ctx: Ctx, ) => void, ): Bootstrapper { const bootstrapper: Bootstrapper = ( connectConfiguration, methods, initialProperties, ) => { if (initialProperties.mode !== mode) { return undefined; } const render = (properties: Record) => { callConfigurationMethod(connectConfiguration, { ...methods, ...properties, } as unknown as Ctx); }; render(initialProperties); return render; }; bootstrapper.mode = mode; return bootstrapper; } function getMaxScrollHeight() { const elements = document.querySelectorAll('body *'); let maxVal = 0; for (let i = 0; i < elements.length; i++) { maxVal = Math.max(elements[i].getBoundingClientRect().bottom, maxVal); } return maxVal; } const buildSizingUtilities = ( methods: SelfResizingPluginFrameCtx, ): SizingUtilities => { let oldHeight: null | number = null; const updateHeight = (height?: number) => { const realHeight = height === undefined ? Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.getBoundingClientRect().height, getMaxScrollHeight(), ) : height; if (realHeight !== oldHeight) { methods.setHeight(realHeight); oldHeight = realHeight; } }; let resizeObserver: ResizeObserver | null = null; let mutationObserver: MutationObserver | null = null; const onMutation = () => updateHeight(); const startAutoResizer = () => { updateHeight(); if (!resizeObserver) { resizeObserver = new ResizeObserver(onMutation); resizeObserver.observe(document.documentElement); } if (!mutationObserver) { mutationObserver = new MutationObserver(onMutation); mutationObserver.observe(window.document.body, { attributes: true, childList: true, subtree: true, characterData: true, }); } }; const stopAutoResizer = () => { if (resizeObserver) { resizeObserver.disconnect(); resizeObserver = null; } if (mutationObserver) { mutationObserver.disconnect(); mutationObserver = null; } }; const isAutoResizerActive = () => Boolean(resizeObserver); return { updateHeight, startAutoResizer, stopAutoResizer, isAutoResizerActive, }; };