UNPKG

datocms-plugin-sdk

Version:
254 lines (212 loc) 5.96 kB
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<T extends (...args: any) => any> = Awaited< ReturnType<T> >; type ModeForPluginFrameCtx<T> = T extends ImposedSizePluginFrameCtx< infer Mode, any, any > ? Mode : never; export type MaybePromise<T> = T | Promise<T>; export function pick<T extends object, K extends keyof T>( obj: T, keys: readonly K[], ): Pick<T, K> { const result = {} as Pick<T, K>; for (const key of keys) { if (key in obj) { result[key] = obj[key]; } } return result; } export function omit<T extends object, K extends keyof T>( obj: T, keys: readonly K[], ): Omit<T, K> { const result = { ...obj } as T; for (const key of keys) { delete result[key]; } return result as Omit<T, K>; } export function fromOneFieldIntoMultipleAndResultsById<Result>( fn: | (( field: Field, ctx: Ctx<any, any> & { itemType: ItemType; }, ) => Result) | undefined, ) { return ( fields: Field[], ctx: Ctx<any, any>, ): Record<string, Result> | undefined => { if (!fn) { return undefined; } const result: Record<string, Result> = {}; 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<Properties>; }; export type Properties<Mode extends string = string> = { mode: Mode }; type OnChangeListenerFn = (newSettings: any) => void; export type ExtractRenderHooks<T extends Record<string, unknown>> = { [K in keyof T as K extends `render${string}` ? K : never]: T[K]; }; export type Bootstrapper< H extends keyof ExtractRenderHooks<FullConnectParameters>, > = { ( connectConfiguration: Partial<ExtractRenderHooks<FullConnectParameters>>, methods: Methods, initialProperties: Properties, ): undefined | OnChangeListenerFn; mode: H; }; export function containedRenderModeBootstrapper< Ctx extends SelfResizingPluginFrameCtx<any, {}, {}>, >( mode: ModeForPluginFrameCtx<Ctx>, callConfigurationMethod: ( connectConfiguration: Partial<ExtractRenderHooks<FullConnectParameters>>, ctx: Ctx, ) => void, ): Bootstrapper<Ctx['mode']> { const bootstrapper: Bootstrapper<Ctx['mode']> = ( connectConfiguration, methods, initialProperties, ) => { if (initialProperties.mode !== mode) { return undefined; } const sizingUtilities = buildSizingUtilities(methods); const render = (properties: Record<string, unknown>) => { callConfigurationMethod(connectConfiguration, { ...methods, ...properties, ...sizingUtilities, } as unknown as Ctx); }; render(initialProperties); return render; }; bootstrapper.mode = mode; return bootstrapper; } export function fullScreenRenderModeBootstrapper< Ctx extends ImposedSizePluginFrameCtx<any, {}, {}>, >( mode: ModeForPluginFrameCtx<Ctx>, callConfigurationMethod: ( connectConfiguration: Partial<ExtractRenderHooks<FullConnectParameters>>, ctx: Ctx, ) => void, ): Bootstrapper<Ctx['mode']> { const bootstrapper: Bootstrapper<Ctx['mode']> = ( connectConfiguration, methods, initialProperties, ) => { if (initialProperties.mode !== mode) { return undefined; } const render = (properties: Record<string, unknown>) => { 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<any, any, any>, ): 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, }; };