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:
@@ -3,6 +3,7 @@ import { ArrowUpRight } from "lucide-react";
|
||||
import type { TWUI_LINK_LIST_LINK_OBJECT } from "../elements/LinkList";
|
||||
import Link from "./Link";
|
||||
import Row from "./Row";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
type Props = ComponentProps<typeof Link> & {
|
||||
link: TWUI_LINK_LIST_LINK_OBJECT;
|
||||
@@ -15,7 +16,16 @@ type Props = ComponentProps<typeof Link> & {
|
||||
*/
|
||||
export default function ArrowedLink({ link, icon, ...props }: Props) {
|
||||
return (
|
||||
<Link href={link.url} {...props} {...link.linkProps}>
|
||||
<Link
|
||||
href={link.url}
|
||||
{...props}
|
||||
{...link.linkProps}
|
||||
className={twMerge(
|
||||
"twui-arrowed-link",
|
||||
props.className,
|
||||
link.linkProps?.className,
|
||||
)}
|
||||
>
|
||||
<Row>
|
||||
<span>{link.title}</span>
|
||||
{icon || <ArrowUpRight size={17} />}
|
||||
|
||||
@@ -277,6 +277,8 @@ export default function Button({
|
||||
const buttonComponent = (
|
||||
<button
|
||||
{...props}
|
||||
disabled={props.disabled || loading}
|
||||
aria-busy={loading || undefined}
|
||||
className={twMerge(
|
||||
"bg-primary text-white font-medium px-4 py-2 rounded-default",
|
||||
"flex items-center justify-center relative transition-all cursor-pointer",
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ComponentProps, ReactNode } from "react";
|
||||
import type { TWUI_LINK_LIST_LINK_OBJECT } from "../elements/LinkList";
|
||||
import Link from "./Link";
|
||||
import Row from "./Row";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
type Props = ComponentProps<typeof Link> & {
|
||||
link: TWUI_LINK_LIST_LINK_OBJECT;
|
||||
@@ -11,7 +12,7 @@ type Props = ComponentProps<typeof Link> & {
|
||||
|
||||
/**
|
||||
* # Link With an Icon
|
||||
* @className twui-arrowed-link
|
||||
* @className twui-icon-link
|
||||
*/
|
||||
export default function IconLink({
|
||||
link,
|
||||
@@ -23,6 +24,7 @@ export default function IconLink({
|
||||
<Link
|
||||
href={link.url}
|
||||
{...props}
|
||||
className={twMerge("twui-icon-link", props.className)}
|
||||
onClick={(e) => {
|
||||
link.onClick?.(e);
|
||||
props.onClick?.(e);
|
||||
|
||||
+20
-12
@@ -85,6 +85,23 @@ export default function Img({
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setImageError(false);
|
||||
}, [props.src, props.srcLight, props.srcDark]);
|
||||
|
||||
/** Try `fallbackImageSrc` first, then switch to the error state */
|
||||
const handleError = (e: React.SyntheticEvent<HTMLImageElement, Event>) => {
|
||||
const fallbackSrc = props.fallbackImageSrc;
|
||||
|
||||
if (fallbackSrc && e.currentTarget.getAttribute("src") !== fallbackSrc) {
|
||||
e.currentTarget.src = fallbackSrc;
|
||||
} else {
|
||||
setImageError(true);
|
||||
}
|
||||
|
||||
props.onError?.(e);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (checkImageFn) {
|
||||
(async () => {
|
||||
@@ -122,10 +139,7 @@ export default function Img({
|
||||
interpolatedProps.className,
|
||||
)}
|
||||
src={props.srcDark}
|
||||
onError={(e) => {
|
||||
setImageError(true);
|
||||
props.onError?.(e);
|
||||
}}
|
||||
onError={handleError}
|
||||
/>
|
||||
<img
|
||||
loading="lazy"
|
||||
@@ -135,10 +149,7 @@ export default function Img({
|
||||
interpolatedProps.className,
|
||||
)}
|
||||
src={props.srcLight}
|
||||
onError={(e) => {
|
||||
setImageError(true);
|
||||
props.onError?.(e);
|
||||
}}
|
||||
onError={handleError}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -149,10 +160,7 @@ export default function Img({
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
{...interpolatedProps}
|
||||
onError={(e) => {
|
||||
setImageError(true);
|
||||
props.onError?.(e);
|
||||
}}
|
||||
onError={handleError}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { twMerge } from "tailwind-merge";
|
||||
|
||||
/**
|
||||
* # Main Wrapper
|
||||
* @className twui-h1
|
||||
* @className twui-main
|
||||
*/
|
||||
export default function Main({
|
||||
...props
|
||||
|
||||
+12
-1
@@ -25,10 +25,21 @@ export default function Span({
|
||||
size == "large" && "text-lg",
|
||||
size == "larger" && "text-xl",
|
||||
variant == "faded" && "opacity-50",
|
||||
truncate ? `` : ``,
|
||||
"twui-span",
|
||||
props.className,
|
||||
)}
|
||||
style={
|
||||
truncate
|
||||
? {
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitLineClamp: truncate.lines || 1,
|
||||
overflow: "hidden",
|
||||
maxWidth: truncate.width,
|
||||
...props.style,
|
||||
}
|
||||
: props.style
|
||||
}
|
||||
>
|
||||
{props.children}
|
||||
</span>
|
||||
|
||||
+13
-5
@@ -23,14 +23,22 @@ export default function Stack({ gap, componentRef, ...props }: Props) {
|
||||
className={twMerge(
|
||||
"flex flex-col items-start gap-4",
|
||||
props.center && "items-center",
|
||||
gap
|
||||
? typeof gap == "string"
|
||||
? `gap-[${gap}]`
|
||||
: `gap-${gap}`
|
||||
: "",
|
||||
"twui-stack",
|
||||
props.className,
|
||||
)}
|
||||
style={
|
||||
gap !== undefined
|
||||
? {
|
||||
// Inline style: dynamic `gap-${gap}` classes are
|
||||
// invisible to Tailwind's class scanner
|
||||
gap:
|
||||
typeof gap == "number"
|
||||
? `calc(var(--spacing) * ${gap})`
|
||||
: gap,
|
||||
...props.style,
|
||||
}
|
||||
: props.style
|
||||
}
|
||||
ref={componentRef}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
Reference in New Issue
Block a user