From fb3a5d26aa04d731275f24f3f160c5eafbf5b5ee Mon Sep 17 00:00:00 2001 From: Mehdi Benadel Date: Thu, 23 May 2024 15:52:12 +0200 Subject: [PATCH] Fixing ShadowDOM situation with CSS --- assets/styles/style.scss | 2 +- .../elements/channel-configuration.ts | 271 ++++++++++-------- .../configuration/elements/channel-home.ts | 13 +- .../elements/configuration-row.ts | 29 ++ .../elements/dynamic-table-form.ts | 39 ++- .../configuration/elements/help-button.ts | 11 +- .../elements/plugin-configuration-row.ts | 36 --- client/common/configuration/register.ts | 6 +- 8 files changed, 221 insertions(+), 186 deletions(-) create mode 100644 client/common/configuration/elements/configuration-row.ts delete mode 100644 client/common/configuration/elements/plugin-configuration-row.ts diff --git a/assets/styles/style.scss b/assets/styles/style.scss index 50b73f4e..00d0ca85 100644 --- a/assets/styles/style.scss +++ b/assets/styles/style.scss @@ -47,7 +47,7 @@ } .peertube-plugin-livechat-buttons-cloned { - // Hidding buttons when cloned by the ConverseJS mini-muc-head plugin. + // Hiding buttons when cloned by the ConverseJS mini-muc-head plugin. display: none; } diff --git a/client/common/configuration/elements/channel-configuration.ts b/client/common/configuration/elements/channel-configuration.ts index a0232251..e083a7a6 100644 --- a/client/common/configuration/elements/channel-configuration.ts +++ b/client/common/configuration/elements/channel-configuration.ts @@ -3,16 +3,15 @@ import { html, LitElement } from 'lit' import { customElement, property, state } from 'lit/decorators.js' import { ptTr } from '../directives/translation' import './dynamic-table-form' -import './plugin-configuration-row' +import './configuration-row' import './help-button' import { Task } from '@lit/task'; import type { ChannelConfiguration } from 'shared/lib/types' import { ChannelDetailsService } from '../services/channel-details' import { provide } from '@lit/context' -import { getGlobalStyleSheets } from '../../global-styles' import { channelConfigurationContext, channelDetailsServiceContext, registerClientOptionsContext } from '../contexts/channel' -@customElement('channel-configuration') +@customElement('livechat-channel-configuration') export class ChannelConfigurationElement extends LitElement { @provide({ context: registerClientOptionsContext }) @@ -29,16 +28,16 @@ export class ChannelConfigurationElement extends LitElement { @provide({ context: channelDetailsServiceContext }) private _channelDetailsService: ChannelDetailsService | undefined - static styles = [ - ...getGlobalStyleSheets() - ]; + protected createRenderRoot = (): HTMLElement | DocumentFragment => { + return this + } @state() public _formStatus: boolean | any = undefined private _asyncTaskRender = new Task(this, { - task: async ([registerClientOptions], {signal}) => { + task: async ([registerClientOptions], { signal }) => { if (this.registerClientOptions) { this._channelDetailsService = new ChannelDetailsService(this.registerClientOptions) this._channelConfiguration = await this._channelDetailsService.fetchConfiguration(this.channelId ?? 0) @@ -50,18 +49,18 @@ export class ChannelConfigurationElement extends LitElement { }); private _saveConfig = () => { - if(this._channelDetailsService && this._channelConfiguration) { + if (this._channelDetailsService && this._channelConfiguration) { this._channelDetailsService.saveOptions(this._channelConfiguration.channel.id, this._channelConfiguration.configuration) - .then((value) => { - this._formStatus = { success: true } - console.log(`Configuration has been updated`) - this.requestUpdate('_formStatus') - }) - .catch((error) => { - this._formStatus = error - console.log(`An error occurred : ${JSON.stringify(this._formStatus)}`) - this.requestUpdate('_formStatus') - }); + .then((value) => { + this._formStatus = { success: true } + console.log(`Configuration has been updated`) + this.requestUpdate('_formStatus') + }) + .catch((error) => { + this._formStatus = error + console.log(`An error occurred : ${JSON.stringify(this._formStatus)}`) + this.requestUpdate('_formStatus') + }); } } @@ -181,142 +180,178 @@ export class ChannelConfigurationElement extends LitElement { ${this._channelConfiguration?.channel.displayName} ${this._channelConfiguration?.channel.name} - - + +

${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_DESC)}

