From b2d09b8774817c37af0ed3dcc20b4bd4b437e9ae Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 21 Feb 2022 02:28:50 -0500 Subject: [PATCH] Moves bootstrap loading into base Improves template replacement bindings --- src/webviews/apps/home/home.ts | 63 +++++++++++---------------- src/webviews/apps/rebase/rebase.ts | 3 +- src/webviews/apps/settings/settings.ts | 5 +-- src/webviews/apps/shared/appBase.ts | 6 ++- src/webviews/apps/shared/appWithConfigBase.ts | 4 +- src/webviews/apps/shared/dom.ts | 36 +++++++++++++++ src/webviews/apps/welcome/welcome.ts | 5 +-- 7 files changed, 73 insertions(+), 49 deletions(-) diff --git a/src/webviews/apps/home/home.ts b/src/webviews/apps/home/home.ts index dcd1c7f..eae73e3 100644 --- a/src/webviews/apps/home/home.ts +++ b/src/webviews/apps/home/home.ts @@ -1,4 +1,4 @@ -/*global window*/ +/*global*/ import './home.scss'; import { provideVSCodeDesignSystem, vsCodeButton, vsCodeDivider } from '@vscode/webview-ui-toolkit'; import { Disposable } from 'vscode'; @@ -13,8 +13,7 @@ export class HomeApp extends App { private $slot2!: HTMLDivElement; constructor() { - super('HomeApp', (window as any).bootstrap); - (window as any).bootstrap = undefined; + super('HomeApp'); } protected override onInitialize() { @@ -34,7 +33,7 @@ export class HomeApp extends App { protected override onBind(): Disposable[] { const disposables = super.onBind?.() ?? []; - disposables.push(DOM.on('[data-action]', 'click', (e, target: HTMLElement) => this.onClicked(e, target))); + disposables.push(DOM.on('[data-action]', 'click', (e, target: HTMLElement) => this.onActionClicked(e, target))); return disposables; } @@ -58,7 +57,7 @@ export class HomeApp extends App { } } - private onClicked(e: MouseEvent, target: HTMLElement) { + private onActionClicked(e: MouseEvent, target: HTMLElement) { const action = target.dataset.action; if (action?.startsWith('command:')) { this.sendCommand(ExecuteCommandType, { command: action.slice(8) }); @@ -68,8 +67,8 @@ export class HomeApp extends App { private updateState() { const { subscription, welcomeVisible } = this.state; if (subscription.account?.verified === false) { - this.insertTemplate('state:verify-email', this.$slot1); - this.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); + DOM.insertTemplate('state:verify-email', this.$slot1); + DOM.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); return; } @@ -80,57 +79,47 @@ export class HomeApp extends App { switch (subscription.state) { case SubscriptionState.Free: if (welcomeVisible) { - this.insertTemplate('welcome', this.$slot1); - this.insertTemplate('state:free', this.$slot2); + DOM.insertTemplate('welcome', this.$slot1); + DOM.insertTemplate('state:free', this.$slot2); } else { - this.insertTemplate('state:free', this.$slot1); - this.insertTemplate('links', this.$slot2); + DOM.insertTemplate('state:free', this.$slot1); + DOM.insertTemplate('links', this.$slot2); } break; case SubscriptionState.FreeInPreview: { const remaining = getSubscriptionTimeRemaining(subscription, 'days') ?? 0; - this.insertTemplate('state:free-preview', this.$slot1, { - previewDays: `${remaining === 1 ? `${remaining} more day` : `${remaining} more days`}`, + DOM.insertTemplate('state:free-preview', this.$slot1, { + bindings: { + previewDays: `${remaining === 1 ? `${remaining} more day` : `${remaining} more days`}`, + }, }); - this.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); + DOM.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); break; } case SubscriptionState.FreePreviewExpired: - this.insertTemplate('state:free-preview-expired', this.$slot1); - this.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); + DOM.insertTemplate('state:free-preview-expired', this.$slot1); + DOM.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); break; case SubscriptionState.FreePlusInTrial: { const remaining = getSubscriptionTimeRemaining(subscription, 'days') ?? 0; - this.insertTemplate('state:plus-trial', this.$slot1, { - trialDays: `${remaining === 1 ? `${remaining} day` : `${remaining} days`}`, + DOM.insertTemplate('state:plus-trial', this.$slot1, { + bindings: { + trialDays: `${remaining === 1 ? `${remaining} day` : `${remaining} days`}`, + }, }); - this.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); + DOM.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); break; } case SubscriptionState.FreePlusTrialExpired: - this.insertTemplate('state:plus-trial-expired', this.$slot1); - this.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); + DOM.insertTemplate('state:plus-trial-expired', this.$slot1); + DOM.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); break; case SubscriptionState.Paid: - this.insertTemplate('state:paid', this.$slot1); - this.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); + DOM.insertTemplate('state:paid', this.$slot1); + DOM.insertTemplate(welcomeVisible ? 'welcome' : 'links', this.$slot2); break; } } - - private insertTemplate(id: string, $slot: HTMLDivElement, bindings?: Record): void { - const $template = (document.getElementById(id) as HTMLTemplateElement)?.content.cloneNode(true); - $slot.replaceChildren($template); - - if (bindings != null) { - for (const [key, value] of Object.entries(bindings)) { - const $el = $slot.querySelector(`[data-bind="${key}"]`); - if ($el != null) { - $el.textContent = String(value); - } - } - } - } } new HomeApp(); diff --git a/src/webviews/apps/rebase/rebase.ts b/src/webviews/apps/rebase/rebase.ts index 4ae78d9..a7a49e0 100644 --- a/src/webviews/apps/rebase/rebase.ts +++ b/src/webviews/apps/rebase/rebase.ts @@ -37,8 +37,7 @@ class RebaseEditor extends App { private readonly commitTokenRegex = new RegExp(encodeURIComponent(`\${commit}`)); constructor() { - super('RebaseEditor', (window as any).bootstrap); - (window as any).bootstrap = undefined; + super('RebaseEditor'); } protected override onInitialize() { diff --git a/src/webviews/apps/settings/settings.ts b/src/webviews/apps/settings/settings.ts index b439db2..5f7489b 100644 --- a/src/webviews/apps/settings/settings.ts +++ b/src/webviews/apps/settings/settings.ts @@ -1,4 +1,4 @@ -/*global window document IntersectionObserver*/ +/*global document IntersectionObserver*/ import './settings.scss'; import { State } from '../../settings/protocol'; import { AppWithConfig } from '../shared/appWithConfigBase'; @@ -15,8 +15,7 @@ export class SettingsApp extends AppWithConfig { private _sections = new Map(); constructor() { - super('SettingsApp', (window as any).bootstrap); - (window as any).bootstrap = undefined; + super('SettingsApp'); } protected override onInitialize() { diff --git a/src/webviews/apps/shared/appBase.ts b/src/webviews/apps/shared/appBase.ts index 2e07fb2..234a221 100644 --- a/src/webviews/apps/shared/appBase.ts +++ b/src/webviews/apps/shared/appBase.ts @@ -34,13 +34,15 @@ export abstract class App { private readonly _api: VsCodeApi; protected state: State; - constructor(protected readonly appName: string, state: State) { + constructor(protected readonly appName: string) { this.log(`${this.appName}.ctor`); + this.state = (window as any).bootstrap; + (window as any).bootstrap = undefined; + this._api = acquireVsCodeApi(); initializeAndWatchThemeColors(); - this.state = state; requestAnimationFrame(() => { this.log(`${this.appName}.initializing`); diff --git a/src/webviews/apps/shared/appWithConfigBase.ts b/src/webviews/apps/shared/appWithConfigBase.ts index a08bad0..84b873a 100644 --- a/src/webviews/apps/shared/appWithConfigBase.ts +++ b/src/webviews/apps/shared/appWithConfigBase.ts @@ -27,8 +27,8 @@ export abstract class AppWithConfig extends Ap private _changes = Object.create(null) as Record; private _updating: boolean = false; - constructor(appName: string, state: State) { - super(appName, state); + constructor(appName: string) { + super(appName); } protected override onInitialized() { diff --git a/src/webviews/apps/shared/dom.ts b/src/webviews/apps/shared/dom.ts index e3c41f3..9080b8b 100644 --- a/src/webviews/apps/shared/dom.ts +++ b/src/webviews/apps/shared/dom.ts @@ -69,4 +69,40 @@ export namespace DOM { }, }; } + + export function insertTemplate( + id: string, + $slot: HTMLDivElement, + options?: { bindings?: Record; visible?: Record }, + ): void { + const $template = (document.getElementById(id) as HTMLTemplateElement)?.content.cloneNode(true); + $slot.replaceChildren($template); + + if (options?.visible != null) { + const $els = $slot.querySelectorAll(`[data-visible]`); + for (const $el of $els) { + const key = $el.dataset.visible; + if (!key) continue; + + if (options.visible[key]) { + $el.style.display = 'initial'; + } else { + $el.style.display = 'none'; + } + } + } + + if (options?.bindings != null) { + const $els = $slot.querySelectorAll(`[data-bind]`); + for (const $el of $els) { + const key = $el.dataset.bind; + if (!key) continue; + + const value = options.bindings[key]; + if (value == null) continue; + + $el.textContent = String(value); + } + } + } } diff --git a/src/webviews/apps/welcome/welcome.ts b/src/webviews/apps/welcome/welcome.ts index 081283a..4ebd734 100644 --- a/src/webviews/apps/welcome/welcome.ts +++ b/src/webviews/apps/welcome/welcome.ts @@ -1,4 +1,4 @@ -/*global window*/ +/*global*/ import './welcome.scss'; import type { State } from '../../welcome/protocol'; import { AppWithConfig } from '../shared/appWithConfigBase'; @@ -6,8 +6,7 @@ import { AppWithConfig } from '../shared/appWithConfigBase'; export class WelcomeApp extends AppWithConfig { constructor() { - super('WelcomeApp', (window as any).bootstrap); - (window as any).bootstrap = undefined; + super('WelcomeApp'); } }