mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 01:15:03 +03:00
chore: upgrade to react-19 (#45886)
- Most changes are related to either types or `useRef` usages (it now requires an initial value). - also updated `vaul` to its latest version and haven't noticed any change ([design-system demo](https://design-system-git-react-19-supabase.vercel.app/design-system/docs/components/drawer)) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Upgraded workspace to React 19. * **Bug Fixes** * Improved null-safety and ref handling across editors, UI components, shortcuts, and markdown/image rendering to reduce runtime errors. * Safer event/timeout/interval cleanup and more robust command/context handling. * **Chores** * Bumped vaul dependency versions. * **Documentation** * Type and TypeScript accuracy improvements for clearer developer feedback. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45886) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
bffe49bb1d
commit
86a3f8b03d
59 files changed
+1543
-1537
No files matched your search
@@ -30,11 +30,17 @@ export function ComponentExample({
|
||||
src: _,
|
||||
...props
|
||||
}: ComponentExampleProps) {
|
||||
const [Example, Code, ...Children] = React.Children.toArray(children) as React.ReactElement[]
|
||||
const [Example, Code, ...Children] = React.Children.toArray(children) as React.ReactElement<{
|
||||
children: React.ReactNode
|
||||
'data-rehype-pretty-code-fragment'?: unknown
|
||||
}>[]
|
||||
|
||||
const codeString = React.useMemo(() => {
|
||||
if (typeof Code?.props['data-rehype-pretty-code-fragment'] !== 'undefined') {
|
||||
const [, Button] = React.Children.toArray(Code.props.children) as React.ReactElement[]
|
||||
const [, Button] = React.Children.toArray(Code.props.children) as React.ReactElement<{
|
||||
value?: string
|
||||
__rawString__?: string
|
||||
}>[]
|
||||
return Button?.props?.value || Button?.props?.__rawString__ || null
|
||||
}
|
||||
}, [Code])
|
||||
@@ -58,7 +64,7 @@ export function ComponentExample({
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
{
|
||||
extractedClassNames ? (
|
||||
extractedClassNames && codeString ? (
|
||||
<CopyWithClassNames
|
||||
value={codeString}
|
||||
classNames={extractedClassNames}
|
||||
|
||||
@@ -9,21 +9,6 @@ import { Index } from '../__registry__'
|
||||
import { styles } from '../registry/styles'
|
||||
import { UnistNode, UnistTree } from '@/types/unist'
|
||||
|
||||
// ComponentType
|
||||
|
||||
function inspectComponentProps<T>(component: React.ComponentType<T>): void {
|
||||
// Assert the component's props type
|
||||
const defaultProps = (component as React.ComponentType<any>).defaultProps || {}
|
||||
|
||||
// console.log('Component props:')
|
||||
// console.log('----------------')
|
||||
|
||||
for (const propName in defaultProps) {
|
||||
const propType = typeof defaultProps[propName]
|
||||
console.log(`${propName}: ${propType}`)
|
||||
}
|
||||
}
|
||||
|
||||
export function rehypeComponent() {
|
||||
return async (tree: UnistTree) => {
|
||||
visit(tree, (node: UnistNode) => {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import matter from 'gray-matter'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { type ComponentProps } from 'react'
|
||||
import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlobalFlagsHandler'
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import { components } from '~/features/docs/MdxBase.shared'
|
||||
import { RefSubLayout } from '~/features/docs/Reference.ui'
|
||||
import { cache_fullProcess_withDevCacheBust } from '~/features/helpers.fs'
|
||||
import { REF_DOCS_DIRECTORY } from '~/lib/docs'
|
||||
import matter from 'gray-matter'
|
||||
import { type ComponentProps } from 'react'
|
||||
|
||||
import { CodeBlock } from '../ui/CodeBlock/CodeBlock'
|
||||
|
||||
async function getRefMarkdownInternal(relPath: string) {
|
||||
@@ -52,6 +53,7 @@ function MDXRemoteRefs({ source }: MDXRemoteRefsProps) {
|
||||
CliGlobalFlagsHandler,
|
||||
}
|
||||
|
||||
// @ts-expect-error: Gildas - This is because RefSubLayout is a namespace containing multiple components. It works but the MDXClient types are stricter
|
||||
return <MDXRemoteBase source={source} components={refComponents} />
|
||||
}
|
||||
|
||||
|
||||
@@ -123,8 +123,8 @@ export function ReferenceNavigationScrollHandler({
|
||||
children,
|
||||
...rest
|
||||
}: PropsWithChildren & HTMLAttributes<HTMLDivElement>) {
|
||||
const parentRef = useRef<HTMLElement>()
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const parentRef = useRef<HTMLElement | null>(null)
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
const initialScrollHappened = useContext(ReferenceContentInitiallyScrolledContext)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@/state/table-editor-table'
|
||||
|
||||
type ShortcutsProps = {
|
||||
gridRef: RefObject<DataGridHandle>
|
||||
gridRef: RefObject<DataGridHandle | null>
|
||||
rows: SupaRow[]
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export const TemplateEditor = ({ template }: TemplateEditorProps) => {
|
||||
PermissionAction.UPDATE,
|
||||
'custom_config_gotrue'
|
||||
)
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor>()
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor>(null)
|
||||
|
||||
// [Joshen] Error state is handled in the parent
|
||||
const { data: authConfig, isSuccess } = useAuthConfigQuery({ projectRef })
|
||||
|
||||
@@ -48,7 +48,7 @@ export const RLSCodeEditor = ({
|
||||
editorRef,
|
||||
monacoRef,
|
||||
}: RLSCodeEditorProps) => {
|
||||
const hasValue = useRef<any>()
|
||||
const hasValue = useRef<editor.IContextKey<boolean>>(null)
|
||||
const monaco = useMonaco()
|
||||
|
||||
const placeholderId = `monaco-placeholder-${id}`
|
||||
@@ -104,7 +104,9 @@ export const RLSCodeEditor = ({
|
||||
}
|
||||
|
||||
const onChangeContent: OnChange = (value) => {
|
||||
hasValue.current.set((value ?? '').length > 0)
|
||||
if (hasValue.current) {
|
||||
hasValue.current.set((value ?? '').length > 0)
|
||||
}
|
||||
|
||||
const placeholderEl = document.getElementById(placeholderId) as HTMLElement | null
|
||||
if (placeholderEl) {
|
||||
|
||||
@@ -17,8 +17,8 @@ import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
||||
import { useShortcut } from '@/state/shortcuts/useShortcut'
|
||||
|
||||
interface UseAuthUsersShortcutsParams {
|
||||
gridRef: RefObject<DataGridHandle>
|
||||
searchInputRef: RefObject<HTMLInputElement>
|
||||
gridRef: RefObject<DataGridHandle | null>
|
||||
searchInputRef: RefObject<HTMLInputElement | null>
|
||||
users: User[]
|
||||
selectedUsers: Set<unknown>
|
||||
setSelectedUsers: Dispatch<SetStateAction<Set<unknown>>>
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ export const PlanUpdateSidePanel = () => {
|
||||
selectedOrganization?.managed_by === MANAGED_BY.STRIPE_PROJECTS
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const originalPlanRef = useRef<string>()
|
||||
const originalPlanRef = useRef<string>(undefined)
|
||||
|
||||
const [showExitSurvey, setShowExitSurvey] = useState(false)
|
||||
const [showUpgradeSurvey, setShowUpgradeSurvey] = useState(false)
|
||||
|
||||
@@ -69,7 +69,7 @@ export const billingMetricUnit = (pricingMetric: PricingMetric) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const generateUpgradeReasons = (originalPlan?: string, upgradedPlan?: string) => {
|
||||
export const generateUpgradeReasons = (originalPlan?: string | null, upgradedPlan?: string) => {
|
||||
const reasons = [
|
||||
'Current plan limits are not enough for me',
|
||||
'I want better customer support from Supabase',
|
||||
|
||||
@@ -46,7 +46,7 @@ const InlineWidget = ({
|
||||
const lineNumber = beforeLineNumber ?? afterLineNumber
|
||||
const key = `${id}-${lineNumber.toString()}`
|
||||
const containerElement = useMemo(() => document.createElement('div'), [])
|
||||
const zoneIdRef = useRef<string>()
|
||||
const zoneIdRef = useRef<string>(null)
|
||||
const viewZoneRef = useRef<{
|
||||
top: number
|
||||
height: number
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
||||
import { useShortcut } from '@/state/shortcuts/useShortcut'
|
||||
|
||||
interface UseFilesBucketsShortcutsParams {
|
||||
searchInputRef: RefObject<HTMLInputElement>
|
||||
searchInputRef: RefObject<HTMLInputElement | null>
|
||||
setFilterString: Dispatch<SetStateAction<string>>
|
||||
sortBucket: STORAGE_BUCKET_SORT
|
||||
setSortBucket: (value: STORAGE_BUCKET_SORT) => void
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
||||
import { useShortcut } from '@/state/shortcuts/useShortcut'
|
||||
|
||||
interface UseFileExplorerHeaderShortcutsParams {
|
||||
uploadButtonRef: RefObject<HTMLInputElement>
|
||||
searchInputRef: RefObject<HTMLInputElement>
|
||||
uploadButtonRef: RefObject<HTMLInputElement | null>
|
||||
searchInputRef: RefObject<HTMLInputElement | null>
|
||||
canUpdateStorage: boolean
|
||||
hasBreadcrumbs: boolean
|
||||
isSearching: boolean
|
||||
|
||||
@@ -138,7 +138,7 @@ export function useAttachmentUpload() {
|
||||
}
|
||||
|
||||
interface AttachmentUploadDisplayProps {
|
||||
uploadButtonRef: RefObject<HTMLInputElement>
|
||||
uploadButtonRef: RefObject<HTMLInputElement | null>
|
||||
isFull: boolean
|
||||
uploadedDataUrls: string[]
|
||||
addFile: () => void
|
||||
|
||||
@@ -34,7 +34,7 @@ vi.mock('next/head', async () => {
|
||||
(child) => React.isValidElement(child) && child.type === 'title'
|
||||
)
|
||||
|
||||
if (!React.isValidElement(titleElement)) return
|
||||
if (!React.isValidElement<{ children: ReactNode }>(titleElement)) return
|
||||
|
||||
const titleText = React.Children.toArray(titleElement.props.children).join('')
|
||||
document.title = titleText
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface ConnectingStateProps {
|
||||
|
||||
const ConnectingState = ({ project }: ConnectingStateProps) => {
|
||||
const { ref } = useParams()
|
||||
const checkProjectConnectionIntervalRef = useRef<number>()
|
||||
const checkProjectConnectionIntervalRef = useRef<number>(null)
|
||||
|
||||
const { setProjectPostgrestStatus } = useSetProjectPostgrestStatus()
|
||||
const { invalidateProjectDetailsQuery } = useInvalidateProjectDetailsQuery()
|
||||
@@ -31,14 +31,18 @@ const ConnectingState = ({ project }: ConnectingStateProps) => {
|
||||
// pingPostgrest timeouts in 2s, wait for another 2s before checking again
|
||||
checkProjectConnectionIntervalRef.current = window.setInterval(testProjectConnection, 4000)
|
||||
return () => {
|
||||
clearInterval(checkProjectConnectionIntervalRef.current)
|
||||
if (checkProjectConnectionIntervalRef.current) {
|
||||
clearInterval(checkProjectConnectionIntervalRef.current)
|
||||
}
|
||||
}
|
||||
}, [project])
|
||||
|
||||
const testProjectConnection = async () => {
|
||||
const result = await pingPostgrest(project.ref)
|
||||
if (result) {
|
||||
clearInterval(checkProjectConnectionIntervalRef.current)
|
||||
if (checkProjectConnectionIntervalRef.current) {
|
||||
clearInterval(checkProjectConnectionIntervalRef.current)
|
||||
}
|
||||
setProjectPostgrestStatus(project.ref, 'ONLINE')
|
||||
await invalidateProjectDetailsQuery(project.ref)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ vi.mock('next/head', async () => {
|
||||
(child) => React.isValidElement(child) && child.type === 'title'
|
||||
)
|
||||
|
||||
if (!React.isValidElement(titleElement)) return
|
||||
if (!React.isValidElement<{ children: ReactNode }>(titleElement)) return
|
||||
|
||||
const titleText = React.Children.toArray(titleElement.props.children).join('')
|
||||
document.title = titleText
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { AssistantModelId } from '@/lib/ai/model.utils'
|
||||
|
||||
export interface FormProps {
|
||||
/* The ref for the textarea, optional. Exposed for the CommandsPopover to attach events. */
|
||||
textAreaRef?: React.RefObject<HTMLTextAreaElement>
|
||||
textAreaRef?: React.RefObject<HTMLTextAreaElement | null>
|
||||
/* The loading state of the form */
|
||||
loading: boolean
|
||||
/* The disabled state of the form */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { untrustedSql } from '@supabase/pg-meta'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Link from 'next/link'
|
||||
import {
|
||||
import React, {
|
||||
isValidElement,
|
||||
memo,
|
||||
ReactNode,
|
||||
@@ -135,8 +135,8 @@ const baseMarkdownComponents = {
|
||||
h3: Heading3,
|
||||
code: InlineCode,
|
||||
a: Hyperlink,
|
||||
img: ({ src }: JSX.IntrinsicElements['img']) => (
|
||||
<span className="text-foreground-light font-mono">[Image: {src}]</span>
|
||||
img: ({ src }: React.JSX.IntrinsicElements['img']) => (
|
||||
<span className="text-foreground-light font-mono">[Image: {src?.toString()}]</span>
|
||||
),
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ export function MessageMarkdown({
|
||||
() => ({
|
||||
...markdownComponents,
|
||||
...baseMarkdownComponents,
|
||||
pre: (props: JSX.IntrinsicElements['pre']) => (
|
||||
pre: (props: React.JSX.IntrinsicElements['pre']) => (
|
||||
<MarkdownPre id={id} isLoading={isLoading} readOnly={readOnly}>
|
||||
{props.children}
|
||||
</MarkdownPre>
|
||||
@@ -206,8 +206,11 @@ export const MarkdownPre = ({
|
||||
})
|
||||
|
||||
const childArray = Array.isArray(children) ? children : [children]
|
||||
const codeElement = childArray.find((child): child is ReactElement => isValidElement(child))
|
||||
const codeProps = codeElement?.props || {}
|
||||
const codeElement = childArray.find(
|
||||
(child): child is ReactElement<{ className?: string; children: ReactNode }> =>
|
||||
isValidElement<{ className?: string; children: ReactNode }>(child)
|
||||
)
|
||||
const codeProps = codeElement?.props || ({} as { className?: string; children: ReactNode })
|
||||
const language = codeProps.className?.replace('language-', '') || 'sql'
|
||||
const codeChildren = codeProps.children
|
||||
const rawContent = Array.isArray(codeChildren)
|
||||
|
||||
@@ -8,7 +8,7 @@ export function useAutoScroll({ enabled = true }: UseAutoScrollProps = {}) {
|
||||
const [container, setContainer] = useState<HTMLDivElement | null>(null)
|
||||
const [isSticky, setIsSticky] = useState(true)
|
||||
const isStickyRef = useRef(true)
|
||||
const lastScrollHeightRef = useRef<number>()
|
||||
const lastScrollHeightRef = useRef<number>(null)
|
||||
|
||||
const ref = useCallback((element: HTMLDivElement | null) => {
|
||||
if (element) {
|
||||
@@ -36,7 +36,7 @@ export function useAutoScroll({ enabled = true }: UseAutoScrollProps = {}) {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => {
|
||||
if (
|
||||
lastScrollHeightRef.current !== undefined &&
|
||||
lastScrollHeightRef.current != null &&
|
||||
container.scrollHeight !== lastScrollHeightRef.current
|
||||
) {
|
||||
lastScrollHeightRef.current = container.scrollHeight
|
||||
|
||||
@@ -85,7 +85,11 @@ const LazyChartWrapper = ({ children }: PropsWithChildren) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div ref={ref}>{React.cloneElement(children as React.ReactElement, { isVisible })}</div>
|
||||
return (
|
||||
<div ref={ref}>
|
||||
{React.cloneElement(children as React.ReactElement<{ isVisible: boolean }>, { isVisible })}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,7 @@ interface CodeEditorProps {
|
||||
explainCode: CodeEditorActions
|
||||
closeAssistant: CodeEditorActions
|
||||
}>
|
||||
editorRef?: MutableRefObject<editor.IStandaloneCodeEditor | undefined>
|
||||
editorRef?: MutableRefObject<editor.IStandaloneCodeEditor | null>
|
||||
onInputChange?: (value?: string) => void
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ export const CodeEditor = ({
|
||||
const monaco = useMonaco()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
|
||||
const hasValue = useRef<any>()
|
||||
const ref = useRef<editor.IStandaloneCodeEditor>()
|
||||
const hasValue = useRef<editor.IContextKey<boolean>>(null)
|
||||
const ref = useRef<editor.IStandaloneCodeEditor>(null)
|
||||
const editorRef = editorRefProps || ref
|
||||
const monacoRef = useRef<Monaco>()
|
||||
const monacoRef = useRef<Monaco>(null)
|
||||
|
||||
const { runQuery, placeholderFill, formatDocument, explainCode, closeAssistant } = {
|
||||
...DEFAULT_ACTIONS,
|
||||
@@ -175,7 +175,9 @@ export const CodeEditor = ({
|
||||
}
|
||||
|
||||
const onChangeContent: OnChange = (value) => {
|
||||
hasValue.current.set((value ?? '').length > 0)
|
||||
if (hasValue.current) {
|
||||
hasValue.current.set((value ?? '').length > 0)
|
||||
}
|
||||
setShowPlaceholder(!value)
|
||||
onInputChange(value)
|
||||
}
|
||||
@@ -191,7 +193,9 @@ export const CodeEditor = ({
|
||||
monacoRef.current !== undefined
|
||||
) {
|
||||
const editor = editorRef.current
|
||||
if (editor == null) return
|
||||
const monaco = monacoRef.current
|
||||
if (monaco == null) return
|
||||
|
||||
editor.addCommand(
|
||||
monaco.KeyCode.Tab,
|
||||
|
||||
@@ -14,7 +14,7 @@ export function DebouncedComponent({
|
||||
children,
|
||||
}: DebouncedComponentProps) {
|
||||
const [shouldRender, setShouldRender] = useState(false)
|
||||
const timeoutRef = useRef<NodeJS.Timeout>()
|
||||
const timeoutRef = useRef<NodeJS.Timeout>(null)
|
||||
const prevValueRef = useRef(value)
|
||||
const isInitialMount = useRef(true)
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ export const EditorPanel = () => {
|
||||
const [showResults, setShowResults] = useState(true)
|
||||
const [isSaveDialogOpen, setIsSaveDialogOpen] = useState(false)
|
||||
const [saveStatus, setSaveStatus] = useState<'idle' | 'success' | 'error'>('idle')
|
||||
const saveStatusTimerRef = useRef<ReturnType<typeof setTimeout>>()
|
||||
const saveStatusTimerRef = useRef<ReturnType<typeof setTimeout>>(null)
|
||||
const originalSnippetRef = useRef<{ sql: string; name: string } | null>(null)
|
||||
|
||||
const refocusEditor = () => {
|
||||
@@ -145,7 +145,9 @@ export const EditorPanel = () => {
|
||||
|
||||
const showSaveSuccess = () => {
|
||||
setSaveStatus('success')
|
||||
clearTimeout(saveStatusTimerRef.current)
|
||||
if (saveStatusTimerRef.current) {
|
||||
clearTimeout(saveStatusTimerRef.current)
|
||||
}
|
||||
saveStatusTimerRef.current = setTimeout(() => setSaveStatus('idle'), 2000)
|
||||
}
|
||||
const [isTemplatesOpen, setIsTemplatesOpen] = useState(false)
|
||||
|
||||
@@ -28,7 +28,7 @@ const SqlEditor = ({
|
||||
onInputChange = noop,
|
||||
}: SqlEditorProps) => {
|
||||
const monaco = useMonaco()
|
||||
const editorRef = useRef<any>()
|
||||
const editorRef = useRef<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (monaco) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
export function useChanged<T>(value: T): boolean {
|
||||
const prev = useRef<T>()
|
||||
const prev = useRef<T>(null)
|
||||
const changed = prev.current !== value
|
||||
|
||||
useEffect(() => {
|
||||
@@ -12,7 +12,7 @@ export function useChanged<T>(value: T): boolean {
|
||||
}
|
||||
|
||||
export function useChangedSync<T>(value: T): boolean {
|
||||
const prev = useRef<T>()
|
||||
const prev = useRef<T>(null)
|
||||
const changed = prev.current !== value
|
||||
prev.current = value
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export const LogsExplorerPage: NextPageWithLayout = () => {
|
||||
else return TEMPLATES.filter((x) => x.label !== 'Metadata IP')
|
||||
}, [logsShowMetadataIpTemplate])
|
||||
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor>()
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor>(null)
|
||||
const [editorId] = useState<string>(uuidv4())
|
||||
const { search, setSearch, timestampStart, timestampEnd, setTimeRange } = useLogsUrlState()
|
||||
const defaultHelper = useMemo(() => getDefaultHelper(EXPLORER_DATEPICKER_HELPERS), [])
|
||||
|
||||
@@ -27,7 +27,7 @@ const JWTKeysLegacyPage: NextPageWithLayout = () => {
|
||||
const jwtSecretUpdateStatus = data?.jwtSecretUpdateStatus
|
||||
const jwtSecretUpdateError = data?.jwtSecretUpdateError
|
||||
|
||||
const previousJwtSecretUpdateStatus = useRef<JwtSecretUpdateStatus>()
|
||||
const previousJwtSecretUpdateStatus = useRef<JwtSecretUpdateStatus>(undefined)
|
||||
const { Failed, Updated, Updating } = JwtSecretUpdateStatus
|
||||
const jwtSecretUpdateErrorMessage =
|
||||
JWT_SECRET_UPDATE_ERROR_MESSAGES[jwtSecretUpdateError as JwtSecretUpdateError]
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
"ui": "workspace:*",
|
||||
"ui-patterns": "workspace:*",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vaul": "^0.9.6",
|
||||
"vaul": "^1.1.2",
|
||||
"y-monaco": "^0.1.6",
|
||||
"y-protocols": "^1.0.7",
|
||||
"yjs": "^13.6.29",
|
||||
|
||||
@@ -14,7 +14,7 @@ import { SurveyChapterSection } from './components/SurveyChapterSection'
|
||||
import { SurveySectionBreak } from './components/SurveySectionBreak'
|
||||
|
||||
interface FloatingTocProps {
|
||||
tocRef: RefObject<HTMLDivElement>
|
||||
tocRef: RefObject<HTMLDivElement | null>
|
||||
isTocOpen: boolean
|
||||
setIsTocOpen: (open: boolean) => void
|
||||
activeChapter: number
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import createGlobe from 'cobe'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { debounce } from 'lib/helpers'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
|
||||
const Globe = () => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
const canvasRef = useRef<any>()
|
||||
const canvasRef = useRef<any | null>(null)
|
||||
|
||||
let rotation: number = 0
|
||||
let width: number = 0
|
||||
|
||||
@@ -2,20 +2,21 @@ import 'swiper/css'
|
||||
import 'swiper/css/a11y'
|
||||
import 'swiper/css/navigation'
|
||||
import 'swiper/css/controller'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useBreakpoint } from 'common'
|
||||
import SectionContainer from 'components/Layouts/SectionContainer'
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Button, cn } from 'ui'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { Swiper, SwiperClass, SwiperRef, SwiperSlide } from 'swiper/react'
|
||||
import { Controller, Navigation, A11y } from 'swiper/modules'
|
||||
|
||||
import SectionContainer from 'components/Layouts/SectionContainer'
|
||||
import { mainDays, WeekDayProps } from './data'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useWindowSize } from 'react-use'
|
||||
import { useBreakpoint } from 'common'
|
||||
import { A11y, Controller, Navigation } from 'swiper/modules'
|
||||
import { Swiper, SwiperClass, SwiperRef, SwiperSlide } from 'swiper/react'
|
||||
import { Button, cn } from 'ui'
|
||||
|
||||
import { mainDays, WeekDayProps } from './data'
|
||||
import { DayLink } from './lw15.components'
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
|
||||
const LW15MainStage = ({ className }: { className?: string }) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
@@ -181,7 +182,7 @@ const CardBG = ({ day }: { day: WeekDayProps }) => (
|
||||
interface Props {
|
||||
className?: string
|
||||
slides: WeekDayProps[]
|
||||
swiperRef: React.RefObject<SwiperRef>
|
||||
swiperRef: React.RefObject<SwiperRef | null>
|
||||
setControlledSwiper: (swiper: SwiperClass) => void
|
||||
controlledSwiper: SwiperClass | null
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LW15_TWEET_TEXT, LW15_URL } from '~/lib/constants'
|
||||
import { useBreakpoint } from 'common'
|
||||
import dayjs from 'dayjs'
|
||||
import { Check } from 'lucide-react'
|
||||
@@ -5,8 +6,8 @@ import { useTheme } from 'next-themes'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import useConfData from '../hooks/use-conf-data'
|
||||
import { LW15_TWEET_TEXT, LW15_URL } from '~/lib/constants'
|
||||
import supabase from '../supabase'
|
||||
|
||||
export default function LW15TicketShare() {
|
||||
@@ -17,7 +18,7 @@ export default function LW15TicketShare() {
|
||||
const [_imgReady, setImgReady] = useState(false)
|
||||
const [_loading, setLoading] = useState(false)
|
||||
const isLessThanMd = useBreakpoint()
|
||||
const downloadLink = useRef<HTMLAnchorElement>()
|
||||
const downloadLink = useRef<HTMLAnchorElement | undefined>(undefined)
|
||||
const link = `${LW15_URL}/tickets/${username}?lw=15&t=${dayjs(new Date()).format('DHHmmss')}`
|
||||
const permalink = encodeURIComponent(link)
|
||||
const text = LW15_TWEET_TEXT
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import React from 'react'
|
||||
|
||||
function getRandomNumber(min: number, max: number) {
|
||||
var range = max - min
|
||||
@@ -10,7 +10,7 @@ function getRandomNumber(min: number, max: number) {
|
||||
|
||||
const DeployGlobally = ({ isHovered }: { isHovered: boolean }) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
const ref = React.useRef<any>()
|
||||
const ref = React.useRef<any | null>(null)
|
||||
const states = ['1', '2', '3']
|
||||
const colors = {
|
||||
green: resolvedTheme?.includes('dark') ? '#1CF7C3' : '#00B99F',
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { DEFAULT_EASE } from '~/lib/animations'
|
||||
import { LazyMotion, domAnimation, m } from 'framer-motion'
|
||||
import { domAnimation, LazyMotion, m } from 'framer-motion'
|
||||
import { useTheme } from 'next-themes'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
|
||||
const PGvectorImg = ({ isHovered }: { isHovered: boolean }) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
const svgRef = useRef<any>()
|
||||
const svgRef = useRef<any | null>(null)
|
||||
|
||||
const colors = {
|
||||
circles1: resolvedTheme?.includes('dark') ? '#105C3B' : '#008B4F',
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { DEFAULT_EASE } from '~/lib/animations'
|
||||
import { useTheme } from 'next-themes'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
|
||||
const SecureAndScalableImg = ({ isHovered }: { isHovered: boolean }) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
const shieldsRef = useRef<any>()
|
||||
const mainRef = useRef<any>()
|
||||
const shieldsRef = useRef<any | null>(null)
|
||||
const mainRef = useRef<any | null>(null)
|
||||
|
||||
const colors = {
|
||||
shieldsStroke: resolvedTheme?.includes('dark') ? '#494949' : '#D5D5D5',
|
||||
|
||||
@@ -19,7 +19,7 @@ export function AnimatedCounter({
|
||||
const countRef = useRef(value)
|
||||
const lastUpdateRef = useRef(0)
|
||||
const isVisibleRef = useRef(false)
|
||||
const rafIdRef = useRef<number>()
|
||||
const rafIdRef = useRef<number | undefined>(undefined)
|
||||
|
||||
const formatNumber = useCallback(
|
||||
(num: number) => {
|
||||
|
||||
@@ -40,7 +40,7 @@ function mdxComponents(callback: Dispatch<SetStateAction<string | null>>) {
|
||||
img: (
|
||||
props: React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>
|
||||
) => {
|
||||
return <img {...props} onClick={() => callback(props.src!)} />
|
||||
return <img {...props} onClick={() => callback(props.src!.toString())} />
|
||||
},
|
||||
}
|
||||
|
||||
@@ -192,6 +192,7 @@ function Partner({ partner, overview }: PartnerData) {
|
||||
{'error' in overview ? (
|
||||
<p>Error rendering integration page: {overview.error.message}</p>
|
||||
) : (
|
||||
// @ts-expect-error: Gildas - This is because CodeHide put its components under the CH namespace. It works but the MDXClient types are stricter
|
||||
<MDXClient {...overview} components={mdxComponents(setFocusedImage)} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ type ResultBox<T> = { v: T }
|
||||
* React hook for creating a value exactly once
|
||||
*/
|
||||
export function useConstant<T>(fn: () => T): T {
|
||||
const ref = useRef<ResultBox<T>>()
|
||||
const ref = useRef<ResultBox<T> | null>(null)
|
||||
|
||||
if (!ref.current) {
|
||||
ref.current = { v: fn() }
|
||||
|
||||
@@ -4,7 +4,7 @@ import { debounce } from 'lodash'
|
||||
import { useEffect, useMemo, useRef } from 'react'
|
||||
|
||||
export const useDebounce = (callback: () => void, delay: number = 1000) => {
|
||||
const ref = useRef<any>()
|
||||
const ref = useRef<any | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = callback
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useCallback, useEffect, useId, useMemo, useReducer, useRef } from 'reac
|
||||
const useSearchParamsShallow = () => {
|
||||
const EVENT_NAME = 'supabase.events.packages.common.useSearchParamsShallow'
|
||||
const id = useId()
|
||||
const timeoutHandle = useRef<ReturnType<typeof setTimeout>>()
|
||||
const timeoutHandle = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
|
||||
|
||||
const reducer = useCallback(
|
||||
(_: URLSearchParams, action: { target: 'int' | 'ext'; newParams: URLSearchParams }) => {
|
||||
|
||||
@@ -2,14 +2,14 @@ import {
|
||||
createElement,
|
||||
forwardRef,
|
||||
ForwardRefExoticComponent,
|
||||
ReactSVG,
|
||||
RefAttributes,
|
||||
SVGElementType,
|
||||
SVGProps,
|
||||
} from 'react'
|
||||
|
||||
import defaultAttributes from './defaultAttributes'
|
||||
|
||||
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][]
|
||||
export type IconNode = [elementName: SVGElementType, attrs: Record<string, string>][]
|
||||
|
||||
export type SVGAttributes = Partial<SVGProps<SVGSVGElement>>
|
||||
type ComponentAttributes = RefAttributes<SVGSVGElement> & SVGAttributes
|
||||
|
||||
@@ -8,7 +8,7 @@ import { cn } from 'ui/src/lib/utils'
|
||||
|
||||
export interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
|
||||
/* The ref for the textarea, optional. Exposed for the CommandsPopover to attach events. */
|
||||
textAreaRef?: React.RefObject<HTMLTextAreaElement>
|
||||
textAreaRef?: React.RefObject<HTMLTextAreaElement | null>
|
||||
/* The loading state of the form */
|
||||
loading: boolean
|
||||
/* The disabled state of the form */
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
import { Popover as _RadixPopover } from 'radix-ui'
|
||||
import {
|
||||
ComponentPropsWithoutRef,
|
||||
ComponentRef,
|
||||
createRef,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useRef,
|
||||
@@ -24,12 +24,12 @@ import { Popover, PopoverAnchor, PopoverContent } from 'ui/src/components/shadcn
|
||||
import { cn } from 'ui/src/lib/utils/cn'
|
||||
|
||||
const AssistantCommandsPopover = forwardRef<
|
||||
ElementRef<typeof Popover>,
|
||||
ComponentRef<typeof Popover>,
|
||||
ComponentPropsWithoutRef<typeof Popover> & {
|
||||
/* The children to render - this is where the AssistantChatForm should be placed */
|
||||
children: React.ReactNode
|
||||
/* The ref for the textarea - used with the AssistantChatForm */
|
||||
textAreaRef: React.RefObject<HTMLTextAreaElement>
|
||||
textAreaRef: React.RefObject<HTMLTextAreaElement | null>
|
||||
/* The function to handle the value change */
|
||||
setValue: (value: string) => void
|
||||
/* The value of the textarea */
|
||||
|
||||
@@ -15,7 +15,7 @@ function useFocusInputOnWiderScreens(ref: React.ForwardedRef<HTMLInputElement>)
|
||||
const isBelowSmSynchronous = useRef(isBelowSm)
|
||||
isBelowSmSynchronous.current = isBelowSm
|
||||
|
||||
const internalRef = useRef<HTMLInputElement>()
|
||||
const internalRef = useRef<HTMLInputElement | null>(null)
|
||||
const combinedRef = (element: HTMLInputElement) => {
|
||||
if (ref instanceof Function) {
|
||||
ref(element)
|
||||
|
||||
@@ -32,7 +32,7 @@ const useRegisterCommands = (
|
||||
options.enabled ??= true
|
||||
const prevEnabled = useRef<boolean | undefined>(options.enabled)
|
||||
|
||||
const unsubscribe = useRef<() => void>()
|
||||
const unsubscribe = useRef<(() => void) | undefined>(undefined)
|
||||
|
||||
/**
|
||||
* useEffect handles the registration on first render, since React runs the
|
||||
|
||||
@@ -110,7 +110,7 @@ interface UseAiChatOptions {
|
||||
}
|
||||
|
||||
const useAiChat = ({ messageTemplate = (message) => message, setIsLoading }: UseAiChatOptions) => {
|
||||
const eventSourceRef = useRef<SSE>()
|
||||
const eventSourceRef = useRef<SSE | undefined>(undefined)
|
||||
const messageIdempotencyKey = useRef(0)
|
||||
|
||||
const [isResponding, setIsResponding] = useState(false)
|
||||
|
||||
@@ -71,7 +71,7 @@ export type FilterBarContextValue = {
|
||||
actions?: FilterBarAction[]
|
||||
icon?: React.ReactNode
|
||||
|
||||
rootRef: React.RefObject<HTMLDivElement>
|
||||
rootRef: React.RefObject<HTMLDivElement | null>
|
||||
}
|
||||
|
||||
const FilterBarContext = createContext<FilterBarContextValue | null>(null)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useLayoutEffect, useState } from 'react'
|
||||
|
||||
export const useMeasuredWidth = <T extends HTMLElement>(ref: React.RefObject<T>) => {
|
||||
export const useMeasuredWidth = <T extends HTMLElement>(ref: React.RefObject<T | null>) => {
|
||||
const [measuredWidth, setMeasuredWidth] = useState<number | null>(null)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LazyMotion, m } from 'framer-motion'
|
||||
import { PropsWithChildren } from 'react'
|
||||
import React, { PropsWithChildren } from 'react'
|
||||
import { createElement, SyntaxHighlighterProps } from 'react-syntax-highlighter'
|
||||
|
||||
// Make sure to return the specific export containing the feature bundle.
|
||||
@@ -7,7 +7,7 @@ const loadFramerFeatures = () => import('./framer-features').then((res) => res.d
|
||||
|
||||
type RendererElementNode = {
|
||||
type: 'element'
|
||||
tagName: keyof JSX.IntrinsicElements | React.ComponentType<any>
|
||||
tagName: keyof React.JSX.IntrinsicElements | React.ComponentType<any>
|
||||
properties: { className: any[]; [key: string]: any }
|
||||
children: RendererNode[]
|
||||
}
|
||||
@@ -25,7 +25,7 @@ export type WrapperProps = PropsWithChildren<{
|
||||
|
||||
export type TransformRendererProps = {
|
||||
search: (text: string) => boolean
|
||||
wrapper: ({ children, text }: WrapperProps) => JSX.Element
|
||||
wrapper: ({ children, text }: WrapperProps) => React.JSX.Element
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,7 +235,7 @@ function splitSpaceElements(nodes: RendererNode[]) {
|
||||
* that the syntax highlighter can work with.
|
||||
*/
|
||||
function elementToRendererElementNode(
|
||||
element: ({ children, text }: WrapperProps) => JSX.Element,
|
||||
element: ({ children, text }: WrapperProps) => React.JSX.Element,
|
||||
text: string
|
||||
): RendererElementNode {
|
||||
const {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useSnapshot } from 'valtio'
|
||||
import { ConsentToast } from './ConsentToast'
|
||||
|
||||
export const useConsentToast = () => {
|
||||
const consentToastId = useRef<string | number>()
|
||||
const consentToastId = useRef<string | number | undefined>(undefined)
|
||||
const snap = useSnapshot(consentState)
|
||||
|
||||
const acceptAll = useCallback(() => {
|
||||
|
||||
@@ -4,15 +4,20 @@ import { cva, VariantProps } from 'class-variance-authority'
|
||||
import { Check, ChevronsUpDown, X as RemoveIcon } from 'lucide-react'
|
||||
// @ts-ignore Required to avoid TS error: The inferred type of MultiSelectorContent cannot be named without a reference to @radix-ui
|
||||
import type { Popover as PopoverPrimitive } from 'radix-ui'
|
||||
import React, { useEffect } from 'react'
|
||||
import { Badge, cn, Popover, PopoverAnchor, PopoverContent, PopoverContentProps } from 'ui'
|
||||
import React, { isValidElement, ReactElement, useEffect } from 'react'
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from 'ui/src/components/shadcn/ui/command'
|
||||
Badge,
|
||||
cn,
|
||||
Command_Shadcn_ as Command,
|
||||
CommandEmpty_Shadcn_ as CommandEmpty,
|
||||
CommandInput_Shadcn_ as CommandInput,
|
||||
CommandItem_Shadcn_ as CommandItem,
|
||||
CommandList_Shadcn_ as CommandList,
|
||||
Popover,
|
||||
PopoverAnchor,
|
||||
PopoverContent,
|
||||
PopoverContentProps,
|
||||
} from 'ui'
|
||||
import { SIZE_VARIANTS, SIZE_VARIANTS_DEFAULT } from 'ui/src/lib/constants'
|
||||
|
||||
interface MultiSelectContextProps {
|
||||
@@ -504,7 +509,9 @@ const MultiSelectorList = React.forwardRef<
|
||||
const options = !!children
|
||||
? Array.isArray(children)
|
||||
? (children as React.ReactNode[])
|
||||
: typeof children === 'object' && 'props' in children
|
||||
: typeof children === 'object' &&
|
||||
'props' in children &&
|
||||
isValidElement<{ children: ReactElement[] }>(children)
|
||||
? children.props.children
|
||||
: []
|
||||
: []
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"sonner": "^1.5.0",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "catalog:",
|
||||
"vaul": "^0.9.9"
|
||||
"vaul": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.6.0",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { cva, VariantProps } from 'class-variance-authority'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { Slot } from 'radix-ui'
|
||||
import { cloneElement, forwardRef, isValidElement } from 'react'
|
||||
import { cloneElement, forwardRef, isValidElement, ReactNode } from 'react'
|
||||
|
||||
import { SIZE_VARIANTS, SIZE_VARIANTS_DEFAULT } from '../../lib/constants'
|
||||
import { cn } from '../../lib/utils/cn'
|
||||
@@ -268,7 +268,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
}}
|
||||
>
|
||||
{asChild ? (
|
||||
isValidElement(children) ? (
|
||||
isValidElement<{ children: ReactNode }>(children) ? (
|
||||
cloneElement(
|
||||
children,
|
||||
undefined,
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface ExpandingTextAreaProps extends React.TextareaHTMLAttributes<HTM
|
||||
/**
|
||||
* This is a custom TextArea component that expands based on the content.
|
||||
*/
|
||||
const ExpandingTextArea = forwardRef<HTMLTextAreaElement, ExpandingTextAreaProps>(
|
||||
const ExpandingTextArea = forwardRef<HTMLTextAreaElement | null, ExpandingTextAreaProps>(
|
||||
({ className, value, ...props }, ref) => {
|
||||
const internalRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const ShadowScrollArea = React.forwardRef<HTMLDivElement, ShadowScrollAreaProps>
|
||||
{ className, containerClassName, children, stickyLastColumn, outerContainerRef, ...props },
|
||||
_ref
|
||||
) => {
|
||||
const containerRef = React.useRef<HTMLDivElement>(null)
|
||||
const containerRef = React.useRef<HTMLDivElement | null>(null)
|
||||
const { hasHorizontalScroll, canScrollLeft, canScrollRight } = useHorizontalScroll(containerRef)
|
||||
|
||||
const stickyColumnShadow = cn(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export const useHorizontalScroll = (ref: React.RefObject<HTMLDivElement>) => {
|
||||
export const useHorizontalScroll = (ref: React.RefObject<HTMLDivElement | null>) => {
|
||||
const [hasHorizontalScroll, setHasHorizontalScroll] = React.useState(false)
|
||||
const [canScrollLeft, setCanScrollLeft] = React.useState(false)
|
||||
const [canScrollRight, setCanScrollRight] = React.useState(false)
|
||||
|
||||
Generated
+1390
-1406
File diff suppressed because it is too large.
Load diff
+4
-4
@@ -14,8 +14,8 @@ catalog:
|
||||
'@supabase/ssr': 0.10.2
|
||||
'@supabase/supabase-js': 2.105.4
|
||||
'@types/node': ^22.0.0
|
||||
'@types/react': ^18.3.0
|
||||
'@types/react-dom': ^18.3.0
|
||||
'@types/react': ^19.2.14
|
||||
'@types/react-dom': ^19.2.3
|
||||
'@vitejs/plugin-react': ^6.0.1
|
||||
'@vitest/coverage-v8': ^4.1.4
|
||||
'@vitest/ui': ^4.1.4
|
||||
@@ -25,8 +25,8 @@ catalog:
|
||||
next-themes: ^0.4.6
|
||||
postcss: ^8.5.10
|
||||
radix-ui: ^1.4.3
|
||||
react: ^18.3.0
|
||||
react-dom: ^18.3.0
|
||||
react: ^19.2.6
|
||||
react-dom: ^19.2.6
|
||||
recharts: ^2.15.4
|
||||
tailwindcss: ^4.2.4
|
||||
tsx: 4.20.3
|
||||
|
||||
Reference in new issue
Block a user