UNPKG

naja

Version:

Modern AJAX library for Nette Framework

143 lines (125 loc) 4.58 kB
import {Naja, Options} from '../Naja'; import {TypedEventListener} from '../utils'; declare module '../Naja' { interface Payload { snippets?: Record<string, string>; } } export type SnippetUpdateOperation = ((snippet: Element, content: string) => void | Promise<void>) | { updateElement(snippet: Element, content: string): void | Promise<void>; updateIndex(currentContent: string, newContent: string): string; }; export class SnippetHandler extends EventTarget { public readonly op: Record<'replace' | 'prepend' | 'append', SnippetUpdateOperation> = { replace: { updateElement(snippet, content) { snippet.innerHTML = content; }, updateIndex(_, newContent) { return newContent; }, }, prepend: { updateElement(snippet, content) { snippet.insertAdjacentHTML('afterbegin', content); }, updateIndex(currentContent, newContent) { return newContent + currentContent; }, }, append: { updateElement(snippet, content) { snippet.insertAdjacentHTML('beforeend', content); }, updateIndex(currentContent, newContent) { return currentContent + newContent; }, }, }; public constructor(naja: Naja) { super(); naja.addEventListener('success', (event) => { const {options, payload} = event.detail; if ( ! payload.snippets) { return; } this.updateSnippets(payload.snippets, false, options); }); } public static findSnippets( predicate?: (snippet: Element) => boolean, document: Document = window.document, ): Record<string, string> { const result: Record<string, string> = {}; const snippets = document.querySelectorAll('[id^="snippet-"]'); snippets.forEach((snippet) => { if (predicate?.(snippet) ?? true) { result[snippet.id] = snippet.innerHTML; } }); return result; } public async updateSnippets(snippets: Record<string, string>, fromCache = false, options: Options = {}): Promise<void> { await Promise.all(Object.keys(snippets).map(async (id) => { const snippet = document.getElementById(id); if (snippet) { await this.updateSnippet(snippet, snippets[id], fromCache, options); } })); } public async updateSnippet(snippet: Element, content: string, fromCache: boolean, options: Options): Promise<void> { let operation = this.op.replace; if ((snippet.hasAttribute('data-naja-snippet-prepend') || snippet.hasAttribute('data-ajax-prepend')) && ! fromCache) { operation = this.op.prepend; } else if ((snippet.hasAttribute('data-naja-snippet-append') || snippet.hasAttribute('data-ajax-append')) && ! fromCache) { operation = this.op.append; } const canUpdate = this.dispatchEvent(new CustomEvent('beforeUpdate', { cancelable: true, detail: { snippet, content, fromCache, operation, changeOperation(value: SnippetUpdateOperation) { operation = value; }, options, }, })); if ( ! canUpdate) { return; } this.dispatchEvent(new CustomEvent('pendingUpdate', { detail: { snippet, content, fromCache, operation, options, }, })); const updateElement = typeof operation === 'function' ? operation : operation.updateElement; await updateElement(snippet, content); this.dispatchEvent(new CustomEvent('afterUpdate', { detail: { snippet, content, fromCache, operation, options, }, })); } declare public addEventListener: <K extends keyof SnippetHandlerEventMap | string>(type: K, listener: TypedEventListener<SnippetHandler, K extends keyof SnippetHandlerEventMap ? SnippetHandlerEventMap[K] : CustomEvent>, options?: boolean | AddEventListenerOptions) => void; declare public removeEventListener: <K extends keyof SnippetHandlerEventMap | string>(type: K, listener: TypedEventListener<SnippetHandler, K extends keyof SnippetHandlerEventMap ? SnippetHandlerEventMap[K] : CustomEvent>, options?: boolean | AddEventListenerOptions) => void; } export type BeforeUpdateEvent = CustomEvent<{snippet: Element, content: string, fromCache: boolean, operation: SnippetUpdateOperation, changeOperation: (value: SnippetUpdateOperation) => void, options: Options}>; export type PendingUpdateEvent = CustomEvent<{snippet: Element, content: string, fromCache: boolean, operation: SnippetUpdateOperation, options: Options}>; export type AfterUpdateEvent = CustomEvent<{snippet: Element, content: string, fromCache: boolean, operation: SnippetUpdateOperation, options: Options}>; interface SnippetHandlerEventMap { beforeUpdate: BeforeUpdateEvent; pendingUpdate: PendingUpdateEvent; afterUpdate: AfterUpdateEvent; }