From 399d9cb4fba224e10f5412a755a68e49e2222da6 Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Tue, 23 Dec 2025 14:40:48 +0100 Subject: [PATCH 1/9] Never preventDefault on beforeInput This is to stop Chrome from thinking the user never inputed any thing when autofilling. Otherwise Chrome will keep autofilling. --- src/components.d.ts | 13 ++++++ src/components/input/InputStateHandler.ts | 55 ++++++++++++++--------- src/components/input/demo/index.tsx | 1 + src/components/input/demo/types/index.tsx | 35 +++++++++++++++ src/components/input/demo/types/style.css | 9 ++++ src/components/input/index.tsx | 11 +++-- 6 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 src/components/input/demo/types/index.tsx create mode 100644 src/components/input/demo/types/style.css diff --git a/src/components.d.ts b/src/components.d.ts index 7e77e5078..8ad6a2693 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -417,6 +417,8 @@ export namespace Components { } interface SmoothlyInputDemoStandard { } + interface SmoothlyInputDemoTypes { + } interface SmoothlyInputDemoUserInput { } interface SmoothlyInputEdit { @@ -1607,6 +1609,12 @@ declare global { prototype: HTMLSmoothlyInputDemoStandardElement; new (): HTMLSmoothlyInputDemoStandardElement; }; + interface HTMLSmoothlyInputDemoTypesElement extends Components.SmoothlyInputDemoTypes, HTMLStencilElement { + } + var HTMLSmoothlyInputDemoTypesElement: { + prototype: HTMLSmoothlyInputDemoTypesElement; + new (): HTMLSmoothlyInputDemoTypesElement; + }; interface HTMLSmoothlyInputDemoUserInputElement extends Components.SmoothlyInputDemoUserInput, HTMLStencilElement { } var HTMLSmoothlyInputDemoUserInputElement: { @@ -2268,6 +2276,7 @@ declare global { "smoothly-input-date-time": HTMLSmoothlyInputDateTimeElement; "smoothly-input-demo": HTMLSmoothlyInputDemoElement; "smoothly-input-demo-standard": HTMLSmoothlyInputDemoStandardElement; + "smoothly-input-demo-types": HTMLSmoothlyInputDemoTypesElement; "smoothly-input-demo-user-input": HTMLSmoothlyInputDemoUserInputElement; "smoothly-input-edit": HTMLSmoothlyInputEditElement; "smoothly-input-file": HTMLSmoothlyInputFileElement; @@ -2707,6 +2716,8 @@ declare namespace LocalJSX { } interface SmoothlyInputDemoStandard { } + interface SmoothlyInputDemoTypes { + } interface SmoothlyInputDemoUserInput { } interface SmoothlyInputEdit { @@ -3087,6 +3098,7 @@ declare namespace LocalJSX { "smoothly-input-date-time": SmoothlyInputDateTime; "smoothly-input-demo": SmoothlyInputDemo; "smoothly-input-demo-standard": SmoothlyInputDemoStandard; + "smoothly-input-demo-types": SmoothlyInputDemoTypes; "smoothly-input-demo-user-input": SmoothlyInputDemoUserInput; "smoothly-input-edit": SmoothlyInputEdit; "smoothly-input-file": SmoothlyInputFile; @@ -3203,6 +3215,7 @@ declare module "@stencil/core" { "smoothly-input-date-time": LocalJSX.SmoothlyInputDateTime & JSXBase.HTMLAttributes; "smoothly-input-demo": LocalJSX.SmoothlyInputDemo & JSXBase.HTMLAttributes; "smoothly-input-demo-standard": LocalJSX.SmoothlyInputDemoStandard & JSXBase.HTMLAttributes; + "smoothly-input-demo-types": LocalJSX.SmoothlyInputDemoTypes & JSXBase.HTMLAttributes; "smoothly-input-demo-user-input": LocalJSX.SmoothlyInputDemoUserInput & JSXBase.HTMLAttributes; "smoothly-input-edit": LocalJSX.SmoothlyInputEdit & JSXBase.HTMLAttributes; "smoothly-input-file": LocalJSX.SmoothlyInputFile & JSXBase.HTMLAttributes; diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index 406d13aed..71d9d08bf 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -80,55 +80,71 @@ export class InputStateHandler { return result } - public onInputEvent(event: InputEvent, state: tidily.State): Readonly & tidily.Settings { + private nextFormattedState?: Readonly & Readonly // Set in beforeinput event - used in input event + public onBeforeInputEvent(event: InputEvent, state: tidily.State): Readonly & tidily.Settings { const input = event.target as HTMLInputElement state.selection.start = input.selectionStart ?? state.selection.start state.selection.end = input.selectionEnd ?? state.selection.end state.selection.direction = input.selectionDirection ?? state.selection.direction - const result = - event.type == "beforeinput" || event.type == "input" - ? this.eventHandlers[event.type][event.inputType]?.(event, this.unformatState(state), state) ?? state - : state + + const result = this.eventHandlers.beforeinput[event.inputType]?.(event, this.unformatState(state), state) ?? state const formatted = this.partialFormatState(result) - if (event.defaultPrevented) { - input.value = formatted.value - input.selectionStart = formatted.selection.start - input.selectionEnd = formatted.selection.end - input.selectionDirection = formatted.selection.direction ?? null - } + this.nextFormattedState = formatted + console.log(event.type, this.nextFormattedState) return formatted } + public onInputEvent(event: InputEvent, state: tidily.State): Readonly & tidily.Settings { + const input = event.target as HTMLInputElement + if (this.nextFormattedState) { + input.value = this.nextFormattedState.value + input.selectionStart = this.nextFormattedState.selection.start + input.selectionEnd = this.nextFormattedState.selection.end + input.selectionDirection = this.nextFormattedState.selection.direction ?? null + return this.nextFormattedState + } else { + // state.selection.start = input.selectionStart ?? state.selection.start + // state.selection.end = input.selectionEnd ?? state.selection.end + // state.selection.direction = input.selectionDirection ?? state.selection.direction + + const result = this.eventHandlers.input[event.inputType]?.(event, this.unformatState(state), state) ?? state + + const formatted = this.partialFormatState(result) + if (event.defaultPrevented) { + input.value = formatted.value + input.selectionStart = formatted.selection.start + input.selectionEnd = formatted.selection.end + input.selectionDirection = formatted.selection.direction ?? null + } + return formatted + } + } private eventHandlers: Record<"beforeinput" | "input", { [inputType: string]: Handler | undefined }> = { beforeinput: { insertText: (event, state) => this.insert(event, state), insertFromPaste: (event, state) => this.insert(event, state), insertFromDrop: (event, state) => this.insert(event, state), - deleteContentBackward: (event, state) => { - event.preventDefault() + deleteContentBackward: (_, state) => { if (state.selection.start == state.selection.end) this.select(state, state.selection.start - 1, state.selection.end) this.erase(state) return state }, - deleteContentForward: (event, state) => { - event.preventDefault() + deleteContentForward: (_, state) => { if (state.selection.start == state.selection.end) this.select(state, state.selection.start, state.selection.end + 1) this.erase(state) return state }, - deleteWordBackward: (event, unformatted, formattedState) => { + deleteWordBackward: (_, unformatted, formattedState) => { let result = unformatted if (this.type != "password") { - event.preventDefault() result = this.deleteWord(formattedState, "backward") } return result }, - deleteWordForward: (event, unformatted, formattedState) => { + deleteWordForward: (_, unformatted, formattedState) => { let result = unformatted if (this.type != "password") { - event.preventDefault() result = this.deleteWord(formattedState, "forward") } return result @@ -154,7 +170,6 @@ export class InputStateHandler { } private insert(event: InputEvent, unformatted: tidily.State): tidily.State { - event.preventDefault() if (typeof event.data == "string") for (const c of event.data) this.formatter.allowed(c, unformatted) && this.replace(unformatted, c.replace(/(\r|\n|\t)/g, "")) diff --git a/src/components/input/demo/index.tsx b/src/components/input/demo/index.tsx index 173b8cb14..da3d1ab47 100644 --- a/src/components/input/demo/index.tsx +++ b/src/components/input/demo/index.tsx @@ -18,6 +18,7 @@ export class SmoothlyInputDemo { +

Calendar

Calendar diff --git a/src/components/input/demo/types/index.tsx b/src/components/input/demo/types/index.tsx new file mode 100644 index 000000000..f823fb9dc --- /dev/null +++ b/src/components/input/demo/types/index.tsx @@ -0,0 +1,35 @@ +import { Component, h, Host } from "@stencil/core" +import { tidily } from "tidily" + +@Component({ + tag: "smoothly-input-demo-types", + styleUrl: "style.css", + scoped: true, +}) +export class SmoothlyInputDemoTypes { + types: tidily.Type[] = [ + "text", + "integer", + "price", + "percent", + "password", + "email", + "card-number", + "card-expires", + "card-csc", + ] as const + + render() { + return ( + +

Input Types

+ {this.types.map(type => ( + + {type} + + + ))} +
+ ) + } +} diff --git a/src/components/input/demo/types/style.css b/src/components/input/demo/types/style.css new file mode 100644 index 000000000..3ca219e18 --- /dev/null +++ b/src/components/input/demo/types/style.css @@ -0,0 +1,9 @@ +:host { + display: block; + max-width: min(calc(100% - 0.5rem), 48rem); + margin: auto; +} +:host smoothly-input { + width: 100%; +} + diff --git a/src/components/input/index.tsx b/src/components/input/index.tsx index b863b3500..2ee48fab2 100644 --- a/src/components/input/index.tsx +++ b/src/components/input/index.tsx @@ -178,12 +178,15 @@ export class SmoothlyInput implements Clearable, Input, Editable { if (!this.element.isConnected) await this.unregister() } - @Listen("input") @Listen("beforeinput") - onEvent(event: InputEvent) { + onBeforeInput(event: InputEvent) { + this.stateHandler.onBeforeInputEvent(event, this.state) + } + @Listen("input") + onInput(event: InputEvent) { this.state = this.stateHandler.onInputEvent(event, this.state) - if (event.type == "input" || event.defaultPrevented) - this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) + this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) + console.log("updating state!!", this.state) } copyText(value?: string) { if (value) { From fb253658b5c747d664803eef157616834468b4fd Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Tue, 23 Dec 2025 15:52:10 +0100 Subject: [PATCH 2/9] Seemingly working with google chrome autofill --- src/components/input/InputStateHandler.ts | 11 ++++++----- src/components/input/index.tsx | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index 71d9d08bf..51e291b54 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -81,17 +81,16 @@ export class InputStateHandler { } private nextFormattedState?: Readonly & Readonly // Set in beforeinput event - used in input event - public onBeforeInputEvent(event: InputEvent, state: tidily.State): Readonly & tidily.Settings { + public onBeforeInputEvent(event: InputEvent, state: tidily.State) { const input = event.target as HTMLInputElement state.selection.start = input.selectionStart ?? state.selection.start state.selection.end = input.selectionEnd ?? state.selection.end state.selection.direction = input.selectionDirection ?? state.selection.direction - const result = this.eventHandlers.beforeinput[event.inputType]?.(event, this.unformatState(state), state) ?? state - const formatted = this.partialFormatState(result) + const result = this.eventHandlers.beforeinput[event.inputType]?.(event, this.unformatState(state), state) + const formatted = result ? this.partialFormatState(result) : undefined this.nextFormattedState = formatted - console.log(event.type, this.nextFormattedState) - return formatted + console.log(event.type, event.inputType, event, this.nextFormattedState) } public onInputEvent(event: InputEvent, state: tidily.State): Readonly & tidily.Settings { const input = event.target as HTMLInputElement @@ -100,6 +99,7 @@ export class InputStateHandler { input.selectionStart = this.nextFormattedState.selection.start input.selectionEnd = this.nextFormattedState.selection.end input.selectionDirection = this.nextFormattedState.selection.direction ?? null + console.log(event.type, event.inputType, "using nextFormattedState", event, this.nextFormattedState) return this.nextFormattedState } else { // state.selection.start = input.selectionStart ?? state.selection.start @@ -115,6 +115,7 @@ export class InputStateHandler { input.selectionEnd = formatted.selection.end input.selectionDirection = formatted.selection.direction ?? null } + console.log(event.type, "new Event", event, formatted) return formatted } } diff --git a/src/components/input/index.tsx b/src/components/input/index.tsx index 2ee48fab2..261552ac2 100644 --- a/src/components/input/index.tsx +++ b/src/components/input/index.tsx @@ -186,7 +186,7 @@ export class SmoothlyInput implements Clearable, Input, Editable { onInput(event: InputEvent) { this.state = this.stateHandler.onInputEvent(event, this.state) this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) - console.log("updating state!!", this.state) + // console.log("updating state!!", this.state) } copyText(value?: string) { if (value) { From 7362e4dbcc068a4175922ed1123163b61f3b77d6 Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Tue, 23 Dec 2025 16:10:34 +0100 Subject: [PATCH 3/9] demo looks=border --- src/components/input/demo/types/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/input/demo/types/index.tsx b/src/components/input/demo/types/index.tsx index f823fb9dc..800b049cf 100644 --- a/src/components/input/demo/types/index.tsx +++ b/src/components/input/demo/types/index.tsx @@ -24,7 +24,7 @@ export class SmoothlyInputDemoTypes {

Input Types

{this.types.map(type => ( - + {type} From 82a633454899e5a876933be3320939b71ca73434 Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Tue, 23 Dec 2025 16:16:37 +0100 Subject: [PATCH 4/9] remove nextFormattedState --- src/components/input/InputStateHandler.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index 51e291b54..de419f041 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -95,12 +95,14 @@ export class InputStateHandler { public onInputEvent(event: InputEvent, state: tidily.State): Readonly & tidily.Settings { const input = event.target as HTMLInputElement if (this.nextFormattedState) { - input.value = this.nextFormattedState.value - input.selectionStart = this.nextFormattedState.selection.start - input.selectionEnd = this.nextFormattedState.selection.end - input.selectionDirection = this.nextFormattedState.selection.direction ?? null - console.log(event.type, event.inputType, "using nextFormattedState", event, this.nextFormattedState) - return this.nextFormattedState + const result = this.nextFormattedState + this.nextFormattedState = undefined + input.value = result.value + input.selectionStart = result.selection.start + input.selectionEnd = result.selection.end + input.selectionDirection = result.selection.direction ?? null + console.log(event.type, event.inputType, "using nextFormattedState", event, result) + return result } else { // state.selection.start = input.selectionStart ?? state.selection.start // state.selection.end = input.selectionEnd ?? state.selection.end From 632f8920079341dbd3878bf1b1e2abbd7262cf50 Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Mon, 29 Dec 2025 10:21:27 +0100 Subject: [PATCH 5/9] only update input if different value --- src/components/input/InputStateHandler.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index de419f041..364dba356 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -111,13 +111,18 @@ export class InputStateHandler { const result = this.eventHandlers.input[event.inputType]?.(event, this.unformatState(state), state) ?? state const formatted = this.partialFormatState(result) - if (event.defaultPrevented) { + if ( + input.value != formatted.value || + input.selectionStart != formatted.selection.start || + input.selectionEnd != formatted.selection.end + ) { input.value = formatted.value input.selectionStart = formatted.selection.start input.selectionEnd = formatted.selection.end input.selectionDirection = formatted.selection.direction ?? null } - console.log(event.type, "new Event", event, formatted) + + console.log(event.type, event.inputType, "new Event", event, formatted) return formatted } } From e5cec383c761b1ed66e9bffc6f5f70370659ffec Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Mon, 29 Dec 2025 15:55:02 +0100 Subject: [PATCH 6/9] sort of works, but it seems super duper hacky --- src/components/input/InputStateHandler.ts | 44 +++++++++++++++-------- src/components/input/index.tsx | 11 +++--- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index 364dba356..1faa8db16 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -4,6 +4,7 @@ import { Adjacent } from "./Adjacent" type Formatter = tidily.Formatter & tidily.Converter type Handler = (event: E, unformatted: tidily.State, formatted: tidily.State) => tidily.State +type CommitState = (state: Readonly & tidily.Settings) => void export class InputStateHandler { constructor(private formatter: Formatter, private type: tidily.Type) {} @@ -79,9 +80,8 @@ export class InputStateHandler { input.value = result.value return result } - private nextFormattedState?: Readonly & Readonly // Set in beforeinput event - used in input event - public onBeforeInputEvent(event: InputEvent, state: tidily.State) { + public onBeforeInputEvent(event: InputEvent, state: tidily.State): void { const input = event.target as HTMLInputElement state.selection.start = input.selectionStart ?? state.selection.start state.selection.end = input.selectionEnd ?? state.selection.end @@ -92,24 +92,41 @@ export class InputStateHandler { this.nextFormattedState = formatted console.log(event.type, event.inputType, event, this.nextFormattedState) } - public onInputEvent(event: InputEvent, state: tidily.State): Readonly & tidily.Settings { + private requestAnimationFrameId: number | undefined + private timeoutId: NodeJS.Timeout | undefined + public onInputEvent(event: InputEvent, state: tidily.State, commitState: CommitState): void { const input = event.target as HTMLInputElement - if (this.nextFormattedState) { + if (!event.inputType) { + this.requestAnimationFrameId && cancelAnimationFrame(this.requestAnimationFrameId) + clearTimeout(this.timeoutId) + const valueBeforeRAF = input.value + + this.requestAnimationFrameId = requestAnimationFrame(() => { + // this.timeoutId = setTimeout(() => { + const newValue = state.value != input.value ? input.value : valueBeforeRAF + console.log("autofill?", { state: state.value, inputValue: input.value, valueBeforeRAF, newValue }) + if (state.value != newValue) { + const result = { ...state, value: newValue } + const formatted = this.partialFormatState(result) + commitState(formatted) + console.log(event.type, event.inputType, "autofill", event, result) + } + // }, 100) + }) + } else if (this.nextFormattedState) { const result = this.nextFormattedState this.nextFormattedState = undefined + console.log(event.type, event.inputType, "using nextFormattedState", event, result) input.value = result.value input.selectionStart = result.selection.start input.selectionEnd = result.selection.end input.selectionDirection = result.selection.direction ?? null - console.log(event.type, event.inputType, "using nextFormattedState", event, result) - return result + commitState(result) } else { - // state.selection.start = input.selectionStart ?? state.selection.start - // state.selection.end = input.selectionEnd ?? state.selection.end - // state.selection.direction = input.selectionDirection ?? state.selection.direction - + state.selection.start = input.selectionStart ?? state.selection.start + state.selection.end = input.selectionEnd ?? state.selection.end + state.selection.direction = input.selectionDirection ?? state.selection.direction const result = this.eventHandlers.input[event.inputType]?.(event, this.unformatState(state), state) ?? state - const formatted = this.partialFormatState(result) if ( input.value != formatted.value || @@ -121,9 +138,8 @@ export class InputStateHandler { input.selectionEnd = formatted.selection.end input.selectionDirection = formatted.selection.direction ?? null } - console.log(event.type, event.inputType, "new Event", event, formatted) - return formatted + commitState(formatted) } } private eventHandlers: Record<"beforeinput" | "input", { [inputType: string]: Handler | undefined }> = { @@ -173,7 +189,7 @@ export class InputStateHandler { deleteWordForward: (event, state) => this.type == "password" ? { ...state, value: (event.target as HTMLInputElement).value } : state, // Chrome will dispatch an input event without inputType when auto-filling - undefined: (event, state) => ({ ...state, value: (event.target as HTMLInputElement).value }), + // undefined: (event, state) => ({ ...state, value: (event.target as HTMLInputElement).value }), }, } diff --git a/src/components/input/index.tsx b/src/components/input/index.tsx index 261552ac2..934c41c1f 100644 --- a/src/components/input/index.tsx +++ b/src/components/input/index.tsx @@ -171,8 +171,8 @@ export class SmoothlyInput implements Clearable, Input, Editable { this.observer.publish() } componentDidLoad() { - if (this.inputElement) - this.inputElement.value = this.state.value + // if (this.inputElement) + // this.inputElement.value = this.state.value } async disconnectedCallback() { if (!this.element.isConnected) @@ -184,8 +184,11 @@ export class SmoothlyInput implements Clearable, Input, Editable { } @Listen("input") onInput(event: InputEvent) { - this.state = this.stateHandler.onInputEvent(event, this.state) - this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) + this.stateHandler.onInputEvent(event, this.state, state => { + this.state = state + this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) + }) + // this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) // console.log("updating state!!", this.state) } copyText(value?: string) { From b51070277c2e117d562bd1f6fef42c4f1350608a Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Tue, 30 Dec 2025 09:08:40 +0100 Subject: [PATCH 7/9] fixed the original bug, but it can still easily be broken, by switching autofill --- src/components/input/InputStateHandler.ts | 32 +++++++++-------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index 1faa8db16..719f2b5ad 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -97,31 +97,25 @@ export class InputStateHandler { public onInputEvent(event: InputEvent, state: tidily.State, commitState: CommitState): void { const input = event.target as HTMLInputElement if (!event.inputType) { + const newValue = input.value this.requestAnimationFrameId && cancelAnimationFrame(this.requestAnimationFrameId) clearTimeout(this.timeoutId) - const valueBeforeRAF = input.value - this.requestAnimationFrameId = requestAnimationFrame(() => { - // this.timeoutId = setTimeout(() => { - const newValue = state.value != input.value ? input.value : valueBeforeRAF - console.log("autofill?", { state: state.value, inputValue: input.value, valueBeforeRAF, newValue }) - if (state.value != newValue) { - const result = { ...state, value: newValue } - const formatted = this.partialFormatState(result) - commitState(formatted) - console.log(event.type, event.inputType, "autofill", event, result) - } - // }, 100) + console.log("autofill?", { lastValue: state.value, newValue }) + const result = { ...state, value: newValue } + const formatted = this.partialFormatState(result) + commitState(formatted) + console.log(event.type, event.inputType, "autofill", event, result) }) } else if (this.nextFormattedState) { - const result = this.nextFormattedState + const newState = this.nextFormattedState this.nextFormattedState = undefined - console.log(event.type, event.inputType, "using nextFormattedState", event, result) - input.value = result.value - input.selectionStart = result.selection.start - input.selectionEnd = result.selection.end - input.selectionDirection = result.selection.direction ?? null - commitState(result) + console.log(event.type, event.inputType, "using nextFormattedState", event, newState) + input.value = newState.value + input.selectionStart = newState.selection.start + input.selectionEnd = newState.selection.end + input.selectionDirection = newState.selection.direction ?? null + commitState(newState) } else { state.selection.start = input.selectionStart ?? state.selection.start state.selection.end = input.selectionEnd ?? state.selection.end From c2675012999fc86b7f55491760ea7c93d22aab1f Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Tue, 30 Dec 2025 09:11:41 +0100 Subject: [PATCH 8/9] Remove console logs --- src/components/input/InputStateHandler.ts | 14 ++++---------- src/components/input/index.tsx | 2 -- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index 719f2b5ad..84816c11b 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -86,11 +86,9 @@ export class InputStateHandler { state.selection.start = input.selectionStart ?? state.selection.start state.selection.end = input.selectionEnd ?? state.selection.end state.selection.direction = input.selectionDirection ?? state.selection.direction - - const result = this.eventHandlers.beforeinput[event.inputType]?.(event, this.unformatState(state), state) - const formatted = result ? this.partialFormatState(result) : undefined + const unformatted = this.eventHandlers.beforeinput[event.inputType]?.(event, this.unformatState(state), state) + const formatted = unformatted ? this.partialFormatState(unformatted) : undefined this.nextFormattedState = formatted - console.log(event.type, event.inputType, event, this.nextFormattedState) } private requestAnimationFrameId: number | undefined private timeoutId: NodeJS.Timeout | undefined @@ -101,16 +99,13 @@ export class InputStateHandler { this.requestAnimationFrameId && cancelAnimationFrame(this.requestAnimationFrameId) clearTimeout(this.timeoutId) this.requestAnimationFrameId = requestAnimationFrame(() => { - console.log("autofill?", { lastValue: state.value, newValue }) - const result = { ...state, value: newValue } - const formatted = this.partialFormatState(result) + const newState = { ...state, value: newValue } + const formatted = this.partialFormatState(newState) commitState(formatted) - console.log(event.type, event.inputType, "autofill", event, result) }) } else if (this.nextFormattedState) { const newState = this.nextFormattedState this.nextFormattedState = undefined - console.log(event.type, event.inputType, "using nextFormattedState", event, newState) input.value = newState.value input.selectionStart = newState.selection.start input.selectionEnd = newState.selection.end @@ -132,7 +127,6 @@ export class InputStateHandler { input.selectionEnd = formatted.selection.end input.selectionDirection = formatted.selection.direction ?? null } - console.log(event.type, event.inputType, "new Event", event, formatted) commitState(formatted) } } diff --git a/src/components/input/index.tsx b/src/components/input/index.tsx index 934c41c1f..f387c30fc 100644 --- a/src/components/input/index.tsx +++ b/src/components/input/index.tsx @@ -188,8 +188,6 @@ export class SmoothlyInput implements Clearable, Input, Editable { this.state = state this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) }) - // this.smoothlyUserInput.emit({ name: this.name, value: this.stateHandler.getValue(this.state) }) - // console.log("updating state!!", this.state) } copyText(value?: string) { if (value) { From f20b3350c7cf41942ff613437367bc0c30ac6bb5 Mon Sep 17 00:00:00 2001 From: Magnus Gustafsson Date: Tue, 30 Dec 2025 09:37:35 +0100 Subject: [PATCH 9/9] COmment --- src/components/input/InputStateHandler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/input/InputStateHandler.ts b/src/components/input/InputStateHandler.ts index 84816c11b..7c85d78b2 100644 --- a/src/components/input/InputStateHandler.ts +++ b/src/components/input/InputStateHandler.ts @@ -95,6 +95,7 @@ export class InputStateHandler { public onInputEvent(event: InputEvent, state: tidily.State, commitState: CommitState): void { const input = event.target as HTMLInputElement if (!event.inputType) { + // Chrome will dispatch an input event without inputType on autofill (this event is not reliable, but it's be best Chrome provides) const newValue = input.value this.requestAnimationFrameId && cancelAnimationFrame(this.requestAnimationFrameId) clearTimeout(this.timeoutId) @@ -176,8 +177,6 @@ export class InputStateHandler { this.type == "password" ? { ...state, value: (event.target as HTMLInputElement).value } : state, deleteWordForward: (event, state) => this.type == "password" ? { ...state, value: (event.target as HTMLInputElement).value } : state, - // Chrome will dispatch an input event without inputType when auto-filling - // undefined: (event, state) => ({ ...state, value: (event.target as HTMLInputElement).value }), }, }