Fix component bugs across hooks, elements, form, layout, editors and utils
- Hooks: per-instance timers in useReady; cleanup for useIntersectionObserver; useWebSocket no longer reconnects after unmount/refresh, drains the queue and dispatches non-JSON messages - Modal/Popover/Dropdown: onClose only on close, Escape to close, no stray DOM props, hooks before early return, scrollbar-aware positioning, top-left/top-right dropdown positions - Elements: Toast/Search/StarRating per-instance timers, Pagination labels and empty state, Breadcrumbs titles, LinkList dividers/props, segment-wise active-link matching (new utils/link-matches-path), Table object cells - Form: Select with a default option is changeable, Input clear notifies controlled parents and copy works on textareas, Checkbox random label, NumberInputButtons runaway interval, upload prop/spread fixes - Layout: Button disabled while loading, Stack gap and Span truncate via inline styles, Img fallbackImageSrc, className/JSDoc fixes - Editors/MDX/AI: TinyMCE id/cleanup/stale handler/duplicate scripts, AceEditor debounce, markdown heights and frontmatter regex, last assistant message, AIPromptBlock prompt ref - Utils: fetchApi headers/FormData/fetchOptions/no mutation, base64 file names and read errors, slugify underscore trim, serializeQuery zero values - Tests for fetchApi, link matching, slugify and serializeQuery - AGENTS.md updated for the package layout Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -59,6 +59,7 @@ export default function NumberInputButtons({
|
||||
|
||||
function incrementDownPress() {
|
||||
window.clearTimeout(pressTimeout);
|
||||
window.clearInterval(pressInterval);
|
||||
setButtonDown(true);
|
||||
|
||||
pressTimeout = setTimeout(() => {
|
||||
@@ -75,6 +76,8 @@ export default function NumberInputButtons({
|
||||
}
|
||||
|
||||
function decrementDownPress() {
|
||||
window.clearTimeout(pressTimeout);
|
||||
window.clearInterval(pressInterval);
|
||||
setButtonDown(true);
|
||||
|
||||
pressTimeout = setTimeout(() => {
|
||||
@@ -143,7 +146,9 @@ export default function NumberInputButtons({
|
||||
onMouseDown={decrementDownPress}
|
||||
onTouchStart={decrementDownPress}
|
||||
onMouseUp={decrementDownCancel}
|
||||
onMouseLeave={decrementDownCancel}
|
||||
onTouchEnd={decrementDownCancel}
|
||||
onTouchCancel={decrementDownCancel}
|
||||
>
|
||||
<Minus size={20} />
|
||||
</Row>
|
||||
@@ -156,7 +161,9 @@ export default function NumberInputButtons({
|
||||
onMouseDown={incrementDownPress}
|
||||
onTouchStart={incrementDownPress}
|
||||
onMouseUp={incrementDownCancel}
|
||||
onMouseLeave={incrementDownCancel}
|
||||
onTouchEnd={incrementDownCancel}
|
||||
onTouchCancel={incrementDownCancel}
|
||||
>
|
||||
<Plus size={20} />
|
||||
</Row>
|
||||
|
||||
+37
-28
@@ -153,8 +153,10 @@ export default function Input<KeyType extends string>(
|
||||
},
|
||||
);
|
||||
|
||||
const inputRef = componentRef || React.useRef<HTMLInputElement>(null);
|
||||
const textAreaRef = componentRef || React.useRef<HTMLTextAreaElement>(null);
|
||||
const internalInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const internalTextAreaRef = React.useRef<HTMLTextAreaElement>(null);
|
||||
const inputRef = componentRef || internalInputRef;
|
||||
const textAreaRef = componentRef || internalTextAreaRef;
|
||||
const buttonDownRef = React.useRef(false);
|
||||
|
||||
const [value, setValue] = React.useState(
|
||||
@@ -344,7 +346,11 @@ export default function Input<KeyType extends string>(
|
||||
|
||||
return (
|
||||
<Stack
|
||||
title={`${finalLabel}${props.required ? " (Required)" : ""}`}
|
||||
title={
|
||||
finalLabel
|
||||
? `${finalLabel}${props.required ? " (Required)" : ""}`
|
||||
: undefined
|
||||
}
|
||||
{...wrapperWrapperProps}
|
||||
className={twMerge(
|
||||
"w-full gap-1.5 relative z-0 hover:z-100",
|
||||
@@ -417,9 +423,9 @@ export default function Input<KeyType extends string>(
|
||||
title="Copy value"
|
||||
className="cursor-pointer opacity-50 hover:opacity-100"
|
||||
onClick={() => {
|
||||
const text =
|
||||
inputRef.current.value ||
|
||||
textAreaRef.current.value;
|
||||
const text = (
|
||||
istextarea ? textAreaRef : inputRef
|
||||
).current?.value;
|
||||
|
||||
if (text) {
|
||||
navigator.clipboard
|
||||
@@ -446,14 +452,18 @@ export default function Input<KeyType extends string>(
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (inputRef.current) {
|
||||
inputRef.current.value = "";
|
||||
}
|
||||
if (textAreaRef.current) {
|
||||
textAreaRef.current.value = "";
|
||||
const element = istextarea
|
||||
? textAreaRef.current
|
||||
: inputRef.current;
|
||||
|
||||
// Goes through React's onChange so controlled
|
||||
// inputs and `onChange` listeners see the clear
|
||||
if (element) {
|
||||
setNativeInputValue(element, "");
|
||||
} else {
|
||||
setValue("");
|
||||
}
|
||||
|
||||
setValue("");
|
||||
clearInputProps?.onClick?.(e);
|
||||
}}
|
||||
>
|
||||
@@ -465,7 +475,7 @@ export default function Input<KeyType extends string>(
|
||||
<div
|
||||
title={
|
||||
inputType == "password"
|
||||
? "View Psasword"
|
||||
? "View Password"
|
||||
: "Hide Password"
|
||||
}
|
||||
className={twMerge("p-1 -my-2 -mx-1")}
|
||||
@@ -545,25 +555,24 @@ export default function Input<KeyType extends string>(
|
||||
|
||||
const PICKER_INPUT_TYPES = ["date", "datetime-local", "time", "month", "week"];
|
||||
|
||||
const updateInput = ({
|
||||
newValue,
|
||||
inputRef,
|
||||
}: {
|
||||
newValue: string;
|
||||
inputRef: RefObject<HTMLInputElement & HTMLTextAreaElement>;
|
||||
}) => {
|
||||
const input = inputRef.current;
|
||||
if (!input) return;
|
||||
function setNativeInputValue(
|
||||
element: HTMLInputElement | HTMLTextAreaElement,
|
||||
newValue: string,
|
||||
) {
|
||||
const prototype =
|
||||
element instanceof HTMLTextAreaElement
|
||||
? window.HTMLTextAreaElement.prototype
|
||||
: window.HTMLInputElement.prototype;
|
||||
|
||||
// Get the native setter (React overrides the default one)
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype,
|
||||
const nativeValueSetter = Object.getOwnPropertyDescriptor(
|
||||
prototype,
|
||||
"value",
|
||||
)?.set;
|
||||
|
||||
// Call it directly on the input
|
||||
nativeInputValueSetter?.call(input, newValue);
|
||||
// Call it directly on the element
|
||||
nativeValueSetter?.call(element, newValue);
|
||||
|
||||
// Dispatch the event - React listens for 'input', not 'change'
|
||||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
};
|
||||
element.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user