From 9a125e3111154c8077237500f609adc30d0bfdc2 Mon Sep 17 00:00:00 2001 From: Danny Morabito Date: Tue, 25 Mar 2025 19:00:41 +0100 Subject: [PATCH] fix repeat events bug with input and textarea --- src/components/DateTimeSettings.ts | 11 ++--- src/components/General/Input.ts | 68 +++++++++++++----------------- src/components/General/Prompt.ts | 8 ++-- src/components/General/Textarea.ts | 9 +--- src/components/Header.ts | 54 +++++++++++++----------- src/components/InitialSetup.ts | 29 +++++++------ src/routes/Arbor/NewPost.ts | 7 +-- src/routes/Arbor/NewTopic.ts | 15 ++++--- src/routes/Settings.ts | 22 +++++----- 9 files changed, 109 insertions(+), 114 deletions(-) diff --git a/src/components/DateTimeSettings.ts b/src/components/DateTimeSettings.ts index 2bebba0..0910025 100644 --- a/src/components/DateTimeSettings.ts +++ b/src/components/DateTimeSettings.ts @@ -1,9 +1,10 @@ import { StyledToggle } from '@/components/General/Toggle'; +import { ArxInputChangeEvent, type StyledInput } from '@components/General/Input'; import { LitElement, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import '@components/General/Input'; import '@components/General/Fieldset'; +import '@components/General/Input'; import '@components/General/Select'; interface DateTimeFormatOptions { @@ -71,10 +72,10 @@ export class DateTimeSettings extends LitElement { } private handleChange(key: keyof DateTimeFormatOptions, e: Event) { - const target = e.target as HTMLSelectElement | HTMLInputElement | StyledToggle; - let value: string | boolean | undefined = target.value; + let value = e instanceof ArxInputChangeEvent ? e.detail.value : (e.target as HTMLSelectElement).value; + const target = e.target as StyledInput | HTMLSelectElement | HTMLInputElement | StyledToggle; - if (key === 'hour12' && target instanceof StyledToggle) value = target.checked; + if (key === 'hour12' && target instanceof StyledToggle) value = target.checked.toString(); this.options = { ...this.options, @@ -126,7 +127,7 @@ export class DateTimeSettings extends LitElement { label="Locale" type="text" .value=${this.options.locale} - @input=${(e: Event) => this.handleChange('locale', e)} + @change=${(e: Event) => this.handleChange('locale', e)} > diff --git a/src/components/General/Input.ts b/src/components/General/Input.ts index 2936518..3b20e82 100644 --- a/src/components/General/Input.ts +++ b/src/components/General/Input.ts @@ -1,17 +1,35 @@ -import { LitElement, css, html } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; +import { LitElement, type PropertyValues, css, html } from 'lit'; +import { customElement, property, query, state } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; +export class ArxInputChangeEvent extends CustomEvent<{ value: string }> { + constructor(value: string) { + super('change', { detail: { value } }); + } +} + @customElement('arx-input') export class StyledInput extends LitElement { @property() placeholder = ''; @property() value = ''; @property({ type: Boolean }) disabled = false; - @property() type = 'text'; + @property() type: 'text' | 'number' | 'password' = 'text'; @property() name = ''; @property({ type: Boolean }) required = false; @property() label = ''; + @query('input') private _input!: HTMLInputElement; + + @state() private _value = ''; + + protected override firstUpdated(_changedProperties: PropertyValues): void { + this._value = this.value; + } + + protected override updated(changedProperties: PropertyValues): void { + if (changedProperties.has('value')) this._value = this.value; + } + static override styles = css` :host { display: inline-block; @@ -98,7 +116,7 @@ export class StyledInput extends LitElement { return html` ${when(this.label, () => html``)} ` diff --git a/src/components/General/Textarea.ts b/src/components/General/Textarea.ts index 93e2791..e799a8f 100644 --- a/src/components/General/Textarea.ts +++ b/src/components/General/Textarea.ts @@ -1,6 +1,7 @@ import { LitElement, css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; +import { ArxInputChangeEvent } from './Input'; @customElement('arx-textarea') export class StyledTextarea extends LitElement { @@ -201,12 +202,6 @@ export class StyledTextarea extends LitElement { private _handleInput(e: InputEvent) { const textarea = e.target as HTMLTextAreaElement; this.value = textarea.value; - this.dispatchEvent( - new CustomEvent('input', { - detail: { value: this.value }, - bubbles: true, - composed: true, - }), - ); + this.dispatchEvent(new ArxInputChangeEvent(this.value)); } } diff --git a/src/components/Header.ts b/src/components/Header.ts index 4834ab3..e8745c9 100644 --- a/src/components/Header.ts +++ b/src/components/Header.ts @@ -1,4 +1,5 @@ import { ndk } from '@/ndk'; +import type { ArxInputChangeEvent } from '@components/General/Input'; import type { NDKEvent } from '@nostr-dev-kit/ndk'; import * as nip19 from '@nostr/tools/nip19'; import { LitElement, css, html } from 'lit'; @@ -8,8 +9,8 @@ import { keyed } from 'lit/directives/keyed.js'; import { map } from 'lit/directives/map.js'; import { when } from 'lit/directives/when.js'; -import '@components/HeaderSugestion'; import '@components/General/Input'; +import '@components/HeaderSugestion'; @customElement('arx-header') export class Header extends LitElement { @@ -49,10 +50,10 @@ export class Header extends LitElement { .nav-buttons { display: flex; gap: var(--space-xs, 0.5rem); - padding-right: var(--space-xs, 0.5rem); + padding: 0 var(--space-xs, 0.5rem); } - .nav-buttons button { + button { text-decoration: none; color: var(--color-primary-content); background: oklch(from var(--color-primary-content) l c h / 0.1); @@ -66,23 +67,21 @@ export class Header extends LitElement { align-items: center; justify-content: center; transition: all 0.2s ease; - } + &:hover { + background: oklch(from var(--color-primary-content) l c h / 0.2); + transform: translateY(-2px); + box-shadow: calc(var(--depth) * 2px) calc(var(--depth) * 2px) + calc(var(--depth) * 4px) + oklch(from var(--color-base-content) l c h / 0.15); + } + &:active { + transform: translateY(1px); + } - .nav-buttons button:hover { - background: oklch(from var(--color-primary-content) l c h / 0.2); - transform: translateY(-2px); - box-shadow: calc(var(--depth) * 2px) calc(var(--depth) * 2px) - calc(var(--depth) * 4px) - oklch(from var(--color-base-content) l c h / 0.15); - } - - .nav-buttons button:active { - transform: translateY(1px); - } - - .nav-buttons button.disabled { - opacity: 0.5; - pointer-events: none; + &.disabled { + opacity: 0.5; + pointer-events: none; + } } .search-container { @@ -153,7 +152,7 @@ export class Header extends LitElement { placeholder=${this.url} @keyup=${this._handleSearch} @focus=${this._handleFocus} - @input=${this._handleInput} + @change=${this._handleInput} > ${when( this.showSuggestions, @@ -175,16 +174,25 @@ export class Header extends LitElement { `, )} + `; } + private _goToWallet() { + window.location.hash = 'wallet'; + } + private _handleFocus() { this.showSuggestions = true; } - private _handleInput(e: InputEvent) { - this.searchQuery = (e.target as HTMLInputElement).value; + private _handleInput(e: ArxInputChangeEvent) { + this.searchQuery = e.detail.value; if (this._debounceTimeout) { clearTimeout(this._debounceTimeout); } @@ -245,8 +253,6 @@ export class Header extends LitElement { private _handleSearch(e: KeyboardEvent) { if (e.key !== 'Enter') return; - const target = e.target as HTMLInputElement; - this.searchQuery = target.value; this.showSuggestions = false; if (this.searchQuery.startsWith('npub1')) { diff --git a/src/components/InitialSetup.ts b/src/components/InitialSetup.ts index a947959..06210c9 100644 --- a/src/components/InitialSetup.ts +++ b/src/components/InitialSetup.ts @@ -1,4 +1,5 @@ import { ndk, setSigner } from '@/ndk'; +import type { ArxInputChangeEvent } from '@components/General/Input'; import { animate } from '@lit-labs/motion'; import { randomBytes } from '@noble/ciphers/webcrypto'; import { NDKEvent, NDKKind, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; @@ -10,10 +11,10 @@ import { encodeBase64 } from '@std/encoding/base64'; import { LitElement, css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import '@components/LoadingView'; import '@components/General/Button'; -import '@components/General/Input'; import '@components/General/Fieldset'; +import '@components/General/Input'; +import '@components/LoadingView'; @customElement('arx-initial-setup') export class InitialSetup extends LitElement { @@ -201,8 +202,8 @@ export class InitialSetup extends LitElement { }, 300); } - private onSeedPhraseInput(event: Event) { - this.seedPhrase = (event.target as HTMLInputElement).value; + private onSeedPhraseInput(event: ArxInputChangeEvent) { + this.seedPhrase = event.detail.value; } private generateSeedPhrase() { @@ -300,7 +301,7 @@ export class InitialSetup extends LitElement {

) { + this.userName = e.detail.value; } - private onProfileImageInput(e: Event) { - this.profileImage = (e.target as HTMLInputElement).value; + private onProfileImageInput(e: CustomEvent<{ value: string }>) { + this.profileImage = e.detail.value; } - private onLightningAddressInput(e: Event) { - this.lightningAddress = (e.target as HTMLInputElement).value; + private onLightningAddressInput(e: CustomEvent<{ value: string }>) { + this.lightningAddress = e.detail.value; } private renderPageFour() { @@ -409,7 +410,7 @@ export class InitialSetup extends LitElement { id="username" type="text" .value=${this.userName} - @input=${this.onUserNameInput} + @change=${this.onUserNameInput} placeholder="Enter your name" > @@ -418,7 +419,7 @@ export class InitialSetup extends LitElement { id="profile-image" type="text" .value=${this.profileImage} - @input=${this.onProfileImageInput} + @change=${this.onProfileImageInput} placeholder="Enter image URL" > @@ -464,7 +465,7 @@ export class InitialSetup extends LitElement { id="lightning-address" type="text" .value=${this.lightningAddress} - @input=${this.onLightningAddressInput} + @change=${this.onLightningAddressInput} placeholder="your@lightning.address" /> diff --git a/src/routes/Arbor/NewPost.ts b/src/routes/Arbor/NewPost.ts index 8a3f8b5..ab2a6c3 100644 --- a/src/routes/Arbor/NewPost.ts +++ b/src/routes/Arbor/NewPost.ts @@ -1,3 +1,4 @@ +import type { ArxInputChangeEvent } from '@/components/General/Input'; import { getSigner, ndk } from '@/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk'; import { LitElement, css, html } from 'lit'; @@ -106,8 +107,8 @@ export class ArborPostCreator extends LitElement { } } - private handleContentInput(e: InputEvent) { - this.postContent = (e.target as HTMLTextAreaElement).value; + private handleContentInput(e: ArxInputChangeEvent) { + this.postContent = e.detail.value; if (this.error && this.postContent.length >= 10) { this.error = null; } @@ -121,7 +122,7 @@ export class ArborPostCreator extends LitElement { diff --git a/src/routes/Arbor/NewTopic.ts b/src/routes/Arbor/NewTopic.ts index 23f3403..7228bee 100644 --- a/src/routes/Arbor/NewTopic.ts +++ b/src/routes/Arbor/NewTopic.ts @@ -1,11 +1,12 @@ import { getSigner, ndk } from '@/ndk'; +import type { ArxInputChangeEvent } from '@components/General/Input'; import { NDKEvent } from '@nostr-dev-kit/ndk'; import { LitElement, css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; +import '@components/General/Button'; import '@components/General/Input'; import '@components/General/Textarea'; -import '@components/General/Button'; @customElement('arx-arbor-topic-creator') export class ArborTopicCreator extends LitElement { @@ -86,12 +87,12 @@ export class ArborTopicCreator extends LitElement { } } - private handleTopicInput(e: InputEvent) { - this.newTopic = (e.target as HTMLInputElement).value; + private handleTopicInput(e: ArxInputChangeEvent) { + this.newTopic = e.detail.value; } - private handleContentInput(e: InputEvent) { - this.topicContent = (e.target as HTMLTextAreaElement).value; + private handleContentInput(e: ArxInputChangeEvent) { + this.topicContent = e.detail.value; } override render() { @@ -102,14 +103,14 @@ export class ArborTopicCreator extends LitElement { type="text" placeholder="New Topic" .value=${this.newTopic} - @input=${this.handleTopicInput} + @change=${this.handleTopicInput} ?disabled=${this.isCreating} > diff --git a/src/routes/Settings.ts b/src/routes/Settings.ts index cdcb210..6e3ddf1 100644 --- a/src/routes/Settings.ts +++ b/src/routes/Settings.ts @@ -1,16 +1,17 @@ import defaultAvatar from '@/default-avatar.png'; import { getSigner, getUserProfile, ndk } from '@/ndk'; +import type { ArxInputChangeEvent } from '@components/General/Input'; import { NDKEvent, type NDKUserProfile } from '@nostr-dev-kit/ndk'; import { LitElement, css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; -import '@components/DateTimeSettings'; -import '@components/General/Input'; -import '@components/General/Button'; -import '@components/General/Fieldset'; -import '@components/General/Card'; import '@components/Breadcrumbs'; +import '@components/DateTimeSettings'; +import '@components/General/Button'; +import '@components/General/Card'; +import '@components/General/Fieldset'; +import '@components/General/Input'; @customElement('arx-settings') export class EveSettings extends LitElement { @@ -73,11 +74,10 @@ export class EveSettings extends LitElement { } } - private handleInputChange(e: Event) { - const target = e.target as HTMLInputElement; + private handleInputChange(field: string, e: ArxInputChangeEvent) { this.profile = { ...this.profile, - [target.name]: target.value, + [field]: e.detail.value, }; } @@ -144,7 +144,7 @@ export class EveSettings extends LitElement { type="text" name="name" .value=${this.profile.name} - @input=${this.handleInputChange} + @change=${(e: ArxInputChangeEvent) => this.handleInputChange('name', e)} placeholder="Your display name" > @@ -153,7 +153,7 @@ export class EveSettings extends LitElement { type="text" name="image" .value=${this.profile.picture} - @input=${this.handleInputChange} + @change=${(e: ArxInputChangeEvent) => this.handleInputChange('picture', e)} placeholder="https://example.com/your-image.jpg" > @@ -162,7 +162,7 @@ export class EveSettings extends LitElement { type="text" name="banner" .value=${this.profile.banner} - @input=${this.handleInputChange} + @change=${(e: ArxInputChangeEvent) => this.handleInputChange('banner', e)} placeholder="https://example.com/your-image.jpg" >