mirror of
https://github.com/langgenius/dify.git
synced 2026-06-14 04:31:07 +08:00
fix(dify-ui): restore pagination jump focus (#37393)
This commit is contained in:
parent
5d77c0af08
commit
ca3cb2a902
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogActions,
|
||||
@ -84,8 +84,8 @@ export const NonDestructive: Story = {
|
||||
}
|
||||
|
||||
const ControlledDemo = () => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [count, setCount] = useState(0)
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const [count, setCount] = React.useState(0)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
@ -130,8 +130,8 @@ export const Controlled: Story = {
|
||||
}
|
||||
|
||||
const LoadingConfirmDemo = () => {
|
||||
const [pending, setPending] = useState(false)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [pending, setPending] = React.useState(false)
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
const handleConfirm = () => {
|
||||
setPending(true)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ComponentPropsWithoutRef, ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { ButtonProps } from '../button'
|
||||
import { AlertDialog as BaseAlertDialog } from '@base-ui/react/alert-dialog'
|
||||
import { Button } from '../button'
|
||||
@ -12,7 +12,7 @@ export const AlertDialogTitle = BaseAlertDialog.Title
|
||||
export const AlertDialogDescription = BaseAlertDialog.Description
|
||||
|
||||
type AlertDialogContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
backdropClassName?: string
|
||||
backdropProps?: Omit<BaseAlertDialog.Backdrop.Props, 'className'>
|
||||
@ -47,7 +47,7 @@ export function AlertDialogContent({
|
||||
)
|
||||
}
|
||||
|
||||
type AlertDialogActionsProps = ComponentPropsWithoutRef<'div'>
|
||||
type AlertDialogActionsProps = React.ComponentProps<'div'>
|
||||
|
||||
export function AlertDialogActions({ className, ...props }: AlertDialogActionsProps) {
|
||||
return (
|
||||
@ -59,7 +59,7 @@ export function AlertDialogActions({ className, ...props }: AlertDialogActionsPr
|
||||
}
|
||||
|
||||
type AlertDialogCancelButtonProps = Omit<ButtonProps, 'children'> & {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
closeProps?: Omit<BaseAlertDialog.Close.Props, 'children' | 'render'>
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
Autocomplete,
|
||||
@ -18,7 +18,7 @@ import {
|
||||
AutocompleteTrigger,
|
||||
} from '../index'
|
||||
|
||||
const renderWithSafeViewport = (ui: ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
@ -31,13 +31,13 @@ const renderAutocomplete = ({
|
||||
open = false,
|
||||
defaultValue = 'workflow',
|
||||
}: {
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
open?: boolean
|
||||
defaultValue?: string
|
||||
} = {}) => renderWithSafeViewport(
|
||||
<Autocomplete open={open} defaultValue={defaultValue} items={['workflow', 'dataset']}>
|
||||
{children ?? (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<AutocompleteInputGroup data-testid="input-group">
|
||||
<AutocompleteInput aria-label="Search suggestions" data-testid="input" />
|
||||
<AutocompleteClear data-testid="clear" />
|
||||
@ -65,7 +65,7 @@ const renderAutocomplete = ({
|
||||
</AutocompleteList>
|
||||
<AutocompleteEmpty data-testid="empty">No suggestions</AutocompleteEmpty>
|
||||
</AutocompleteContent>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Autocomplete>,
|
||||
)
|
||||
@ -150,7 +150,7 @@ describe('Autocomplete wrappers', () => {
|
||||
it('should rely on aria-labelledby when provided instead of injecting fallback labels', async () => {
|
||||
const screen = await renderAutocomplete({
|
||||
children: (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<span id="clear-label">Clear from label</span>
|
||||
<span id="trigger-label">Trigger from label</span>
|
||||
<AutocompleteInputGroup>
|
||||
@ -158,7 +158,7 @@ describe('Autocomplete wrappers', () => {
|
||||
<AutocompleteClear aria-labelledby="clear-label" />
|
||||
<AutocompleteTrigger aria-labelledby="trigger-label" />
|
||||
</AutocompleteInputGroup>
|
||||
</>
|
||||
</React.Fragment>
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { Virtualizer } from '@tanstack/react-virtual'
|
||||
import type { RefObject } from 'react'
|
||||
import { useVirtualizer } from '@tanstack/react-virtual'
|
||||
import { useEffect, useMemo, useRef, useState, useTransition } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Autocomplete,
|
||||
AutocompleteClear,
|
||||
@ -336,12 +335,12 @@ const LimitedStatus = ({
|
||||
}
|
||||
|
||||
const AsyncSearchDemo = () => {
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [searchResults, setSearchResults] = useState<Suggestion[]>([])
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const [searchValue, setSearchValue] = React.useState('')
|
||||
const [searchResults, setSearchResults] = React.useState<Suggestion[]>([])
|
||||
const [error, setError] = React.useState<string | null>(null)
|
||||
const [isPending, startTransition] = React.useTransition()
|
||||
const { contains } = useAutocompleteFilter()
|
||||
const abortControllerRef = useRef<AbortController | null>(null)
|
||||
const abortControllerRef = React.useRef<AbortController | null>(null)
|
||||
|
||||
const status = (() => {
|
||||
if (isPending)
|
||||
@ -419,9 +418,9 @@ const AsyncSearchDemo = () => {
|
||||
const VirtualizedSuggestionList = ({
|
||||
virtualizerRef,
|
||||
}: {
|
||||
virtualizerRef: RefObject<StoryVirtualizer | null>
|
||||
virtualizerRef: React.RefObject<StoryVirtualizer | null>
|
||||
}) => {
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null)
|
||||
const scrollRef = React.useRef<HTMLDivElement | null>(null)
|
||||
const filteredItems = useAutocompleteFilteredItems<Suggestion>()
|
||||
const virtualizer = useVirtualizer({
|
||||
count: filteredItems.length,
|
||||
@ -430,7 +429,7 @@ const VirtualizedSuggestionList = ({
|
||||
overscan: 6,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
virtualizerRef.current = virtualizer
|
||||
|
||||
return () => {
|
||||
@ -490,7 +489,7 @@ const FuzzyHighlight = ({
|
||||
text: string
|
||||
query: string
|
||||
}) => {
|
||||
const parts = useMemo(() => {
|
||||
const parts = React.useMemo(() => {
|
||||
const trimmed = query.trim()
|
||||
|
||||
if (!trimmed)
|
||||
@ -501,18 +500,18 @@ const FuzzyHighlight = ({
|
||||
}, [query, text])
|
||||
|
||||
return (
|
||||
<>
|
||||
<React.Fragment>
|
||||
{parts.map((part, index) => (
|
||||
part.toLowerCase() === query.trim().toLowerCase()
|
||||
? <mark key={`${part}-${index}`} className="bg-transparent text-text-accent">{part}</mark>
|
||||
: part
|
||||
))}
|
||||
</>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const FuzzyMatchingDemo = () => {
|
||||
const [value, setValue] = useState('retr')
|
||||
const [value, setValue] = React.useState('retr')
|
||||
const { contains } = useAutocompleteFilter({ sensitivity: 'base' })
|
||||
|
||||
return (
|
||||
@ -702,7 +701,7 @@ export const CommandPalette: Story = {
|
||||
}
|
||||
|
||||
const VirtualizedLongSuggestionsDemo = () => {
|
||||
const virtualizerRef = useRef<StoryVirtualizer | null>(null)
|
||||
const virtualizerRef = React.useRef<StoryVirtualizer | null>(null)
|
||||
|
||||
return (
|
||||
<div className={inputWidth}>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { HTMLAttributes, ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { Placement } from '../placement'
|
||||
import { Autocomplete as BaseAutocomplete } from '@base-ui/react/autocomplete'
|
||||
import { cva } from 'class-variance-authority'
|
||||
@ -223,7 +223,7 @@ export function AutocompleteIcon({
|
||||
}
|
||||
|
||||
type AutocompleteContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
@ -302,7 +302,7 @@ export function AutocompleteItem({
|
||||
)
|
||||
}
|
||||
|
||||
export type AutocompleteItemTextProps = HTMLAttributes<HTMLSpanElement>
|
||||
export type AutocompleteItemTextProps = React.ComponentProps<'span'>
|
||||
|
||||
export function AutocompleteItemText({
|
||||
className,
|
||||
@ -368,7 +368,7 @@ export function AutocompleteItemIndicator({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: HTMLAttributes<HTMLSpanElement>) {
|
||||
}: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span
|
||||
className={cn(overlayIndicatorClassName, className)}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { FormEvent } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { userEvent } from 'vitest/browser'
|
||||
import { Button } from '../index'
|
||||
@ -174,7 +174,7 @@ describe('Button', () => {
|
||||
})
|
||||
|
||||
it('does not submit a form when a loading submit button is clicked', async () => {
|
||||
const onSubmit = vi.fn((event: FormEvent<HTMLFormElement>) => event.preventDefault())
|
||||
const onSubmit = vi.fn((event: React.FormEvent<HTMLFormElement>) => event.preventDefault())
|
||||
const screen = await render(
|
||||
<form onSubmit={onSubmit}>
|
||||
<Button type="submit" loading>Submit</Button>
|
||||
@ -187,7 +187,7 @@ describe('Button', () => {
|
||||
})
|
||||
|
||||
it('does not implicitly submit a form through a loading submit button', async () => {
|
||||
const onSubmit = vi.fn((event: FormEvent<HTMLFormElement>) => event.preventDefault())
|
||||
const onSubmit = vi.fn((event: React.FormEvent<HTMLFormElement>) => event.preventDefault())
|
||||
const screen = await render(
|
||||
<form onSubmit={onSubmit}>
|
||||
<label htmlFor="name">Name</label>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import * as React from 'react'
|
||||
|
||||
import { Button } from '.'
|
||||
|
||||
@ -112,10 +113,10 @@ export const WithIcon: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
children: (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<span aria-hidden className="mr-1.5 i-ri-rocket-line size-4 shrink-0" />
|
||||
Launch
|
||||
</>
|
||||
</React.Fragment>
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { Checkbox } from '../../checkbox'
|
||||
import { FieldItem, FieldLabel, FieldRoot } from '../../field'
|
||||
@ -10,7 +10,7 @@ const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElem
|
||||
describe('CheckboxGroup', () => {
|
||||
it('should manage selected values and parent mixed state', async () => {
|
||||
function PermissionsDemo() {
|
||||
const [value, setValue] = useState(['read'])
|
||||
const [value, setValue] = React.useState(['read'])
|
||||
|
||||
return (
|
||||
<CheckboxGroup value={value} onValueChange={setValue} allValues={['read', 'write']}>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useId, useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import { CheckboxGroup } from '.'
|
||||
import { Checkbox } from '../checkbox'
|
||||
import {
|
||||
@ -29,8 +29,8 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
function DocumentSelectionDemo() {
|
||||
const documentIds = ['doc-1', 'doc-2', 'doc-3']
|
||||
const [selected, setSelected] = useState<string[]>(['doc-1'])
|
||||
const groupLabelId = useId()
|
||||
const [selected, setSelected] = React.useState<string[]>(['doc-1'])
|
||||
const groupLabelId = React.useId()
|
||||
|
||||
return (
|
||||
<CheckboxGroup
|
||||
@ -77,7 +77,7 @@ function DynamicFormFieldDemo() {
|
||||
{ value: 'pdf', label: 'PDF' },
|
||||
{ value: 'html', label: 'HTML' },
|
||||
]
|
||||
const [selected, setSelected] = useState<string[]>(['markdown'])
|
||||
const [selected, setSelected] = React.useState<string[]>(['markdown'])
|
||||
|
||||
return (
|
||||
<FieldRoot name="allowed_file_types" className="flex w-80 flex-col gap-2">
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxSkeleton,
|
||||
@ -42,8 +41,8 @@ const meta = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
function CheckboxDemo(args: Partial<ComponentProps<typeof Checkbox>>) {
|
||||
const [checked, setChecked] = useState(args.checked ?? false)
|
||||
function CheckboxDemo(args: Partial<React.ComponentProps<typeof Checkbox>>) {
|
||||
const [checked, setChecked] = React.useState(args.checked ?? false)
|
||||
|
||||
return (
|
||||
<label className="flex items-center gap-2 system-sm-medium text-text-secondary">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { Checkbox as BaseCheckboxNS } from '@base-ui/react/checkbox'
|
||||
import type { HTMLAttributes } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -82,7 +82,7 @@ export function Checkbox({
|
||||
}
|
||||
|
||||
export type CheckboxSkeletonProps
|
||||
= Omit<HTMLAttributes<HTMLDivElement>, 'className'>
|
||||
= Omit<React.ComponentProps<'div'>, 'className'>
|
||||
& {
|
||||
className?: string
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ function RecoveryKeys({
|
||||
panelProps?: React.ComponentProps<typeof CollapsiblePanel>
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<CollapsibleTrigger className={triggerClassName}>
|
||||
Recovery keys
|
||||
<TriggerIcon />
|
||||
@ -58,7 +58,7 @@ function RecoveryKeys({
|
||||
<div>horse-battery-staple</div>
|
||||
</div>
|
||||
</CollapsiblePanel>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
Combobox,
|
||||
@ -24,7 +24,7 @@ import {
|
||||
ComboboxValue,
|
||||
} from '../index'
|
||||
|
||||
const renderWithSafeViewport = (ui: ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
@ -36,12 +36,12 @@ const renderSelectLikeCombobox = ({
|
||||
children,
|
||||
open = false,
|
||||
}: {
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
open?: boolean
|
||||
} = {}) => renderWithSafeViewport(
|
||||
<Combobox open={open} defaultValue="workflow" items={['workflow', 'dataset']}>
|
||||
{children ?? (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<ComboboxLabel data-testid="label">Resource type</ComboboxLabel>
|
||||
<ComboboxTrigger aria-label="Resource type" data-testid="trigger">
|
||||
<ComboboxValue placeholder="Select resource" />
|
||||
@ -68,7 +68,7 @@ const renderSelectLikeCombobox = ({
|
||||
</ComboboxList>
|
||||
<ComboboxEmpty data-testid="empty">No options</ComboboxEmpty>
|
||||
</ComboboxContent>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Combobox>,
|
||||
)
|
||||
@ -77,12 +77,12 @@ const renderInputCombobox = ({
|
||||
children,
|
||||
open = false,
|
||||
}: {
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
open?: boolean
|
||||
} = {}) => renderWithSafeViewport(
|
||||
<Combobox open={open} defaultValue="workflow" items={['workflow', 'dataset']}>
|
||||
{children ?? (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<ComboboxInputGroup data-testid="input-group">
|
||||
<ComboboxInput aria-label="Search resources" data-testid="input" />
|
||||
<ComboboxClear data-testid="clear" />
|
||||
@ -96,7 +96,7 @@ const renderInputCombobox = ({
|
||||
</ComboboxItem>
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Combobox>,
|
||||
)
|
||||
@ -208,7 +208,7 @@ describe('Combobox wrappers', () => {
|
||||
it('should rely on aria-labelledby when provided instead of injecting fallback labels', async () => {
|
||||
const screen = await renderInputCombobox({
|
||||
children: (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<span id="clear-label">Clear from label</span>
|
||||
<span id="trigger-label">Trigger from label</span>
|
||||
<ComboboxInputGroup>
|
||||
@ -216,7 +216,7 @@ describe('Combobox wrappers', () => {
|
||||
<ComboboxClear aria-labelledby="clear-label" />
|
||||
<ComboboxInputTrigger aria-labelledby="trigger-label" />
|
||||
</ComboboxInputGroup>
|
||||
</>
|
||||
</React.Fragment>
|
||||
),
|
||||
})
|
||||
|
||||
@ -349,7 +349,7 @@ describe('Combobox wrappers', () => {
|
||||
<ComboboxChips className="custom-chips" data-testid="chips">
|
||||
<ComboboxValue>
|
||||
{(selectedValue: string[]) => (
|
||||
<>
|
||||
<React.Fragment>
|
||||
{selectedValue.map(item => (
|
||||
<ComboboxChip key={item} className="custom-chip">
|
||||
<span>{item}</span>
|
||||
@ -357,7 +357,7 @@ describe('Combobox wrappers', () => {
|
||||
</ComboboxChip>
|
||||
))}
|
||||
<ComboboxInput aria-label="Reviewers" />
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ComboboxValue>
|
||||
</ComboboxChips>
|
||||
@ -378,7 +378,7 @@ describe('Combobox wrappers', () => {
|
||||
<ComboboxChips>
|
||||
<ComboboxValue>
|
||||
{(selectedValue: string[]) => (
|
||||
<>
|
||||
<React.Fragment>
|
||||
{selectedValue.map(item => (
|
||||
<ComboboxChip key={item}>
|
||||
<span id="remove-maya">Remove Maya</span>
|
||||
@ -386,7 +386,7 @@ describe('Combobox wrappers', () => {
|
||||
</ComboboxChip>
|
||||
))}
|
||||
<ComboboxInput aria-label="Reviewers" />
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ComboboxValue>
|
||||
</ComboboxChips>
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { Virtualizer } from '@tanstack/react-virtual'
|
||||
import type { RefObject } from 'react'
|
||||
import { useVirtualizer } from '@tanstack/react-virtual'
|
||||
import { useEffect, useMemo, useRef, useState, useTransition } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxChip,
|
||||
@ -278,9 +277,9 @@ const GroupedToolList = () => {
|
||||
const VirtualizedModelList = ({
|
||||
virtualizerRef,
|
||||
}: {
|
||||
virtualizerRef: RefObject<StoryVirtualizer | null>
|
||||
virtualizerRef: React.RefObject<StoryVirtualizer | null>
|
||||
}) => {
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null)
|
||||
const scrollRef = React.useRef<HTMLDivElement | null>(null)
|
||||
const filteredItems = useComboboxFilteredItems<Option>()
|
||||
const virtualizer = useVirtualizer({
|
||||
count: filteredItems.length,
|
||||
@ -289,7 +288,7 @@ const VirtualizedModelList = ({
|
||||
overscan: 6,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
virtualizerRef.current = virtualizer
|
||||
|
||||
return () => {
|
||||
@ -345,8 +344,8 @@ const FilteredModelStatus = () => {
|
||||
}
|
||||
|
||||
const VirtualizedLongListDemo = () => {
|
||||
const [value, setValue] = useState<Option | null>(modelCatalogOptions[137]!)
|
||||
const virtualizerRef = useRef<StoryVirtualizer | null>(null)
|
||||
const [value, setValue] = React.useState<Option | null>(modelCatalogOptions[137]!)
|
||||
const virtualizerRef = React.useRef<StoryVirtualizer | null>(null)
|
||||
|
||||
return (
|
||||
<div className={fieldWidth}>
|
||||
@ -375,15 +374,15 @@ const VirtualizedLongListDemo = () => {
|
||||
}
|
||||
|
||||
const AsyncDirectoryDemo = () => {
|
||||
const [searchResults, setSearchResults] = useState<Option[]>([])
|
||||
const [selectedValue, setSelectedValue] = useState<Option | null>(null)
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const [searchResults, setSearchResults] = React.useState<Option[]>([])
|
||||
const [selectedValue, setSelectedValue] = React.useState<Option | null>(null)
|
||||
const [searchValue, setSearchValue] = React.useState('')
|
||||
const [error, setError] = React.useState<string | null>(null)
|
||||
const [isPending, startTransition] = React.useTransition()
|
||||
const { contains } = useComboboxFilter()
|
||||
const abortControllerRef = useRef<AbortController | null>(null)
|
||||
const abortControllerRef = React.useRef<AbortController | null>(null)
|
||||
const trimmedSearchValue = searchValue.trim()
|
||||
const items = useMemo(() => {
|
||||
const items = React.useMemo(() => {
|
||||
if (!selectedValue || searchResults.some(option => option.value === selectedValue.value))
|
||||
return searchResults
|
||||
|
||||
@ -477,18 +476,18 @@ const AsyncDirectoryDemo = () => {
|
||||
}
|
||||
|
||||
const AsyncReviewerDemo = () => {
|
||||
const [searchResults, setSearchResults] = useState<Option[]>([])
|
||||
const [selectedValues, setSelectedValues] = useState<Option[]>(defaultAsyncReviewers)
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [blockStartStatus, setBlockStartStatus] = useState(false)
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const [searchResults, setSearchResults] = React.useState<Option[]>([])
|
||||
const [selectedValues, setSelectedValues] = React.useState<Option[]>(defaultAsyncReviewers)
|
||||
const [searchValue, setSearchValue] = React.useState('')
|
||||
const [error, setError] = React.useState<string | null>(null)
|
||||
const [blockStartStatus, setBlockStartStatus] = React.useState(false)
|
||||
const [isPending, startTransition] = React.useTransition()
|
||||
const { contains } = useComboboxFilter()
|
||||
const abortControllerRef = useRef<AbortController | null>(null)
|
||||
const selectedValuesRef = useRef<Option[]>(defaultAsyncReviewers)
|
||||
const abortControllerRef = React.useRef<AbortController | null>(null)
|
||||
const selectedValuesRef = React.useRef<Option[]>(defaultAsyncReviewers)
|
||||
const trimmedSearchValue = searchValue.trim()
|
||||
|
||||
const items = useMemo(() => {
|
||||
const items = React.useMemo(() => {
|
||||
if (selectedValues.length === 0)
|
||||
return searchResults
|
||||
|
||||
@ -587,7 +586,7 @@ const AsyncReviewerDemo = () => {
|
||||
<ComboboxChips>
|
||||
<ComboboxValue>
|
||||
{(selectedValue: Option[]) => (
|
||||
<>
|
||||
<React.Fragment>
|
||||
{selectedValue.map(item => (
|
||||
<ComboboxChip key={item.value} aria-label={item.label}>
|
||||
<span className="max-w-32 truncate">{item.label}</span>
|
||||
@ -595,7 +594,7 @@ const AsyncReviewerDemo = () => {
|
||||
</ComboboxChip>
|
||||
))}
|
||||
<ComboboxInput placeholder={selectedValue.length ? '' : 'Search reviewers…'} className="min-w-24 px-1 py-0.5" />
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ComboboxValue>
|
||||
</ComboboxChips>
|
||||
@ -735,7 +734,7 @@ export const Grouped: Story = {
|
||||
}
|
||||
|
||||
const MultipleChipsDemo = () => {
|
||||
const [value, setValue] = useState<Option[]>(defaultReviewers)
|
||||
const [value, setValue] = React.useState<Option[]>(defaultReviewers)
|
||||
|
||||
return (
|
||||
<FieldRoot name="reviewers" className={fieldWidth}>
|
||||
@ -745,7 +744,7 @@ const MultipleChipsDemo = () => {
|
||||
<ComboboxChips>
|
||||
<ComboboxValue>
|
||||
{(selectedValue: Option[]) => (
|
||||
<>
|
||||
<React.Fragment>
|
||||
{selectedValue.map(item => (
|
||||
<ComboboxChip key={item.value}>
|
||||
<span className="max-w-32 truncate">{item.label}</span>
|
||||
@ -753,7 +752,7 @@ const MultipleChipsDemo = () => {
|
||||
</ComboboxChip>
|
||||
))}
|
||||
<ComboboxInput placeholder={selectedValue.length ? '' : 'Assign reviewers…'} className="min-w-24 px-1 py-0.5" />
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ComboboxValue>
|
||||
</ComboboxChips>
|
||||
@ -829,7 +828,7 @@ export const DisabledAndReadOnly: Story = {
|
||||
}
|
||||
|
||||
const ControlledDemo = () => {
|
||||
const [value, setValue] = useState<Option | null>(defaultTag)
|
||||
const [value, setValue] = React.useState<Option | null>(defaultTag)
|
||||
|
||||
return (
|
||||
<div className="flex w-80 flex-col items-start gap-3">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { HTMLAttributes, ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { Placement } from '../placement'
|
||||
import { Combobox as BaseCombobox } from '@base-ui/react/combobox'
|
||||
import { cva } from 'class-variance-authority'
|
||||
@ -80,7 +80,7 @@ type ComboboxTriggerProps
|
||||
& VariantProps<typeof comboboxTriggerVariants>
|
||||
& {
|
||||
className?: string
|
||||
icon?: ReactNode | false
|
||||
icon?: React.ReactNode | false
|
||||
}
|
||||
|
||||
export function ComboboxTrigger({
|
||||
@ -286,7 +286,7 @@ export function ComboboxIcon({
|
||||
}
|
||||
|
||||
type ComboboxContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
@ -365,7 +365,7 @@ export function ComboboxItem({
|
||||
)
|
||||
}
|
||||
|
||||
export type ComboboxItemTextProps = HTMLAttributes<HTMLSpanElement>
|
||||
export type ComboboxItemTextProps = React.ComponentProps<'span'>
|
||||
|
||||
export function ComboboxItemText({
|
||||
className,
|
||||
@ -383,7 +383,7 @@ export function ComboboxItemIndicator({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: Omit<BaseCombobox.ItemIndicator.Props, 'children'> & { children?: ReactNode }) {
|
||||
}: Omit<BaseCombobox.ItemIndicator.Props, 'children'> & { children?: React.ReactNode }) {
|
||||
return (
|
||||
<BaseCombobox.ItemIndicator
|
||||
className={cn(overlayIndicatorClassName, className)}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
ContextMenu,
|
||||
@ -11,7 +12,7 @@ import {
|
||||
ContextMenuTrigger,
|
||||
} from '../index'
|
||||
|
||||
const renderWithSafeViewport = (ui: import('react').ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuCheckboxItem,
|
||||
@ -100,7 +100,7 @@ export const WithGroupLabel: Story = {
|
||||
}
|
||||
|
||||
const WithRadioItemsDemo = () => {
|
||||
const [value, setValue] = useState('comfortable')
|
||||
const [value, setValue] = React.useState('comfortable')
|
||||
|
||||
return (
|
||||
<ContextMenu>
|
||||
@ -130,9 +130,9 @@ export const WithRadioItems: Story = {
|
||||
}
|
||||
|
||||
const WithCheckboxItemsDemo = () => {
|
||||
const [showToolbar, setShowToolbar] = useState(true)
|
||||
const [showSidebar, setShowSidebar] = useState(false)
|
||||
const [showStatusBar, setShowStatusBar] = useState(true)
|
||||
const [showToolbar, setShowToolbar] = React.useState(true)
|
||||
const [showSidebar, setShowSidebar] = React.useState(false)
|
||||
const [showStatusBar, setShowStatusBar] = React.useState(true)
|
||||
|
||||
return (
|
||||
<ContextMenu>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { OverlayItemVariant } from '../overlay-shared'
|
||||
import type { Placement } from '../placement'
|
||||
import { ContextMenu as BaseContextMenu } from '@base-ui/react/context-menu'
|
||||
@ -27,7 +27,7 @@ export type ContextMenuActions = BaseContextMenu.Root.Actions
|
||||
// Intentionally no public Backdrop export; Base UI handles context-menu modal dismissal internally.
|
||||
|
||||
type ContextMenuContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
@ -225,7 +225,7 @@ export function ContextMenuSubTrigger({
|
||||
}
|
||||
|
||||
type ContextMenuSubContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Dialog,
|
||||
DialogCloseButton,
|
||||
@ -95,7 +95,7 @@ export const WithoutCloseButton: Story = {
|
||||
}
|
||||
|
||||
const ControlledDemo = () => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
@ -148,7 +148,7 @@ type ApiExtensionFormValues = {
|
||||
}
|
||||
|
||||
const FormDialogDemo = () => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen} disablePointerDismissal>
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
'use client'
|
||||
|
||||
// z-index strategy (relies on root `isolation: isolate` in layout.tsx):
|
||||
// All @langgenius/dify-ui/* overlay primitives — z-50
|
||||
// Toast stays one layer above overlays at z-60.
|
||||
// Overlays share the same z-index; DOM order handles stacking when multiple are open.
|
||||
// This ensures overlays inside a Dialog (e.g. a Tooltip on a dialog button) render
|
||||
// above the dialog backdrop instead of being clipped by it.
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Dialog as BaseDialog } from '@base-ui/react/dialog'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -39,7 +32,7 @@ export function DialogCloseButton({
|
||||
}
|
||||
|
||||
type DialogContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
backdropClassName?: string
|
||||
backdropProps?: Omit<BaseDialog.Backdrop.Props, 'className'>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Drawer as BaseDrawer } from '@base-ui/react/drawer'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -90,7 +90,7 @@ export function DrawerContent({
|
||||
}
|
||||
|
||||
type DrawerCloseButtonProps = Omit<BaseDrawer.Close.Props, 'children'> & {
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function DrawerCloseButton({
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
DropdownMenu,
|
||||
@ -11,7 +12,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '../index'
|
||||
|
||||
const renderWithSafeViewport = (ui: import('react').ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
@ -133,7 +133,7 @@ export const WithSubmenu: Story = {
|
||||
}
|
||||
|
||||
const WithRadioItemsDemo = () => {
|
||||
const [value, setValue] = useState('comfortable')
|
||||
const [value, setValue] = React.useState('comfortable')
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@ -163,9 +163,9 @@ export const WithRadioItems: Story = {
|
||||
}
|
||||
|
||||
const WithCheckboxItemsDemo = () => {
|
||||
const [showToolbar, setShowToolbar] = useState(true)
|
||||
const [showSidebar, setShowSidebar] = useState(false)
|
||||
const [showStatusBar, setShowStatusBar] = useState(true)
|
||||
const [showToolbar, setShowToolbar] = React.useState(true)
|
||||
const [showSidebar, setShowSidebar] = React.useState(false)
|
||||
const [showStatusBar, setShowStatusBar] = React.useState(true)
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@ -252,8 +252,8 @@ export const WithLinkItems: Story = {
|
||||
}
|
||||
|
||||
const ComplexDemo = () => {
|
||||
const [sortOrder, setSortOrder] = useState('newest')
|
||||
const [showArchived, setShowArchived] = useState(false)
|
||||
const [sortOrder, setSortOrder] = React.useState('newest')
|
||||
const [showArchived, setShowArchived] = React.useState(false)
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { OverlayItemVariant } from '../overlay-shared'
|
||||
import type { Placement } from '../placement'
|
||||
import { Menu } from '@base-ui/react/menu'
|
||||
@ -89,7 +89,7 @@ export function DropdownMenuLabel({
|
||||
}
|
||||
|
||||
type DropdownMenuContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
@ -197,7 +197,7 @@ export function DropdownMenuSubTrigger({
|
||||
}
|
||||
|
||||
type DropdownMenuSubContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { Checkbox } from '../../checkbox'
|
||||
import { CheckboxGroup } from '../../checkbox-group'
|
||||
@ -96,7 +97,7 @@ describe('Field primitives', () => {
|
||||
|
||||
it('should apply design-system control sizes when requested', async () => {
|
||||
const screen = await render(
|
||||
<>
|
||||
<React.Fragment>
|
||||
<FieldRoot name="name">
|
||||
<FieldLabel>Name</FieldLabel>
|
||||
<FieldControl size="large" />
|
||||
@ -105,7 +106,7 @@ describe('Field primitives', () => {
|
||||
<FieldLabel>Alias</FieldLabel>
|
||||
<FieldControl size="small" />
|
||||
</FieldRoot>
|
||||
</>,
|
||||
</React.Fragment>,
|
||||
)
|
||||
|
||||
await expect.element(screen.getByRole('textbox', { name: 'Name' })).toHaveClass('rounded-[10px]', 'py-[7px]', 'system-md-regular')
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { FileTreeIconType } from '.'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
FileTreeBadge,
|
||||
FileTreeFile,
|
||||
@ -118,7 +117,7 @@ function FileTreeNodeRows({
|
||||
}
|
||||
|
||||
function ComposedFileTree() {
|
||||
const [selectedItemId, setSelectedItemId] = useState<string | null>('button')
|
||||
const [selectedItemId, setSelectedItemId] = React.useState<string | null>('button')
|
||||
|
||||
return (
|
||||
<FileTreeRoot
|
||||
@ -177,7 +176,7 @@ function ComposedFileTree() {
|
||||
}
|
||||
|
||||
function DataDrivenFileTree() {
|
||||
const [selectedItemId, setSelectedItemId] = useState<string | null>('app-components-file-tree')
|
||||
const [selectedItemId, setSelectedItemId] = React.useState<string | null>('app-components-file-tree')
|
||||
|
||||
return (
|
||||
<FileTreeRoot
|
||||
@ -241,7 +240,7 @@ function StateFrame({
|
||||
children,
|
||||
}: {
|
||||
label: string
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div className="w-80 min-w-0 space-y-1">
|
||||
|
||||
@ -1,22 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { Collapsible as BaseCollapsible } from '@base-ui/react/collapsible'
|
||||
import { mergeProps } from '@base-ui/react/merge-props'
|
||||
import { useRender } from '@base-ui/react/use-render'
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
} from 'react'
|
||||
import * as React from 'react'
|
||||
import { cn } from '../cn'
|
||||
|
||||
const FileTreeLevelContext = createContext(1)
|
||||
const FileTreeLevelContext = React.createContext(1)
|
||||
|
||||
function useFileTreeLevel() {
|
||||
return useContext(FileTreeLevelContext)
|
||||
return React.useContext(FileTreeLevelContext)
|
||||
}
|
||||
|
||||
function getLabelText(children: ReactNode) {
|
||||
function getLabelText(children: React.ReactNode) {
|
||||
return typeof children === 'string' || typeof children === 'number'
|
||||
? String(children)
|
||||
: undefined
|
||||
@ -201,12 +197,12 @@ export function FileTreeFile({
|
||||
'aria-current': selected ? 'true' : undefined,
|
||||
'className': fileTreeRowClassName({ className }),
|
||||
'children': (
|
||||
<>
|
||||
<React.Fragment>
|
||||
{renderGuides(level)}
|
||||
<div className="flex min-w-0 flex-[1_0_0] items-center py-0.5">
|
||||
{children}
|
||||
</div>
|
||||
</>
|
||||
</React.Fragment>
|
||||
),
|
||||
} as useRender.ElementProps<'button'>
|
||||
|
||||
@ -272,7 +268,7 @@ export type FileTreeIconProps
|
||||
= Omit<useRender.ComponentProps<'span'>, 'children'>
|
||||
& {
|
||||
type?: FileTreeIconType
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function FileTreeIcon({
|
||||
@ -286,18 +282,18 @@ export function FileTreeIcon({
|
||||
'aria-hidden': true,
|
||||
'className': cn('relative flex size-5 shrink-0 items-center justify-center text-text-secondary', className),
|
||||
'children': (
|
||||
<>
|
||||
<React.Fragment>
|
||||
{children ?? (
|
||||
type === 'folder'
|
||||
? (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<span className="size-4 i-ri-folder-line group-data-panel-open/file-tree-row:hidden" />
|
||||
<span className="hidden size-4 text-text-accent i-ri-folder-open-line group-data-panel-open/file-tree-row:block" />
|
||||
</>
|
||||
</React.Fragment>
|
||||
)
|
||||
: <span className={cn('size-4', fileTreeIconClassNames[type])} />
|
||||
)}
|
||||
</>
|
||||
</React.Fragment>
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { FieldControl, FieldError, FieldLabel, FieldRoot } from '../../field'
|
||||
import { Form } from '../../form'
|
||||
@ -22,7 +23,7 @@ describe('Input', () => {
|
||||
|
||||
it('should apply size variants shared with FieldControl', async () => {
|
||||
const screen = await render(
|
||||
<>
|
||||
<React.Fragment>
|
||||
<label>
|
||||
Small input
|
||||
<Input size="small" />
|
||||
@ -34,7 +35,7 @@ describe('Input', () => {
|
||||
<FieldControl size="large" />
|
||||
</FieldRoot>
|
||||
</div>
|
||||
</>,
|
||||
</React.Fragment>,
|
||||
)
|
||||
|
||||
await expect.element(screen.getByRole('textbox', { name: 'Small input' })).toHaveClass('rounded-md', 'py-[3px]', 'system-xs-regular')
|
||||
|
||||
7
packages/dify-ui/src/internals/use-iso-layout-effect.ts
Normal file
7
packages/dify-ui/src/internals/use-iso-layout-effect.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as React from 'react'
|
||||
|
||||
const noop: typeof React.useLayoutEffect = () => {}
|
||||
|
||||
export const useIsoLayoutEffect = typeof document !== 'undefined'
|
||||
? React.useLayoutEffect
|
||||
: noop
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ComponentProps } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -28,7 +28,7 @@ const kbdVariants = cva(
|
||||
export type KbdColor = NonNullable<VariantProps<typeof kbdVariants>['color']>
|
||||
|
||||
export type KbdProps
|
||||
= Omit<ComponentProps<'kbd'>, 'color'>
|
||||
= Omit<React.ComponentProps<'kbd'>, 'color'>
|
||||
& VariantProps<typeof kbdVariants>
|
||||
|
||||
export function Kbd({
|
||||
@ -46,7 +46,7 @@ export function Kbd({
|
||||
)
|
||||
}
|
||||
|
||||
export type KbdGroupProps = ComponentProps<'span'>
|
||||
export type KbdGroupProps = React.ComponentProps<'span'>
|
||||
|
||||
export function KbdGroup({
|
||||
className,
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type {
|
||||
NumberFieldButtonProps,
|
||||
NumberFieldControlsProps,
|
||||
@ -6,6 +5,7 @@ import type {
|
||||
NumberFieldInputProps,
|
||||
NumberFieldUnitProps,
|
||||
} from '../index'
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
NumberField,
|
||||
@ -21,7 +21,7 @@ type RenderNumberFieldOptions = {
|
||||
defaultValue?: number
|
||||
groupProps?: Partial<NumberFieldGroupProps>
|
||||
inputProps?: Partial<NumberFieldInputProps>
|
||||
unitProps?: Partial<NumberFieldUnitProps> & { children?: ReactNode }
|
||||
unitProps?: Partial<NumberFieldUnitProps> & { children?: React.ReactNode }
|
||||
controlsProps?: Partial<NumberFieldControlsProps>
|
||||
incrementProps?: Partial<NumberFieldButtonProps>
|
||||
decrementProps?: Partial<NumberFieldButtonProps>
|
||||
@ -191,7 +191,7 @@ describe('NumberField wrapper', () => {
|
||||
|
||||
it('should rely on aria-labelledby when provided instead of injecting a fallback aria-label', async () => {
|
||||
const screen = await render(
|
||||
<>
|
||||
<React.Fragment>
|
||||
<span id="increment-label">Increment from label</span>
|
||||
<span id="decrement-label">Decrement from label</span>
|
||||
<NumberField defaultValue={8}>
|
||||
@ -203,7 +203,7 @@ describe('NumberField wrapper', () => {
|
||||
</NumberFieldControls>
|
||||
</NumberFieldGroup>
|
||||
</NumberField>
|
||||
</>,
|
||||
</React.Fragment>,
|
||||
)
|
||||
|
||||
await expect.element(screen.getByRole('button', { name: 'Increment from label' })).not.toHaveAttribute('aria-label')
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useId, useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldControls,
|
||||
@ -67,8 +67,8 @@ const DemoField = ({
|
||||
widthClassName,
|
||||
formatValue,
|
||||
}: DemoFieldProps) => {
|
||||
const inputId = useId()
|
||||
const [value, setValue] = useState<number | null>(defaultValue ?? null)
|
||||
const inputId = React.useId()
|
||||
const [value, setValue] = React.useState<number | null>(defaultValue ?? null)
|
||||
|
||||
return (
|
||||
<div className={cn('flex w-full max-w-80 flex-col gap-2', widthClassName)}>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { HTMLAttributes } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { NumberField as BaseNumberField } from '@base-ui/react/number-field'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import { cn } from '../cn'
|
||||
@ -97,7 +97,7 @@ export const numberFieldUnitVariants = cva(
|
||||
},
|
||||
)
|
||||
|
||||
export type NumberFieldUnitProps = HTMLAttributes<HTMLSpanElement> & VariantProps<typeof numberFieldUnitVariants>
|
||||
export type NumberFieldUnitProps = React.ComponentProps<'span'> & VariantProps<typeof numberFieldUnitVariants>
|
||||
|
||||
export function NumberFieldUnit({
|
||||
className,
|
||||
@ -116,7 +116,7 @@ const numberFieldControlsVariants = cva(
|
||||
'flex shrink-0 flex-col items-stretch border-l border-divider-subtle bg-transparent text-text-tertiary',
|
||||
)
|
||||
|
||||
export type NumberFieldControlsProps = HTMLAttributes<HTMLDivElement>
|
||||
export type NumberFieldControlsProps = React.ComponentProps<'div'>
|
||||
|
||||
export function NumberFieldControls({
|
||||
className,
|
||||
|
||||
@ -152,7 +152,11 @@ describe('Pagination primitive', () => {
|
||||
cancelable: true,
|
||||
}))
|
||||
|
||||
await expect.element(screen.getByRole('button', { name: 'Edit page number, current page 2 of 200' })).toBeInTheDocument()
|
||||
const summaryButton = screen.getByRole('button', { name: 'Edit page number, current page 2 of 200' })
|
||||
await expect.element(summaryButton).toBeInTheDocument()
|
||||
await vi.waitFor(() => {
|
||||
expect(document.activeElement).toBe(summaryButton.element())
|
||||
})
|
||||
})
|
||||
|
||||
it('cancels the page input editing mode with Escape', async () => {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Pagination,
|
||||
PaginationSkeleton,
|
||||
@ -15,8 +14,8 @@ function PaginationExample({
|
||||
initialPageSize?: number
|
||||
totalPages?: number
|
||||
}) {
|
||||
const [page, setPage] = useState(initialPage)
|
||||
const [pageSize, setPageSize] = useState(initialPageSize)
|
||||
const [page, setPage] = React.useState(initialPage)
|
||||
const [pageSize, setPageSize] = React.useState(initialPageSize)
|
||||
|
||||
return (
|
||||
<Pagination
|
||||
@ -32,7 +31,7 @@ function PaginationExample({
|
||||
)
|
||||
}
|
||||
|
||||
function PaginationDemo(props: ComponentProps<typeof PaginationExample>) {
|
||||
function PaginationDemo(props: React.ComponentProps<typeof PaginationExample>) {
|
||||
return (
|
||||
<div className="w-236 max-w-full bg-components-panel-bg px-16 py-10">
|
||||
<PaginationExample {...props} />
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import type { Button as BaseButtonNS } from '@base-ui/react/button'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Button as BaseButton } from '@base-ui/react/button'
|
||||
import { mergeProps } from '@base-ui/react/merge-props'
|
||||
import { useRender } from '@base-ui/react/use-render'
|
||||
import { createContext, useContext, useMemo, useRef, useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import { cn } from '../cn'
|
||||
import { useIsoLayoutEffect } from '../internals/use-iso-layout-effect'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldGroup,
|
||||
@ -28,10 +28,10 @@ type PaginationContextValue = {
|
||||
items: PageItem[]
|
||||
}
|
||||
|
||||
const PaginationContext = createContext<PaginationContextValue | null>(null)
|
||||
const PaginationContext = React.createContext<PaginationContextValue | null>(null)
|
||||
|
||||
function usePaginationContext(component: string) {
|
||||
const context = useContext(PaginationContext)
|
||||
const context = React.useContext(PaginationContext)
|
||||
|
||||
if (!context)
|
||||
throw new Error(`${component} must be used inside PaginationRoot.`)
|
||||
@ -156,7 +156,7 @@ export function PaginationRoot({
|
||||
const normalizedPage = clampPage(page, normalizedTotalPages)
|
||||
const hasPages = normalizedTotalPages > 0
|
||||
const disabled = normalizedTotalPages <= 1
|
||||
const items = useMemo(() => getPageItems({
|
||||
const items = React.useMemo(() => getPageItems({
|
||||
page: normalizedPage,
|
||||
totalPages: normalizedTotalPages,
|
||||
siblingCount,
|
||||
@ -170,7 +170,7 @@ export function PaginationRoot({
|
||||
visiblePageCount,
|
||||
])
|
||||
|
||||
const context = useMemo<PaginationContextValue>(() => ({
|
||||
const context = React.useMemo<PaginationContextValue>(() => ({
|
||||
page: normalizedPage,
|
||||
totalPages: normalizedTotalPages,
|
||||
hasPages,
|
||||
@ -239,7 +239,7 @@ export function PaginationNavigation({
|
||||
}
|
||||
|
||||
type PaginationButtonProps = Omit<BaseButtonNS.Props, 'children'> & {
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const paginationArrowButtonClassName = [
|
||||
@ -316,7 +316,7 @@ export function PaginationNext({
|
||||
|
||||
export type PaginationPageJumpProps = Omit<BaseButtonNS.Props, 'children'> & {
|
||||
inputLabel?: string
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function PaginationPageJump({
|
||||
@ -327,8 +327,26 @@ export function PaginationPageJump({
|
||||
...props
|
||||
}: PaginationPageJumpProps) {
|
||||
const pagination = usePaginationContext('PaginationPageJump')
|
||||
const [editing, setEditing] = useState(false)
|
||||
const summaryButtonRef = useRef<HTMLButtonElement | null>(null)
|
||||
const [editing, setEditing] = React.useState(false)
|
||||
const summaryButtonRef = React.useRef<HTMLButtonElement | null>(null)
|
||||
const restoreSummaryFocusRef = React.useRef(false)
|
||||
|
||||
useIsoLayoutEffect(() => {
|
||||
if (editing || !restoreSummaryFocusRef.current)
|
||||
return
|
||||
|
||||
restoreSummaryFocusRef.current = false
|
||||
|
||||
const summaryButton = summaryButtonRef.current
|
||||
if (!summaryButton)
|
||||
return
|
||||
|
||||
const activeElement = summaryButton.ownerDocument.activeElement
|
||||
if (activeElement && activeElement !== summaryButton.ownerDocument.body)
|
||||
return
|
||||
|
||||
summaryButton.focus({ preventScroll: true })
|
||||
}, [editing])
|
||||
|
||||
if (!pagination.hasPages)
|
||||
return null
|
||||
@ -367,14 +385,15 @@ export function PaginationPageJump({
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
restoreSummaryFocusRef.current = true
|
||||
event.currentTarget.blur()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault()
|
||||
restoreSummaryFocusRef.current = true
|
||||
setEditing(false)
|
||||
requestAnimationFrame(() => summaryButtonRef.current?.focus())
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -402,11 +421,11 @@ export function PaginationPageJump({
|
||||
}}
|
||||
>
|
||||
{children ?? (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<span>{pagination.page}</span>
|
||||
<span className="text-text-quaternary">/</span>
|
||||
<span>{pagination.totalPages}</span>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</BaseButton>
|
||||
)
|
||||
@ -444,7 +463,7 @@ export function PaginationPageList({
|
||||
|
||||
export type PaginationPageProps = Omit<BaseButtonNS.Props, 'children'> & {
|
||||
page: number
|
||||
children?: ReactNode
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function PaginationPage({
|
||||
@ -505,7 +524,7 @@ export type PaginationPageSizeProps<Value extends number = number> = {
|
||||
'value': Value
|
||||
'options': readonly Value[]
|
||||
'onValueChange': (value: Value) => void
|
||||
'label'?: ReactNode
|
||||
'label'?: React.ReactNode
|
||||
'aria-label'?: string
|
||||
'className'?: string
|
||||
}
|
||||
@ -563,7 +582,7 @@ export type PaginationPageSizeConfig<Value extends number = number> = {
|
||||
value: Value
|
||||
options: readonly Value[]
|
||||
onValueChange: (value: Value) => void
|
||||
label?: ReactNode
|
||||
label?: React.ReactNode
|
||||
ariaLabel?: string
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
Popover,
|
||||
@ -5,7 +6,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '..'
|
||||
|
||||
const renderWithSafeViewport = (ui: import('react').ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { Placement } from '.'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Popover,
|
||||
PopoverClose,
|
||||
@ -159,7 +159,7 @@ const PLACEMENTS: Placement[] = [
|
||||
]
|
||||
|
||||
const PlacementsDemo = () => {
|
||||
const [placement, setPlacement] = useState<Placement>('bottom')
|
||||
const [placement, setPlacement] = React.useState<Placement>('bottom')
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-4 p-20">
|
||||
@ -208,7 +208,7 @@ export const Placements: Story = {
|
||||
}
|
||||
|
||||
const ControlledDemo = () => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { Placement } from '../placement'
|
||||
import { Popover as BasePopover } from '@base-ui/react/popover'
|
||||
import { cn } from '../cn'
|
||||
@ -16,7 +16,7 @@ export const PopoverDescription = BasePopover.Description
|
||||
export const createPopoverHandle = BasePopover.createHandle
|
||||
|
||||
type PopoverContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
PreviewCard,
|
||||
@ -5,7 +6,7 @@ import {
|
||||
PreviewCardTrigger,
|
||||
} from '..'
|
||||
|
||||
const renderWithSafeViewport = (ui: import('react').ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { Placement } from '.'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
createPreviewCardHandle,
|
||||
PreviewCard,
|
||||
@ -144,7 +144,7 @@ const PLACEMENTS: Placement[] = [
|
||||
]
|
||||
|
||||
const PlacementsDemo = () => {
|
||||
const [placement, setPlacement] = useState<Placement>('bottom')
|
||||
const [placement, setPlacement] = React.useState<Placement>('bottom')
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-4 p-20">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { Placement } from '../placement'
|
||||
import { PreviewCard as BasePreviewCard } from '@base-ui/react/preview-card'
|
||||
import { cn } from '../cn'
|
||||
@ -27,7 +27,7 @@ export const PreviewCardTrigger = BasePreviewCard.Trigger
|
||||
export const createPreviewCardHandle = BasePreviewCard.createHandle
|
||||
|
||||
type PreviewCardContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ProgressCircleColor, ProgressCircleSize } from '.'
|
||||
import { Fragment } from 'react'
|
||||
import * as React from 'react'
|
||||
import { ProgressCircle } from '.'
|
||||
|
||||
const colors: ProgressCircleColor[] = ['gray', 'white', 'blue', 'warning', 'error']
|
||||
@ -48,7 +48,7 @@ export const CircleMatrix: Story = {
|
||||
</div>
|
||||
))}
|
||||
{colors.map(color => (
|
||||
<Fragment key={color}>
|
||||
<React.Fragment key={color}>
|
||||
<div className="system-xs-semibold-uppercase text-text-secondary">
|
||||
{color}
|
||||
</div>
|
||||
@ -61,7 +61,7 @@ export const CircleMatrix: Story = {
|
||||
aria-label={`${color} ${size} progress`}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { FieldItem, FieldLabel, FieldRoot } from '../../field'
|
||||
import { FieldsetLegend, FieldsetRoot } from '../../fieldset'
|
||||
@ -12,7 +12,7 @@ const clickElement = (element: HTMLElement | SVGElement) => {
|
||||
describe('RadioGroup', () => {
|
||||
it('should manage a controlled single selection', async () => {
|
||||
function StorageDemo() {
|
||||
const [value, setValue] = useState('ssd')
|
||||
const [value, setValue] = React.useState('ssd')
|
||||
|
||||
return (
|
||||
<FieldRoot name="storageType">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import { RadioGroup } from '.'
|
||||
import {
|
||||
FieldDescription,
|
||||
@ -28,7 +28,7 @@ export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
function StandardFormRowsDemo() {
|
||||
const [value, setValue] = useState('vector')
|
||||
const [value, setValue] = React.useState('vector')
|
||||
|
||||
return (
|
||||
<FieldRoot name="retrievalIndex" className="w-80">
|
||||
@ -67,7 +67,7 @@ export const StandardFormRows: Story = {
|
||||
}
|
||||
|
||||
function BooleanInlineDemo() {
|
||||
const [value, setValue] = useState(true)
|
||||
const [value, setValue] = React.useState(true)
|
||||
|
||||
return (
|
||||
<FieldRoot name="streaming" className="w-80">
|
||||
@ -108,7 +108,7 @@ export const BooleanInline: Story = {
|
||||
}
|
||||
|
||||
function OptionCardsDemo() {
|
||||
const [value, setValue] = useState('default')
|
||||
const [value, setValue] = React.useState('default')
|
||||
|
||||
return (
|
||||
<FieldRoot name="promptMode" className="w-100">
|
||||
@ -174,7 +174,7 @@ function DynamicFormFieldDemo() {
|
||||
{ value: 'high_quality', label: 'High quality' },
|
||||
{ value: 'economy', label: 'Economy' },
|
||||
]
|
||||
const [selected, setSelected] = useState('automatic')
|
||||
const [selected, setSelected] = React.useState('automatic')
|
||||
|
||||
return (
|
||||
<FieldRoot name="generation_mode" className="flex w-80 flex-col gap-2">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ComponentProps, ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { FieldItem, FieldLabel, FieldRoot } from '../../field'
|
||||
import { FieldsetLegend, FieldsetRoot } from '../../fieldset'
|
||||
@ -15,8 +15,8 @@ const clickElement = (element: HTMLElement | SVGElement) => {
|
||||
element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
|
||||
}
|
||||
|
||||
type TestRadioGroupProps = ComponentProps<typeof RadioGroup> & {
|
||||
children: ReactNode
|
||||
type TestRadioGroupProps = React.ComponentProps<typeof RadioGroup> & {
|
||||
children: React.ReactNode
|
||||
label: string
|
||||
name?: string
|
||||
}
|
||||
@ -37,8 +37,8 @@ function TestRadioGroup({
|
||||
)
|
||||
}
|
||||
|
||||
type TestRadioOptionProps = ComponentProps<typeof Radio> & {
|
||||
children: ReactNode
|
||||
type TestRadioOptionProps = React.ComponentProps<typeof Radio> & {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function TestRadioOption({
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Radio,
|
||||
RadioSkeleton,
|
||||
@ -36,8 +35,8 @@ const meta = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
function RadioDemo(args: Partial<ComponentProps<typeof Radio>>) {
|
||||
const [value, setValue] = useState('ssd')
|
||||
function RadioDemo(args: Partial<React.ComponentProps<typeof Radio>>) {
|
||||
const [value, setValue] = React.useState('ssd')
|
||||
|
||||
return (
|
||||
<FieldRoot name="storageType">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { Radio as BaseRadioNS } from '@base-ui/react/radio'
|
||||
import type { HTMLAttributes } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Radio as BaseRadio } from '@base-ui/react/radio'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -87,7 +87,7 @@ export function Radio<Value = string>({
|
||||
}
|
||||
|
||||
export type RadioSkeletonProps
|
||||
= Omit<HTMLAttributes<HTMLDivElement>, 'className'>
|
||||
= Omit<React.ComponentProps<'div'>, 'className'>
|
||||
& {
|
||||
className?: string
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
ScrollArea,
|
||||
@ -84,7 +85,7 @@ describe('scroll-area wrapper', () => {
|
||||
|
||||
it('should render the convenience wrapper and apply slot props', async () => {
|
||||
const screen = await render(
|
||||
<>
|
||||
<React.Fragment>
|
||||
<p id="installed-apps-label">Installed apps</p>
|
||||
<ScrollArea
|
||||
className="h-40 w-40"
|
||||
@ -98,7 +99,7 @@ describe('scroll-area wrapper', () => {
|
||||
>
|
||||
<div className="h-48 w-20">Scrollable content</div>
|
||||
</ScrollArea>
|
||||
</>,
|
||||
</React.Fragment>,
|
||||
)
|
||||
|
||||
const viewport = screen.getByRole('region', { name: 'Installed apps' })
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import * as React from 'react'
|
||||
import type * as React from 'react'
|
||||
import {
|
||||
ScrollAreaContent,
|
||||
ScrollAreaCorner,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { ScrollArea as BaseScrollArea } from '@base-ui/react/scroll-area'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -16,7 +16,7 @@ type ScrollAreaSlotClassNames = {
|
||||
}
|
||||
|
||||
type ScrollAreaProps = Omit<ScrollAreaRootProps, 'children'> & {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
orientation?: 'vertical' | 'horizontal'
|
||||
slotClassNames?: ScrollAreaSlotClassNames
|
||||
label?: string
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
SegmentedControl,
|
||||
SegmentedControlDivider,
|
||||
@ -35,10 +35,10 @@ const Icon = () => (
|
||||
)
|
||||
|
||||
const Item = () => (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<Icon />
|
||||
<span className="px-0.5">Item</span>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
function SegmentedControlExample({
|
||||
@ -93,7 +93,7 @@ function SpecPanel({
|
||||
children,
|
||||
}: {
|
||||
className?: string
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div className={className}>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import type { Toggle as BaseToggleNS } from '@base-ui/react/toggle'
|
||||
import type { ToggleGroup as BaseToggleGroupNS } from '@base-ui/react/toggle-group'
|
||||
import type { HTMLAttributes } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Toggle as BaseToggle } from '@base-ui/react/toggle'
|
||||
import { ToggleGroup as BaseToggleGroup } from '@base-ui/react/toggle-group'
|
||||
import { cn } from '../cn'
|
||||
@ -39,7 +39,7 @@ export function SegmentedControlItem<Value extends string = string>({
|
||||
)
|
||||
}
|
||||
|
||||
export type SegmentedControlDividerProps = Omit<HTMLAttributes<HTMLSpanElement>, 'className'> & {
|
||||
export type SegmentedControlDividerProps = Omit<React.ComponentProps<'span'>, 'className'> & {
|
||||
className?: string
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
Select,
|
||||
@ -13,7 +14,7 @@ import {
|
||||
} from '../index'
|
||||
|
||||
const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElement
|
||||
const renderWithSafeViewport = (ui: import('react').ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@ -263,7 +263,7 @@ export const ReadOnly: Story = {
|
||||
}
|
||||
|
||||
const ControlledDemo = () => {
|
||||
const [value, setValue] = useState<string | null>('balanced')
|
||||
const [value, setValue] = React.useState<string | null>('balanced')
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { Placement } from '../placement'
|
||||
import { Select as BaseSelect } from '@base-ui/react/select'
|
||||
import { cva } from 'class-variance-authority'
|
||||
@ -107,7 +107,7 @@ export function SelectSeparator({
|
||||
}
|
||||
|
||||
type SelectContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Slider,
|
||||
SliderControl,
|
||||
@ -51,7 +50,7 @@ function SliderDemo({
|
||||
defaultValue: _defaultValue,
|
||||
...args
|
||||
}: React.ComponentProps<typeof Slider>) {
|
||||
const [value, setValue] = useState(initialValue)
|
||||
const [value, setValue] = React.useState(initialValue)
|
||||
|
||||
return (
|
||||
<div className="w-[320px] space-y-3">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { StatusDotSize, StatusDotStatus } from '.'
|
||||
import { Fragment } from 'react'
|
||||
import * as React from 'react'
|
||||
import { StatusDot, StatusDotSkeleton } from '.'
|
||||
|
||||
const statuses: StatusDotStatus[] = ['success', 'warning', 'error', 'normal', 'disabled']
|
||||
@ -39,14 +39,14 @@ export const Matrix: Story = {
|
||||
<div className="system-xs-medium text-text-tertiary">Small</div>
|
||||
<div className="system-xs-medium text-text-tertiary">Medium</div>
|
||||
{statuses.map(status => (
|
||||
<Fragment key={status}>
|
||||
<React.Fragment key={status}>
|
||||
<div className="system-xs-semibold-uppercase text-text-secondary">
|
||||
{status}
|
||||
</div>
|
||||
{sizes.map(size => (
|
||||
<StatusDot key={`${status}-${size}`} status={status} size={size} />
|
||||
))}
|
||||
</Fragment>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ComponentProps } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -49,14 +49,14 @@ export type StatusDotStatus = NonNullable<StatusDotVariants['status']>
|
||||
export type StatusDotSize = NonNullable<StatusDotVariants['size']>
|
||||
|
||||
export type StatusDotProps
|
||||
= Omit<ComponentProps<'span'>, 'children'>
|
||||
= Omit<React.ComponentProps<'span'>, 'children'>
|
||||
& {
|
||||
status?: StatusDotStatus
|
||||
size?: StatusDotSize
|
||||
}
|
||||
|
||||
export type StatusDotSkeletonProps
|
||||
= Omit<ComponentProps<'span'>, 'children'>
|
||||
= Omit<React.ComponentProps<'span'>, 'children'>
|
||||
& {
|
||||
size?: StatusDotSize
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useState, useTransition } from 'react'
|
||||
import * as React from 'react'
|
||||
import { Switch, SwitchSkeleton } from '.'
|
||||
import {
|
||||
FieldDescription,
|
||||
@ -47,12 +46,12 @@ const meta = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
type SwitchDemoProps = Partial<Omit<ComponentProps<typeof Switch>, 'checked' | 'defaultChecked' | 'onCheckedChange'>> & {
|
||||
type SwitchDemoProps = Partial<Omit<React.ComponentProps<typeof Switch>, 'checked' | 'defaultChecked' | 'onCheckedChange'>> & {
|
||||
checked?: boolean
|
||||
}
|
||||
|
||||
const SwitchDemo = (args: SwitchDemoProps) => {
|
||||
const [enabled, setEnabled] = useState(args.checked ?? false)
|
||||
const [enabled, setEnabled] = React.useState(args.checked ?? false)
|
||||
|
||||
return (
|
||||
<FieldRoot name="autoRetry" className="w-72">
|
||||
@ -167,7 +166,7 @@ export const AllStates: Story = {
|
||||
}
|
||||
|
||||
const SizeComparisonDemo = () => {
|
||||
const [states, setStates] = useState({
|
||||
const [states, setStates] = React.useState({
|
||||
xs: false,
|
||||
sm: false,
|
||||
md: true,
|
||||
@ -209,7 +208,7 @@ export const SizeComparison: Story = {
|
||||
}
|
||||
|
||||
const LoadingDemo = () => {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
@ -275,7 +274,7 @@ export const Loading: Story = {
|
||||
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
function useMockAutoRetrySettingQuery() {
|
||||
const [enabled, setEnabled] = useState(false)
|
||||
const [enabled, setEnabled] = React.useState(false)
|
||||
|
||||
return {
|
||||
data: {
|
||||
@ -290,8 +289,8 @@ function useMockUpdateAutoRetrySettingMutation({
|
||||
}: {
|
||||
onSuccess: (enabled: boolean) => void
|
||||
}) {
|
||||
const [requestCount, setRequestCount] = useState(0)
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const [requestCount, setRequestCount] = React.useState(0)
|
||||
const [isPending, startTransition] = React.useTransition()
|
||||
|
||||
const mutate = (nextValue: boolean) => {
|
||||
if (isPending)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import type { Switch as BaseSwitchNS } from '@base-ui/react/switch'
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { HTMLAttributes } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Switch as BaseSwitch } from '@base-ui/react/switch'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import { cn } from '../cn'
|
||||
@ -134,7 +134,7 @@ const switchSkeletonVariants = cva(
|
||||
)
|
||||
|
||||
export type SwitchSkeletonProps
|
||||
= HTMLAttributes<HTMLDivElement>
|
||||
= React.ComponentProps<'div'>
|
||||
& VariantProps<typeof switchSkeletonVariants>
|
||||
|
||||
export function SwitchSkeleton({
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { FocusEvent } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import {
|
||||
FieldDescription,
|
||||
@ -131,7 +131,7 @@ describe('Textarea', () => {
|
||||
|
||||
it('should route field props through Base UI Field.Control and textarea-only props to textarea', async () => {
|
||||
const onFormSubmit = vi.fn()
|
||||
const onBlur = vi.fn((event: FocusEvent<HTMLTextAreaElement>) => {
|
||||
const onBlur = vi.fn((event: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
expect(event.currentTarget.tagName).toBe('TEXTAREA')
|
||||
})
|
||||
const screen = await render(
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import { Button } from '../button'
|
||||
import {
|
||||
FieldDescription,
|
||||
@ -91,7 +91,7 @@ export const States: Story = {
|
||||
}
|
||||
|
||||
const FormDemo = () => {
|
||||
const [savedDescription, setSavedDescription] = useState<string | null>(null)
|
||||
const [savedDescription, setSavedDescription] = React.useState<string | null>(null)
|
||||
|
||||
return (
|
||||
<Form
|
||||
@ -134,7 +134,7 @@ export const WithField: Story = {
|
||||
}
|
||||
|
||||
const ControlledDemo = () => {
|
||||
const [value, setValue] = useState('Summarize customer feedback into actionable product themes.')
|
||||
const [value, setValue] = React.useState('Summarize customer feedback into actionable product themes.')
|
||||
|
||||
return (
|
||||
<FieldRoot name="prompt">
|
||||
@ -160,7 +160,7 @@ export const Controlled: Story = {
|
||||
|
||||
const CharacterCounterDemo = () => {
|
||||
const maxLength = 120
|
||||
const [value, setValue] = useState('Summarize customer feedback into actionable product themes.')
|
||||
const [value, setValue] = React.useState('Summarize customer feedback into actionable product themes.')
|
||||
|
||||
return (
|
||||
<FieldRoot name="limitedPrompt">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import type { Field as BaseFieldNS } from '@base-ui/react/field'
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ComponentPropsWithRef } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Field as BaseField } from '@base-ui/react/field'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import { cn } from '../cn'
|
||||
@ -50,7 +50,7 @@ type UncontrolledTextareaProps = {
|
||||
onValueChange?: TextareaOnValueChange
|
||||
}
|
||||
|
||||
type TextareaNativeProps = ComponentPropsWithRef<'textarea'>
|
||||
type TextareaNativeProps = React.ComponentPropsWithRef<'textarea'>
|
||||
type TextareaOnlyProps = Pick<TextareaNativeProps, 'cols' | 'rows' | 'wrap'>
|
||||
type TextareaElementProps = Omit<
|
||||
TextareaNativeProps,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import * as React from 'react'
|
||||
import { toast, ToastHost } from '.'
|
||||
|
||||
const buttonClassName = 'rounded-lg border border-divider-subtle bg-components-button-secondary-bg px-3 py-2 text-sm text-text-secondary shadow-xs transition-colors hover:bg-state-base-hover'
|
||||
@ -15,7 +14,7 @@ const ExampleCard = ({
|
||||
eyebrow: string
|
||||
title: string
|
||||
description: string
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
return (
|
||||
<section className={cardClassName}>
|
||||
@ -250,7 +249,7 @@ const ActionExamples = () => {
|
||||
}
|
||||
|
||||
const DeduplicateExamples = () => {
|
||||
const saveCountRef = useRef(0)
|
||||
const saveCountRef = React.useRef(0)
|
||||
|
||||
const saveDraft = () => {
|
||||
saveCountRef.current += 1
|
||||
@ -314,7 +313,7 @@ const UpdateExamples = () => {
|
||||
|
||||
const ToastDocsDemo = () => {
|
||||
return (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<ToastHost />
|
||||
<div className="min-h-screen bg-background-default-subtle px-6 py-12">
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-col gap-8">
|
||||
@ -339,7 +338,7 @@ const ToastDocsDemo = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import type {
|
||||
ToastManagerUpdateOptions,
|
||||
ToastObject,
|
||||
} from '@base-ui/react/toast'
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import { Toast as BaseToast } from '@base-ui/react/toast'
|
||||
import { cn } from '../cn'
|
||||
|
||||
@ -64,11 +64,11 @@ type ToastHostProps = {
|
||||
}
|
||||
|
||||
type ToastDismiss = (toastId?: string) => void
|
||||
type ToastCall = (title: ReactNode, options?: ToastOptions) => string
|
||||
type TypedToastCall = (title: ReactNode, options?: TypedToastOptions) => string
|
||||
type ToastCall = (title: React.ReactNode, options?: ToastOptions) => string
|
||||
type TypedToastCall = (title: React.ReactNode, options?: TypedToastOptions) => string
|
||||
|
||||
type ToastApi = {
|
||||
(title: ReactNode, options?: ToastOptions): string
|
||||
(title: React.ReactNode, options?: ToastOptions): string
|
||||
success: TypedToastCall
|
||||
error: TypedToastCall
|
||||
warning: TypedToastCall
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import type * as React from 'react'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '../index'
|
||||
|
||||
const renderWithSafeViewport = (ui: import('react').ReactNode) => render(
|
||||
const renderWithSafeViewport = (ui: React.ReactNode) => render(
|
||||
<div style={{ minHeight: '100vh', minWidth: '100vw', padding: '240px' }}>
|
||||
{ui}
|
||||
</div>,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import type { Placement } from '.'
|
||||
import { useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@ -107,7 +107,7 @@ const PLACEMENTS: Placement[] = [
|
||||
]
|
||||
|
||||
const PlacementsDemo = () => {
|
||||
const [placement, setPlacement] = useState<Placement>('top')
|
||||
const [placement, setPlacement] = React.useState<Placement>('top')
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-4 p-24">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import type * as React from 'react'
|
||||
import type { Placement } from '../placement'
|
||||
import { Tooltip as BaseTooltip } from '@base-ui/react/tooltip'
|
||||
import { cn } from '../cn'
|
||||
@ -32,7 +32,7 @@ export const Tooltip = BaseTooltip.Root
|
||||
export const TooltipTrigger = BaseTooltip.Trigger
|
||||
|
||||
type TooltipContentProps = {
|
||||
children: ReactNode
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
sideOffset?: number
|
||||
alignOffset?: number
|
||||
|
||||
Loading…
Reference in New Issue
Block a user