- -
- +
+
+ +
- - -
-
+
+
+ + +
+
+
+ +
+ ${this._channelConfiguration?.configuration.bot.enabled ? html`
- - { - if (event?.target && this._channelConfiguration) - this._channelConfiguration.configuration.bot.nickname = (event.target as HTMLInputElement).value - this.requestUpdate('_channelConfiguration') + + { + if (event?.target && this._channelConfiguration) + this._channelConfiguration.configuration.bot.nickname = (event.target as HTMLInputElement).value + this.requestUpdate('_channelConfiguration') + } } - } - value="${this._channelConfiguration?.configuration.bot.nickname}" - /> -
` + value="${this._channelConfiguration?.configuration.bot.nickname}" + /> +
` : '' } - +
+
${this._channelConfiguration?.configuration.bot.enabled ? - html` - { - if (this._channelConfiguration) this._channelConfiguration.configuration.bot.forbiddenWords = e.detail - this.requestUpdate('_channelConfiguration') + html`
+
+ + +
+
+ { + if (this._channelConfiguration) { + this._channelConfiguration.configuration.bot.forbiddenWords = e.detail + this.requestUpdate('_channelConfiguration') + } + } } - } - .formName=${'forbidden-words'}> - - - - + +
+
+
+
+ + +
+
+ { - if (this._channelConfiguration) this._channelConfiguration.configuration.bot.quotes = e.detail - this.requestUpdate('_channelConfiguration') + if (this._channelConfiguration) { + this._channelConfiguration.configuration.bot.quotes = e.detail + this.requestUpdate('_channelConfiguration') + } } } .formName=${'quote'}> - - - - +
+
+
+
+ + +
+
+ { - if (this._channelConfiguration) this._channelConfiguration.configuration.bot.commands = e.detail - this.requestUpdate('_channelConfiguration') + if (this._channelConfiguration) { + this._channelConfiguration.configuration.bot.commands = e.detail + this.requestUpdate('_channelConfiguration') + } } } .formName=${'command'}> - - ` + +
+
` : '' }
${(this._formStatus && this._formStatus.success === undefined) ? - html`` + html`` : '' } ${(this._formStatus && this._formStatus.success === true) ? - html`` + html`` : '' } diff --git a/client/common/configuration/elements/channel-home.ts b/client/common/configuration/elements/channel-home.ts index 415bf480..2b773c89 100644 --- a/client/common/configuration/elements/channel-home.ts +++ b/client/common/configuration/elements/channel-home.ts @@ -7,10 +7,9 @@ import { Task } from '@lit/task'; import type { ChannelLiveChatInfos } from 'shared/lib/types' import { ChannelDetailsService } from '../services/channel-details' import { provide } from '@lit/context' -import { getGlobalStyleSheets } from '../../global-styles' import { channelDetailsServiceContext, registerClientOptionsContext } from '../contexts/channel' -@customElement('channel-home') +@customElement('livechat-channel-home') export class ChannelHomeElement extends LitElement { @provide({ context: registerClientOptionsContext }) @@ -23,9 +22,9 @@ export class ChannelHomeElement extends LitElement { @provide({ context: channelDetailsServiceContext }) private _channelDetailsService: ChannelDetailsService | undefined - static styles = [ - ...getGlobalStyleSheets() - ]; + protected createRenderRoot = (): HTMLElement | DocumentFragment => { + return this + } @state() public _formStatus: boolean | any = undefined @@ -55,8 +54,8 @@ export class ChannelHomeElement extends LitElement {

${ptTr(LOC_LIVECHAT_CONFIGURATION_TITLE)} - - + +

${ptTr(LOC_LIVECHAT_CONFIGURATION_DESC)}

${ptTr(LOC_LIVECHAT_CONFIGURATION_PLEASE_SELECT)}

diff --git a/client/common/configuration/elements/configuration-row.ts b/client/common/configuration/elements/configuration-row.ts new file mode 100644 index 00000000..a2edca34 --- /dev/null +++ b/client/common/configuration/elements/configuration-row.ts @@ -0,0 +1,29 @@ +import { html, LitElement } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import './help-button' + +@customElement('livechat-configuration-row') +export class ConfigurationRowElement extends LitElement { + + @property({ attribute: false }) + public title: string = `title` + + @property({ attribute: false }) + public description: string = `Here's a description` + + @property({ attribute: false }) + public helpPage: string = 'documentation' + + protected createRenderRoot = (): HTMLElement | DocumentFragment => { + return this + } + + render() { + return html` +

${this.title}

+

${this.description}

+ + + ` + } +} diff --git a/client/common/configuration/elements/dynamic-table-form.ts b/client/common/configuration/elements/dynamic-table-form.ts index 55ba9a67..b7a9f0e8 100644 --- a/client/common/configuration/elements/dynamic-table-form.ts +++ b/client/common/configuration/elements/dynamic-table-form.ts @@ -2,7 +2,6 @@ import { css, html, LitElement, nothing, TemplateResult } from 'lit' import { repeat } from 'lit/directives/repeat.js' import { customElement, property, state } from 'lit/decorators.js' import { ifDefined } from 'lit/directives/if-defined.js' -import { getGlobalStyleSheets } from '../../global-styles' import { unsafeHTML } from 'lit/directives/unsafe-html.js' // This content comes from the file assets/images/plus-square.svg, from the Feather icons set https://feathericons.com/ @@ -57,7 +56,7 @@ interface CellDataSchema { default?: DynamicTableAcceptedTypes } -@customElement('dynamic-table-form') +@customElement('livechat-dynamic-table-form') export class DynamicTableFormElement extends LitElement { @property({ attribute: false }) @@ -82,37 +81,47 @@ export class DynamicTableFormElement extends LitElement { @property({ attribute: false }) private _colOrder: string[] = [] - static styles = [ - ...getGlobalStyleSheets(), - css` - :host table { + static styles = css` + table { table-layout: fixed; text-align: center; } - :host table td, table th { + table td, table th { word-wrap:break-word; vertical-align: top; padding: 5px 7px; } - :host table tbody > :nth-child(odd) { + table tbody > :nth-child(odd) { background-color: var(--greySecondaryBackgroundColor); } - :host button { + button { padding: 2px; } - :host .dynamic-table-add-row { + .dynamic-table-add-row { background-color: var(--bs-green); } - :host .dynamic-table-remove-row { + .dynamic-table-remove-row { background-color: var(--bs-orange); } - ` - ]; + `; + + protected createRenderRoot = (): HTMLElement | DocumentFragment => { + if (document.head.querySelector(`style[data-tagname="${this.tagName}"]`)) { + return this; + } + + const style = document.createElement("style"); + style.innerHTML = DynamicTableFormElement.styles.toString(); + style.setAttribute("data-tagname", this.tagName); + document.head.append(style); + + return this + } // fixes situations when list has been reinitialized or changed outside of CustomElement private _updateLastRowId = () => { @@ -197,7 +206,7 @@ export class DynamicTableFormElement extends LitElement { ${Object.entries(rowData.row).filter(([k, v]) => k != '_id') .sort(([k1,_1], [k2,_2]) => this._colOrder.indexOf(k1) - this._colOrder.indexOf(k2)) .map((data) => this.renderDataCell(data, rowData._id))} - + ` } @@ -206,7 +215,7 @@ export class DynamicTableFormElement extends LitElement { return html` ${Object.values(this.header).map(() => html``)} - + ` } diff --git a/client/common/configuration/elements/help-button.ts b/client/common/configuration/elements/help-button.ts index ce29dda5..cff00c6a 100644 --- a/client/common/configuration/elements/help-button.ts +++ b/client/common/configuration/elements/help-button.ts @@ -9,12 +9,11 @@ import { Task } from '@lit/task' import { localizedHelpUrl } from '../../../utils/help' import { ptTr } from '../directives/translation' import { DirectiveResult } from 'lit/directive' -import { getGlobalStyleSheets } from '../../global-styles' -@customElement('help-button') +@customElement('livechat-help-button') export class HelpButtonElement extends LitElement { - @consume({context: registerClientOptionsContext}) + @consume({ context: registerClientOptionsContext, subscribe: true }) public registerClientOptions: RegisterClientOptions | undefined @property({ attribute: false }) @@ -26,9 +25,9 @@ export class HelpButtonElement extends LitElement { @state() public url: URL = new URL('https://lmddgtfy.net/') - static styles = [ - ...getGlobalStyleSheets() - ]; + protected createRenderRoot = (): HTMLElement | DocumentFragment => { + return this + } private _asyncTaskRender = new Task(this, { task: async ([registerClientOptions], {signal}) => { diff --git a/client/common/configuration/elements/plugin-configuration-row.ts b/client/common/configuration/elements/plugin-configuration-row.ts deleted file mode 100644 index 74ed56f6..00000000 --- a/client/common/configuration/elements/plugin-configuration-row.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { html, LitElement } from 'lit' -import { customElement, property } from 'lit/decorators.js' -import './help-button' -import { getGlobalStyleSheets } from '../../global-styles' - -@customElement('plugin-configuration-row') -export class PluginConfigurationRowElement extends LitElement { - - @property({ attribute: false }) - public title: string = `title` - - @property({ attribute: false }) - public description: string = `Here's a description` - - @property({ attribute: false }) - public helpPage: string = 'documentation' - - static styles = [ - ...getGlobalStyleSheets() - ]; - - render() { - return html` -
-
-

${this.title}

-

${this.description}

- - -
-
-

Nothing in this row.

-
-
` - } -} diff --git a/client/common/configuration/register.ts b/client/common/configuration/register.ts index 7435de96..ae5bd36a 100644 --- a/client/common/configuration/register.ts +++ b/client/common/configuration/register.ts @@ -20,7 +20,7 @@ async function registerConfiguration (clientOptions: RegisterClientOptions): Pro registerClientRoute({ route: 'livechat/configuration', onMount: async ({ rootEl }) => { - render(html``, rootEl) + render(html``, rootEl) } }) @@ -29,8 +29,8 @@ async function registerConfiguration (clientOptions: RegisterClientOptions): Pro onMount: async ({ rootEl }) => { const urlParams = new URLSearchParams(window.location.search) const channelId = urlParams.get('channelId') ?? '' - render(html``, rootEl) + render(html``, rootEl) } })