export function initGraphQlPlaygroundWidgets() { Array.from(document.querySelectorAll('.graphql-playground-widget')).forEach(el => { const playground = new GraphqlPlaygroundWidget(el as HTMLElement); playground.init(); }); } /** * A widget which lazily creates an iframe containing an instance of the graphql-playground app. * * The widget can contain one or more tabs with queries defined as below: * * @example * ``` *
*
* query { * products(options: { skip: 0 take: 3 }) { * totalItems * items { * name * variants { * sku * name * price * } * } * } * } *
*
* ``` */ export class GraphqlPlaygroundWidget { private readonly endpoint = 'https://demo.vendure.io/shop-api'; private tabs: Array<{ name: string; query: string; }>; private triggerYTop: number; private triggerYBottom: number; private activateTimer: any; constructor(private targetElement: HTMLElement) {} private scrollHandler = () => { clearTimeout(this.activateTimer); if (this.triggerYTop < window.scrollY && window.scrollY < this.triggerYBottom) { this.activateTimer = setTimeout(() => this.activate(), 250); } } init() { const tabElements: HTMLElement[] = Array.from(this.targetElement.querySelectorAll('.graphql-playground-tab')); this.tabs = tabElements.map(el => { return { name: el.dataset.name || 'Query', query: this.removeLeadingWhitespace(el.innerHTML), }; }); this.targetElement.innerHTML = this.loadingHtml; this.triggerYTop = this.targetElement.offsetTop - window.innerHeight; this.triggerYBottom = this.targetElement.offsetTop + window.innerHeight; window.addEventListener('scroll', this.scrollHandler); this.scrollHandler(); } activate() { this.targetElement.innerHTML = ''; window.removeEventListener('scroll', this.scrollHandler); const iframe = document.createElement('iframe'); const html = this.generateIframeContent(); this.targetElement.appendChild(iframe); if (iframe.contentWindow) { iframe.contentWindow.document.open(); iframe.contentWindow.document.write(html); iframe.contentWindow.document.close(); } } private removeLeadingWhitespace(s: string): string { const matches = s.match(/^\s+/m); if (!matches) { return s; } const indent = matches[0].replace(/\n/, ''); return s.replace(new RegExp(`^${indent}`, 'gm'), '').trim(); } private generateIframeContent(): string { return this.wrapInHtmlDocument(` `); } private generateTabsArray() { return this.tabs.map(tab => ` { endpoint: '${this.endpoint}', name: '${tab.name}', query: \`${tab.query}\` }, `); } private wrapInHtmlDocument(toWrap: string): string { return ` GraphQL Playground
${toWrap} `; } private loadingHtml = `
Loading GraphQL Playground
`; }