Refactor options page to use Typescript
Also, I've added and ran eslint, and fixed several minor issues accross
the Typescript codebase.
Bug: translateselectedtext:15
Change-Id: I8cfd67697f9bfb22f6de93b64fd750de66bab863
diff --git a/src/options/elements/credits-dialog/credits-dialog.ts b/src/options/elements/credits-dialog/credits-dialog.ts
new file mode 100644
index 0000000..e9201ee
--- /dev/null
+++ b/src/options/elements/credits-dialog/credits-dialog.ts
@@ -0,0 +1,139 @@
+import {css, html, LitElement} from 'lit';
+import {customElement} from 'lit/decorators.js';
+import {map} from 'lit/directives/map.js';
+import {unsafeHTML} from 'lit/directives/unsafe-html.js';
+
+import {msg} from '../../../common/i18n';
+import {DIALOG_STYLES} from '../../shared/dialog-styles';
+import {SHARED_STYLES} from '../../shared/shared-styles';
+import {credits, i18nCredits} from '../../tsCredits';
+
+@customElement('credits-dialog')
+export default class CreditsDialog extends LitElement {
+ static get styles() {
+ return [
+ SHARED_STYLES,
+ DIALOG_STYLES,
+ css`
+ dialog {
+ max-height: 430px;
+ width: 400px;
+ }
+
+ dialog[open] {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .content_area h4 {
+ margin-bottom: 0;
+ }
+
+ .entry {
+ position: relative;
+ }
+
+ .entry a.homepage {
+ position: absolute;
+ inset-inline-end: 16px;
+ font-size: 14px;
+ }
+
+ p,
+ span {
+ font-size: 14px;
+ }
+
+ p.author {
+ margin-top: 7px;
+ }
+
+ #translators .name {
+ font-weight: bold;
+ }
+
+ .createdby {
+ font-style: italic;
+ }
+ `,
+ ];
+ }
+
+ constructor() {
+ super();
+ this.addEventListener('show-credits-dialog', this.showDialog);
+ }
+
+ render() {
+ const translators = map(i18nCredits, (contributor) => {
+ const languagesArray =
+ contributor?.languages?.map?.((lang) => lang?.name ?? 'undefined');
+ const languages =
+ languagesArray.length > 0 ? ': ' + languagesArray.join(', ') : '';
+ return html`
+ <li><span class="name">${contributor?.name}</span>${languages}</li>
+ `;
+ });
+
+ const homepageMsg = msg('options_credits_homepage');
+ const creditsByMsg = msg('options_credits_by');
+
+ const otherCredits = map(credits, (c) => {
+ const url = c.url ?
+ html` <a href=${c?.url} target="_blank" class="homepage">
+ ${homepageMsg}
+ </a>` :
+ undefined;
+ const license = c.license ? ' - ' + c.license : '';
+ const author = c.author ?
+ html` <p class="author">${creditsByMsg} ${c.author}${license}</p> ` :
+ undefined;
+
+ return html`
+ <div class="entry">
+ ${url}
+ <h4>${c?.name}</h4>
+ ${author}
+ </div>
+ `;
+ });
+
+ return html`
+ <dialog>
+ <div class="scrollable">
+ <h3>${msg('options_credits')}</h3>
+ <div class="entry createdby">
+ <div>${unsafeHTML(msg('options_credits_createdby'))}</div>
+ </div>
+ <div class="entry">
+ <a href="https://gtranslate.avm99963.com/" class="homepage" target="_blank">
+ ${msg('options_credits_homepage')}
+ </a>
+ <h4>${msg('options_credits_translations')}</h4>
+ <div>${msg('options_credits_translations_paragraph')}</div>
+ <ul id="translators">
+ ${translators}
+ </ul>
+ </div>
+ <div class="content_area">${otherCredits}</div>
+ </div>
+ <div class="action_buttons">
+ <button id="credits_ok" @click="${this.closeDialog}">
+ ${msg('options_ok')}
+ </button>
+ </div>
+ </dialog>
+ `;
+ }
+
+ showDialog() {
+ const dialog = this.renderRoot.querySelector('dialog');
+ dialog.showModal();
+ dialog.querySelector('.scrollable').scrollTo(0, 0);
+ (dialog.querySelector('#credits_ok') as HTMLElement).focus();
+ }
+
+ closeDialog() {
+ this.renderRoot.querySelector('dialog').close();
+ }
+}