fix(SW-3691): Setup one prettier config for whole repo * Setup prettierrc in root and remove other configs Approved-by: Joakim Jäderberg Approved-by: Linus Flood
121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
import type { ChangeEvent, ChangeEventHandler, RefObject } from "react"
|
|
import { useState, useEffect } from "react"
|
|
|
|
interface ClearInputOptions {
|
|
inputRef: RefObject<HTMLInputElement | null>
|
|
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
value?: string | number | readonly string[]
|
|
onRightIconClick?: () => void
|
|
}
|
|
|
|
/**
|
|
* Clears an input field value, handling both controlled and uncontrolled components.
|
|
* Works with React Aria Components Input which can be used standalone or within TextField.
|
|
*/
|
|
export function clearInput({
|
|
inputRef,
|
|
onChange,
|
|
value,
|
|
}: ClearInputOptions): void {
|
|
if (!inputRef.current) {
|
|
return
|
|
}
|
|
|
|
const isControlled = value !== undefined
|
|
|
|
if (isControlled && onChange) {
|
|
// For controlled components: call onChange with a synthetic event
|
|
const syntheticEvent = {
|
|
target: { value: "" },
|
|
currentTarget: inputRef.current,
|
|
} as ChangeEvent<HTMLInputElement>
|
|
onChange(syntheticEvent)
|
|
} else {
|
|
// For uncontrolled components: use native input value setter
|
|
// This triggers React's change detection properly
|
|
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
|
window.HTMLInputElement.prototype,
|
|
"value"
|
|
)?.set
|
|
|
|
if (nativeInputValueSetter) {
|
|
nativeInputValueSetter.call(inputRef.current, "")
|
|
} else {
|
|
inputRef.current.value = ""
|
|
}
|
|
|
|
// Dispatch input event to trigger any listeners
|
|
const inputEvent = new Event("input", {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
})
|
|
inputRef.current.dispatchEvent(inputEvent)
|
|
|
|
// Also dispatch change event
|
|
const changeEvent = new Event("change", {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
})
|
|
inputRef.current.dispatchEvent(changeEvent)
|
|
}
|
|
|
|
// Focus the input after clearing
|
|
inputRef.current.focus()
|
|
}
|
|
|
|
/**
|
|
* Hook to track whether an input has a value.
|
|
* Works for both controlled and uncontrolled components.
|
|
*/
|
|
export function useInputHasValue(
|
|
value: string | number | readonly string[] | undefined,
|
|
inputRef: RefObject<HTMLInputElement | null>
|
|
): boolean {
|
|
const [hasValue, setHasValue] = useState(() => {
|
|
// For controlled components, check value prop
|
|
if (value !== undefined) {
|
|
return String(value ?? "").trim().length > 0
|
|
}
|
|
// For uncontrolled, check ref
|
|
return String(inputRef.current?.value ?? "").trim().length > 0
|
|
})
|
|
|
|
// Sync with controlled value changes
|
|
useEffect(() => {
|
|
if (value !== undefined) {
|
|
setHasValue(String(value ?? "").trim().length > 0)
|
|
}
|
|
}, [value])
|
|
|
|
// For uncontrolled components, sync from ref on input events
|
|
useEffect(() => {
|
|
if (value !== undefined) {
|
|
return // Controlled component, no need to listen
|
|
}
|
|
|
|
const input = inputRef.current
|
|
if (!input) return
|
|
|
|
const updateHasValue = (event?: Event) => {
|
|
// Use setTimeout to ensure we read the value after React/React Aria Components
|
|
// has fully processed the change, especially for operations like select-all + delete
|
|
setTimeout(() => {
|
|
const target = (event?.target as HTMLInputElement) || inputRef.current
|
|
if (target) {
|
|
setHasValue((target.value ?? "").trim().length > 0)
|
|
}
|
|
}, 0)
|
|
}
|
|
|
|
input.addEventListener("input", updateHasValue)
|
|
// Also listen to change event as a fallback
|
|
input.addEventListener("change", updateHasValue)
|
|
return () => {
|
|
input.removeEventListener("input", updateHasValue)
|
|
input.removeEventListener("change", updateHasValue)
|
|
}
|
|
}, [value, inputRef])
|
|
|
|
return hasValue
|
|
}
|