Fix types

This commit is contained in:
Konstantin Wohlwend 2026-06-01 16:25:49 -07:00
parent f9d081da09
commit 2e1bfecb5f
24 changed files with 215 additions and 656 deletions

View File

@ -79,7 +79,7 @@ type ListItemProps = {
onMouseLeave?: () => void,
isEven?: boolean,
isHighlighted?: boolean,
itemRef?: React.RefObject<HTMLDivElement>,
itemRef?: React.RefObject<HTMLDivElement | null>,
actionItems?: ActionMenuItem[],
};
@ -180,9 +180,9 @@ function ListGroup({ title, children }: ListGroupProps) {
// Connection line component
type ConnectionLineProps = {
fromRef: React.RefObject<HTMLDivElement>,
toRef: React.RefObject<HTMLDivElement>,
containerRef: React.RefObject<HTMLDivElement>,
fromRef: React.RefObject<HTMLDivElement | null>,
toRef: React.RefObject<HTMLDivElement | null>,
containerRef: React.RefObject<HTMLDivElement | null>,
quantity?: number,
};
@ -320,7 +320,7 @@ type ProductsListProps = {
paymentsGroups: any,
hoveredItemId: string | null,
getConnectedProducts: (itemId: string) => string[],
productRefs?: Record<string, React.RefObject<HTMLDivElement>>,
productRefs?: Record<string, React.RefObject<HTMLDivElement | null>>,
onProductMouseEnter: (productId: string) => void,
onProductMouseLeave: () => void,
onProductAdd?: () => void,
@ -470,7 +470,7 @@ type ItemsListProps = {
items: CompleteConfig['payments']['items'],
hoveredProductId: string | null,
getConnectedItems: (productId: string) => string[],
itemRefs?: Record<string, React.RefObject<HTMLDivElement>>,
itemRefs?: Record<string, React.RefObject<HTMLDivElement | null>>,
onItemMouseEnter: (itemId: string) => void,
onItemMouseLeave: () => void,
onItemAdd?: () => void,

View File

@ -3,8 +3,8 @@
import { useRef } from "react";
export function useStableValue<T>(value: T, fingerprint: string): T {
const previousRef = useRef<{ fingerprint: string, value: T }>();
if (previousRef.current && previousRef.current.fingerprint === fingerprint) {
const previousRef = useRef<{ fingerprint: string, value: T } | null>(null);
if (previousRef.current != null && previousRef.current.fingerprint === fingerprint) {
return previousRef.current.value;
}
previousRef.current = { fingerprint, value };

View File

@ -6,7 +6,7 @@ import { CalendarIcon, CaretDownIcon, CaretUpIcon, InfoIcon, XIcon } from '@phos
import { captureError } from '@hexclave/shared/dist/utils/errors';
import { runAsynchronously } from '@hexclave/shared/dist/utils/promises';
import Image from 'next/image';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState, type ComponentPropsWithoutRef } from 'react';
import { createPortal } from 'react-dom';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
@ -101,8 +101,8 @@ export function ChangelogWidget({ isActive, initialData }: ChangelogWidgetProps)
</div>
</div>
),
img: ({ src, alt }: { src?: string, alt?: string }) => {
if (!src) return null;
img: ({ src, alt }: ComponentPropsWithoutRef<'img'>) => {
if (typeof src !== 'string') return null;
return (
<button
type="button"

View File

@ -4,12 +4,12 @@ import { cn } from "@/lib/utils";
import { CopyIcon, SparkleIcon } from "@phosphor-icons/react";
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
import React from "react";
import { Button } from "./button";
import { Button, type ButtonProps } from "./button";
import { useToast } from "./use-toast";
const CopyButton = forwardRefIfNeeded<
React.ElementRef<typeof Button>,
React.ComponentProps<typeof Button> & { content: string }
HTMLButtonElement,
ButtonProps & { content: string }
>((props, ref) => {
const { toast } = useToast();
@ -36,8 +36,8 @@ const CopyButton = forwardRefIfNeeded<
CopyButton.displayName = "CopyButton";
const CopyPromptButton = forwardRefIfNeeded<
React.ElementRef<typeof Button>,
React.ComponentProps<typeof Button> & { content: string }
HTMLButtonElement,
ButtonProps & { content: string }
>(({ content, children, onClick, ...props }, ref) => {
const { toast } = useToast();

View File

@ -16,8 +16,8 @@ const DropdownMenuContext = React.createContext<{
} | undefined>(undefined);
const DropdownMenu = forwardRefIfNeeded<
React.ElementRef<typeof DropdownMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>
never,
DropdownMenuPrimitive.DropdownMenuProps
>(({ ...props }, ref) => {
const [open, setOpen] = React.useState(() => props.open ?? props.defaultOpen ?? false);

View File

@ -46,8 +46,8 @@
"next-themes": "^0.4.6",
"posthog-js": "^1.336.1",
"posthog-node": "^4.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-medium-image-zoom": "^5.4.0",
"react-remove-scroll": "^2.7.0",
"remark": "^15.0.1",
@ -62,8 +62,8 @@
"@tailwindcss/postcss": "^4.1.7",
"@types/mdx": "^2.0.13",
"@types/node": "22.15.18",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"eslint": "^8.57.0",
"eslint-config-next": "15.3.2",
"glob": "^11.0.0",

View File

@ -185,12 +185,28 @@ function renderNode(node: MessageNode, index: number): React.ReactNode {
}
case 'heading': {
const HeadingTag = `h${Math.min(node.level || 1, 6)}` as keyof JSX.IntrinsicElements;
return (
<HeadingTag key={index} className="font-semibold mt-4 mb-2 text-sm">
{node.children?.map(renderNode)}
</HeadingTag>
);
const children = node.children?.map(renderNode);
const className = "font-semibold mt-4 mb-2 text-sm";
switch (Math.min(node.level || 1, 6)) {
case 1: {
return <h1 key={index} className={className}>{children}</h1>;
}
case 2: {
return <h2 key={index} className={className}>{children}</h2>;
}
case 3: {
return <h3 key={index} className={className}>{children}</h3>;
}
case 4: {
return <h4 key={index} className={className}>{children}</h4>;
}
case 5: {
return <h5 key={index} className={className}>{children}</h5>;
}
default: {
return <h6 key={index} className={className}>{children}</h6>;
}
}
}
case 'paragraph': {

View File

@ -202,7 +202,7 @@ export function CustomSearchDialog({ open, onOpenChange }: CustomSearchDialogPro
const [selectedIndex, setSelectedIndex] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
const searchTimeoutRef = useRef<NodeJS.Timeout>();
const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const sidebarContext = useSidebar();
// Handle AI chat opening

View File

@ -21,7 +21,7 @@ export type BaseCodeblockProps = {
/** Custom key to force re-render when theme changes externally */
themeKey?: string,
/** Ref to attach to the code container div for measuring line positions */
codeContainerRef?: React.RefObject<HTMLDivElement>,
codeContainerRef?: React.RefObject<HTMLDivElement | null>,
};
/**

View File

@ -3,7 +3,7 @@
import { useUser } from '@hexclave/next';
import { runAsynchronously } from '@hexclave/shared/dist/utils/promises';
import { decodeProtectedHeader, decodeJwt as joseDecodeJwt } from 'jose';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState, type ReactElement } from 'react';
import { cn } from '../../lib/cn';
type DecodedJWT = {
@ -177,7 +177,7 @@ export function JWTViewer({ defaultToken = '', className = '' }: JWTViewerProps)
return { status, exp, iat, nbf, issuer, audience, subject } as const;
}, [decoded]);
const summaryRows: Array<{ key: string, label: string, content: JSX.Element }> = [];
const summaryRows: Array<{ key: string, label: string, content: ReactElement }> = [];
if (decoded && tokenMeta) {
if (tokenMeta.issuer) {
summaryRows.push({

View File

@ -1,6 +1,5 @@
import { StackProvider, StackTheme } from "@hexclave/next";
import { Metadata } from "next";
import React from "react";
import Header from "src/components/header";
import Provider from "src/components/provider";
import { stackServerApp } from "src/stack";
@ -13,9 +12,7 @@ export const metadata: Metadata = {
export default function RootLayout({
children,
}: {
children: React.ReactNode,
}) {
}: LayoutProps<"/">) {
return (
<html lang="en" suppressHydrationWarning>
<head />

View File

@ -10,10 +10,12 @@ export const metadata: Metadata = {
description: "A demo of Hexclave's middleware capabilities.",
};
type StackThemeChildren = Parameters<typeof StackTheme>[0]["children"];
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode,
children: StackThemeChildren,
}>) {
return (
<html lang="en">

View File

@ -84,8 +84,8 @@
"yup": "^1.7.1"
},
"peerDependencies": {
"@types/react": ">=18.3.0",
"react": ">=18.3.0"
"@types/react": ">=19.0.0",
"react": ">=19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {

View File

@ -1,7 +1,7 @@
import { useLayoutEffect } from "react";
import { useRefState } from "../utils/react";
export function useHover<T extends HTMLElement>(
export function useHover<T extends HTMLElement | null>(
ref: React.RefObject<T>,
options: {
onMouseEnter?: () => void,

View File

@ -67,7 +67,7 @@ export function getNodeText(node: React.ReactNode): string {
if (Array.isArray(node)) {
return node.map(getNodeText).join("");
}
if (typeof node === "object" && "props" in node) {
if (React.isValidElement<{ children?: React.ReactNode }>(node)) {
return getNodeText(node.props.children);
}
throw new Error(`Unknown node type: ${typeof node}`);

View File

@ -2,7 +2,7 @@
import { CircleAlert, Info, LucideIcon } from "lucide-react";
import React, { Suspense, useId } from "react";
import { Alert, Button, Checkbox, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Label, Skeleton } from '..';
import { Alert, Button, type ButtonProps, Checkbox, Dialog, DialogBody, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Label, Skeleton } from '..';
export type ActionDialogProps = {
trigger?: React.ReactNode,
@ -16,12 +16,12 @@ export type ActionDialogProps = {
okButton?: boolean | Readonly<{
label?: string,
onClick?: () => Promise<"prevent-close" | undefined | void>,
props?: Partial<React.ComponentProps<typeof Button>>,
props?: Partial<ButtonProps>,
}>,
cancelButton?: boolean | Readonly<{
label?: string,
onClick?: () => Promise<"prevent-close" | undefined | void>,
props?: Partial<React.ComponentProps<typeof Button>>,
props?: Partial<ButtonProps>,
}>,
confirmText?: string,
children?: React.ReactNode,

View File

@ -3,11 +3,11 @@
import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
import { Copy } from "lucide-react";
import React from "react";
import { Button, cn, useToast } from "..";
import { Button, cn, type ButtonProps, useToast } from "..";
const CopyButton = forwardRefIfNeeded<
React.ElementRef<typeof Button>,
React.ComponentProps<typeof Button> & { content: string }
HTMLButtonElement,
ButtonProps & { content: string }
>((props, ref) => {
const { toast } = useToast();

View File

@ -17,8 +17,8 @@ const DropdownMenuContext = React.createContext<{
} | undefined>(undefined);
const DropdownMenu = forwardRefIfNeeded<
React.ElementRef<typeof DropdownMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>
never,
DropdownMenuPrimitive.DropdownMenuProps
>(({ ...props }, ref) => {
const [open, setOpen] = React.useState(!!props.open);

View File

@ -85,11 +85,11 @@
"yup": "^1.7.1"
},
"peerDependencies": {
"@types/react": ">=18.3.0",
"@types/react-dom": ">=18.3.0",
"react-dom": ">=18.3.0",
"@types/react": ">=19.0.0",
"@types/react-dom": ">=19.0.0",
"react-dom": ">=19.0.0",
"next": ">=14.1 || >=15.0.0-canary.0 || >=15.0.0-rc.0",
"react": ">=18.3.0"
"react": ">=19.0.0"
},
"peerDependenciesMeta": {
"@types/react-dom": {

View File

@ -95,10 +95,10 @@
"yup": "^1.7.1"
},
"peerDependencies": {
"@types/react": ">=18.3.0",
"@types/react": ">=19.0.0",
"@tanstack/react-router": ">=1.100.0",
"@tanstack/react-start": ">=1.100.0",
"react": ">=18.3.0"
"react": ">=19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {

View File

@ -140,17 +140,17 @@
},
"//": "IF_PLATFORM react-like",
"peerDependencies": {
"@types/react": ">=18.3.0",
"@types/react": ">=19.0.0",
"//": "IF_PLATFORM next",
"@types/react-dom": ">=18.3.0",
"react-dom": ">=18.3.0",
"@types/react-dom": ">=19.0.0",
"react-dom": ">=19.0.0",
"next": ">=14.1 || >=15.0.0-canary.0 || >=15.0.0-rc.0",
"//": "END_PLATFORM",
"//": "IF_PLATFORM tanstack-start",
"@tanstack/react-router": ">=1.100.0",
"@tanstack/react-start": ">=1.100.0",
"//": "END_PLATFORM",
"react": ">=18.3.0"
"react": ">=19.0.0"
},
"//": "END_PLATFORM",
"//": "IF_PLATFORM react-like",

View File

@ -101,13 +101,13 @@
"yup": "^1.7.1"
},
"peerDependencies": {
"@types/react": ">=18.3.0",
"@types/react-dom": ">=18.3.0",
"react-dom": ">=18.3.0",
"@types/react": ">=19.0.0",
"@types/react-dom": ">=19.0.0",
"react-dom": ">=19.0.0",
"next": ">=14.1 || >=15.0.0-canary.0 || >=15.0.0-rc.0",
"@tanstack/react-router": ">=1.100.0",
"@tanstack/react-start": ">=1.100.0",
"react": ">=18.3.0"
"react": ">=19.0.0"
},
"peerDependenciesMeta": {
"@types/react-dom": {

View File

@ -2,6 +2,7 @@
import { BrandIcons, Button, SimpleTooltip } from '@hexclave/ui';
import Color, { ColorInstance } from 'color';
import type { ReactElement } from 'react';
import { useEffect, useId, useState } from 'react';
import { useStackApp } from '../lib/hooks';
import { useTranslation } from '../lib/translations';
@ -42,7 +43,7 @@ export function OAuthButton({
backgroundColor?: string,
textColor?: string,
name: string,
icon: JSX.Element | null,
icon: ReactElement | null,
border?: string,
};
switch (provider) {

File diff suppressed because it is too large Load Diff