mirror of
https://github.com/langgenius/dify.git
synced 2026-05-25 10:00:43 -04:00
feat: add dify-ui input primitive (#36446)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -65,6 +65,10 @@
|
||||
"types": "./src/form/index.tsx",
|
||||
"import": "./src/form/index.tsx"
|
||||
},
|
||||
"./input": {
|
||||
"types": "./src/input/index.tsx",
|
||||
"import": "./src/input/index.tsx"
|
||||
},
|
||||
"./meter": {
|
||||
"types": "./src/meter/index.tsx",
|
||||
"import": "./src/meter/index.tsx"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import type { Field as BaseFieldNS } from '@base-ui/react/field'
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import { Field as BaseField } from '@base-ui/react/field'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import { cn } from '../cn'
|
||||
import { textControlVariants } from '../text-control-variants'
|
||||
|
||||
export type FieldRootProps
|
||||
= Omit<BaseFieldNS.Root.Props, 'className'>
|
||||
@@ -62,37 +62,11 @@ export function FieldLabel({
|
||||
)
|
||||
}
|
||||
|
||||
const fieldControlVariants = cva(
|
||||
[
|
||||
'w-full appearance-none border border-transparent bg-components-input-bg-normal text-components-input-text-filled caret-primary-600 outline-hidden transition-[background-color,border-color,box-shadow]',
|
||||
'placeholder:text-components-input-text-placeholder',
|
||||
'hover:border-components-input-border-hover hover:bg-components-input-bg-hover',
|
||||
'focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs',
|
||||
'data-invalid:border-components-input-border-destructive data-invalid:bg-components-input-bg-destructive',
|
||||
'read-only:cursor-default read-only:shadow-none read-only:hover:border-transparent read-only:hover:bg-components-input-bg-normal read-only:focus:border-transparent read-only:focus:bg-components-input-bg-normal read-only:focus:shadow-none',
|
||||
'disabled:cursor-not-allowed disabled:border-transparent disabled:bg-components-input-bg-disabled disabled:text-components-input-text-filled-disabled',
|
||||
'disabled:hover:border-transparent disabled:hover:bg-components-input-bg-disabled',
|
||||
'motion-reduce:transition-none',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
small: 'rounded-md px-2 py-[3px] system-xs-regular',
|
||||
medium: 'rounded-lg px-3 py-[7px] system-sm-regular',
|
||||
large: 'rounded-[10px] px-4 py-[7px] system-md-regular',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'medium',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type FieldControlSize = NonNullable<VariantProps<typeof fieldControlVariants>['size']>
|
||||
export type FieldControlSize = NonNullable<VariantProps<typeof textControlVariants>['size']>
|
||||
|
||||
export type FieldControlProps
|
||||
= Omit<BaseFieldNS.Control.Props, 'className' | 'size'>
|
||||
& VariantProps<typeof fieldControlVariants>
|
||||
& VariantProps<typeof textControlVariants>
|
||||
& {
|
||||
className?: string
|
||||
}
|
||||
@@ -106,7 +80,7 @@ export function FieldControl({
|
||||
}: FieldControlProps) {
|
||||
return (
|
||||
<BaseField.Control
|
||||
className={cn(fieldControlVariants({ size }), className)}
|
||||
className={cn(textControlVariants({ size }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
83
packages/dify-ui/src/input/__tests__/index.spec.tsx
Normal file
83
packages/dify-ui/src/input/__tests__/index.spec.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { FieldControl, FieldError, FieldLabel, FieldRoot } from '../../field'
|
||||
import { Form } from '../../form'
|
||||
import { Input } from '../index'
|
||||
|
||||
const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElement
|
||||
|
||||
describe('Input', () => {
|
||||
it('should render a labelled Base UI input with design-system classes', async () => {
|
||||
const screen = await render(
|
||||
<label>
|
||||
Workspace name
|
||||
<Input name="workspaceName" defaultValue="Dify" />
|
||||
</label>,
|
||||
)
|
||||
|
||||
const input = screen.getByRole('textbox', { name: 'Workspace name' })
|
||||
|
||||
await expect.element(input).toHaveValue('Dify')
|
||||
await expect.element(input).toHaveClass('rounded-lg', 'py-[7px]', 'system-sm-regular')
|
||||
})
|
||||
|
||||
it('should apply size variants shared with FieldControl', async () => {
|
||||
const screen = await render(
|
||||
<>
|
||||
<label>
|
||||
Small input
|
||||
<Input size="small" />
|
||||
</label>
|
||||
<div>
|
||||
Large field
|
||||
<FieldRoot name="largeField">
|
||||
<FieldLabel>Large field</FieldLabel>
|
||||
<FieldControl size="large" />
|
||||
</FieldRoot>
|
||||
</div>
|
||||
</>,
|
||||
)
|
||||
|
||||
await expect.element(screen.getByRole('textbox', { name: 'Small input' })).toHaveClass('rounded-md', 'py-[3px]', 'system-xs-regular')
|
||||
await expect.element(screen.getByRole('textbox', { name: 'Large field' })).toHaveClass('rounded-[10px]', 'py-[7px]', 'system-md-regular')
|
||||
})
|
||||
|
||||
it('should use FieldRoot invalid state', async () => {
|
||||
const screen = await render(
|
||||
<FieldRoot name="repositoryUrl" invalid>
|
||||
<FieldLabel>Repository URL</FieldLabel>
|
||||
<Input defaultValue="github.com/langgenius" />
|
||||
</FieldRoot>,
|
||||
)
|
||||
|
||||
const input = screen.getByRole('textbox', { name: 'Repository URL' })
|
||||
|
||||
await expect.element(input).toHaveAttribute('aria-invalid', 'true')
|
||||
await expect.element(input).toHaveAttribute('data-invalid')
|
||||
await expect.element(input).toHaveClass('data-invalid:border-components-input-border-destructive')
|
||||
})
|
||||
|
||||
it('should integrate with FieldRoot and Base UI Form validation', async () => {
|
||||
const onFormSubmit = vi.fn()
|
||||
const screen = await render(
|
||||
<Form aria-label="account form" onFormSubmit={onFormSubmit}>
|
||||
<FieldRoot name="email">
|
||||
<FieldLabel>Email</FieldLabel>
|
||||
<Input type="email" required />
|
||||
<FieldError match="valueMissing">Email is required.</FieldError>
|
||||
</FieldRoot>
|
||||
<button type="submit">Save</button>
|
||||
</Form>,
|
||||
)
|
||||
|
||||
const input = screen.getByRole('textbox', { name: 'Email' })
|
||||
|
||||
asHTMLElement(screen.getByRole('button', { name: 'Save' }).element()).click()
|
||||
|
||||
await vi.waitFor(async () => {
|
||||
await expect.element(screen.getByText('Email is required.')).toBeInTheDocument()
|
||||
await expect.element(input).toHaveAttribute('aria-invalid', 'true')
|
||||
await expect.element(input).toHaveAttribute('data-invalid')
|
||||
})
|
||||
expect(onFormSubmit).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
124
packages/dify-ui/src/input/index.stories.tsx
Normal file
124
packages/dify-ui/src/input/index.stories.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { Button } from '../button'
|
||||
import {
|
||||
FieldDescription,
|
||||
FieldError,
|
||||
FieldLabel,
|
||||
FieldRoot,
|
||||
} from '../field'
|
||||
import { Form } from '../form'
|
||||
import { Input } from './index'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/Form/Input',
|
||||
component: Input,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
description: {
|
||||
component: 'A standalone text input primitive built on Base UI Input. Use it for labelled text boxes outside FieldControl, and keep FieldControl for full FieldRoot form composition.',
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
} satisfies Meta<typeof Input>
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Basic: Story = {
|
||||
render: () => (
|
||||
<div className="w-80">
|
||||
<label htmlFor="workspace-name" className="mb-1 block w-fit py-1 text-text-secondary system-sm-medium">
|
||||
Workspace name
|
||||
</label>
|
||||
<Input
|
||||
id="workspace-name"
|
||||
name="workspaceName"
|
||||
autoComplete="organization"
|
||||
placeholder="e.g. Acme workspace…"
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<div className="grid w-80 gap-3">
|
||||
<label className="grid gap-1 text-text-secondary system-sm-medium" htmlFor="small-input">
|
||||
Small
|
||||
<Input id="small-input" size="small" name="smallInput" placeholder="e.g. tag…" autoComplete="off" />
|
||||
</label>
|
||||
<label className="grid gap-1 text-text-secondary system-sm-medium" htmlFor="medium-input">
|
||||
Medium
|
||||
<Input id="medium-input" name="mediumInput" placeholder="e.g. Production API…" autoComplete="off" />
|
||||
</label>
|
||||
<label className="grid gap-1 text-text-secondary system-sm-medium" htmlFor="large-input">
|
||||
Large
|
||||
<Input id="large-input" size="large" name="largeInput" placeholder="e.g. Customer portal…" autoComplete="off" />
|
||||
</label>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
export const States: Story = {
|
||||
render: () => (
|
||||
<div className="grid w-80 gap-3">
|
||||
<div className="grid gap-1">
|
||||
<label className="text-text-secondary system-sm-medium" htmlFor="placeholder-state">Placeholder</label>
|
||||
<Input id="placeholder-state" name="placeholderState" placeholder="e.g. Search datasets…" autoComplete="off" />
|
||||
</div>
|
||||
<div className="grid gap-1">
|
||||
<label className="text-text-secondary system-sm-medium" htmlFor="filled-state">Filled</label>
|
||||
<Input id="filled-state" name="filledState" defaultValue="Customer knowledge base" autoComplete="off" />
|
||||
</div>
|
||||
<div className="grid gap-1">
|
||||
<FieldRoot name="repositoryUrl" invalid>
|
||||
<FieldLabel>Invalid</FieldLabel>
|
||||
<Input
|
||||
id="invalid-state"
|
||||
type="url"
|
||||
inputMode="url"
|
||||
defaultValue="github.com/langgenius"
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
<FieldError match>Enter a full URL including https://.</FieldError>
|
||||
</FieldRoot>
|
||||
</div>
|
||||
<div className="grid gap-1">
|
||||
<label className="text-text-secondary system-sm-medium" htmlFor="disabled-state">Disabled</label>
|
||||
<Input id="disabled-state" disabled name="disabledEmail" type="email" inputMode="email" placeholder="name@example.com…" autoComplete="email" spellCheck={false} />
|
||||
</div>
|
||||
<div className="grid gap-1">
|
||||
<label className="text-text-secondary system-sm-medium" htmlFor="readonly-state">Read-only</label>
|
||||
<Input id="readonly-state" readOnly name="endpoint" type="url" inputMode="url" defaultValue="https://api.example.com" autoComplete="url" spellCheck={false} />
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
export const WithField: Story = {
|
||||
render: () => (
|
||||
<Form aria-label="Account form" className="grid w-80 gap-4" onFormSubmit={() => undefined}>
|
||||
<FieldRoot name="email">
|
||||
<FieldLabel>Email</FieldLabel>
|
||||
<Input type="email" inputMode="email" required autoComplete="email" placeholder="name@example.com…" spellCheck={false} />
|
||||
<FieldDescription>Used for account notifications.</FieldDescription>
|
||||
<FieldError match="valueMissing">Email is required.</FieldError>
|
||||
<FieldError match="typeMismatch">Enter a valid email address.</FieldError>
|
||||
</FieldRoot>
|
||||
<FieldRoot name="repositoryUrl">
|
||||
<FieldLabel>Repository URL</FieldLabel>
|
||||
<Input type="url" inputMode="url" required autoComplete="off" placeholder="https://github.com/langgenius/dify…" spellCheck={false} />
|
||||
<FieldDescription>Use the full GitHub repository URL.</FieldDescription>
|
||||
<FieldError match="valueMissing">Repository URL is required.</FieldError>
|
||||
<FieldError match="typeMismatch">Enter a valid URL.</FieldError>
|
||||
</FieldRoot>
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit" variant="primary">Save Settings</Button>
|
||||
</div>
|
||||
</Form>
|
||||
),
|
||||
}
|
||||
31
packages/dify-ui/src/input/index.tsx
Normal file
31
packages/dify-ui/src/input/index.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
'use client'
|
||||
|
||||
import type { Input as BaseInputNS } from '@base-ui/react/input'
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import { Input as BaseInput } from '@base-ui/react/input'
|
||||
import { cn } from '../cn'
|
||||
import { textControlVariants } from '../text-control-variants'
|
||||
|
||||
export type InputSize = NonNullable<VariantProps<typeof textControlVariants>['size']>
|
||||
|
||||
export type InputProps
|
||||
= Omit<BaseInputNS.Props, 'className' | 'size'>
|
||||
& VariantProps<typeof textControlVariants>
|
||||
& {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export type InputChangeEventDetails = BaseInputNS.ChangeEventDetails
|
||||
|
||||
export function Input({
|
||||
className,
|
||||
size = 'medium',
|
||||
...props
|
||||
}: InputProps) {
|
||||
return (
|
||||
<BaseInput
|
||||
className={cn(textControlVariants({ size }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
27
packages/dify-ui/src/text-control-variants.ts
Normal file
27
packages/dify-ui/src/text-control-variants.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
export const textControlVariants = cva(
|
||||
[
|
||||
'w-full appearance-none border border-transparent bg-components-input-bg-normal text-components-input-text-filled caret-primary-600 outline-hidden transition-[background-color,border-color,box-shadow]',
|
||||
'placeholder:text-components-input-text-placeholder',
|
||||
'hover:border-components-input-border-hover hover:bg-components-input-bg-hover',
|
||||
'focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs',
|
||||
'data-invalid:border-components-input-border-destructive data-invalid:bg-components-input-bg-destructive',
|
||||
'read-only:cursor-default read-only:shadow-none read-only:hover:border-transparent read-only:hover:bg-components-input-bg-normal read-only:focus:border-transparent read-only:focus:bg-components-input-bg-normal read-only:focus:shadow-none',
|
||||
'disabled:cursor-not-allowed disabled:border-transparent disabled:bg-components-input-bg-disabled disabled:text-components-input-text-filled-disabled',
|
||||
'disabled:hover:border-transparent disabled:hover:bg-components-input-bg-disabled',
|
||||
'motion-reduce:transition-none',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
small: 'rounded-md px-2 py-[3px] system-xs-regular',
|
||||
medium: 'rounded-lg px-3 py-[7px] system-sm-regular',
|
||||
large: 'rounded-[10px] px-4 py-[7px] system-md-regular',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'medium',
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -2,12 +2,12 @@ import type { FC } from 'react'
|
||||
import type { VersionHistory } from '@/types/workflow'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
|
||||
import { FieldControl, FieldLabel, FieldRoot } from '@langgenius/dify-ui/field'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '../../base/input'
|
||||
import Textarea from '../../base/textarea'
|
||||
|
||||
type VersionInfoModalProps = {
|
||||
@@ -57,10 +57,6 @@ const VersionInfoModal: FC<VersionInfoModalProps> = ({
|
||||
onClose()
|
||||
}
|
||||
|
||||
const handleTitleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTitle(e.target.value)
|
||||
}, [])
|
||||
|
||||
const handleDescriptionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setReleaseNotes(e.target.value)
|
||||
}, [])
|
||||
@@ -89,17 +85,16 @@ const VersionInfoModal: FC<VersionInfoModalProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-y-4 px-6 py-3">
|
||||
<div className="flex flex-col gap-y-1">
|
||||
<div className="flex h-6 items-center system-sm-semibold text-text-secondary">
|
||||
<FieldRoot name="title" invalid={titleError} className="gap-y-1">
|
||||
<FieldLabel className="flex h-6 items-center py-0 system-sm-semibold text-text-secondary">
|
||||
{t('versionHistory.editField.title', { ns: 'workflow' })}
|
||||
</div>
|
||||
<Input
|
||||
</FieldLabel>
|
||||
<FieldControl
|
||||
value={title}
|
||||
placeholder={`${t('versionHistory.nameThisVersion', { ns: 'workflow' })}${t('panel.optional', { ns: 'workflow' })}`}
|
||||
onChange={handleTitleChange}
|
||||
destructive={titleError}
|
||||
onValueChange={setTitle}
|
||||
/>
|
||||
</div>
|
||||
</FieldRoot>
|
||||
<div className="flex flex-col gap-y-1">
|
||||
<div className="flex h-6 items-center system-sm-semibold text-text-secondary">
|
||||
{t('versionHistory.editField.releaseNotes', { ns: 'workflow' })}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@langgenius/dify-ui/dropdown-menu'
|
||||
import { FieldControl, FieldLabel, FieldRoot } from '@langgenius/dify-ui/field'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import {
|
||||
Tooltip,
|
||||
@@ -30,11 +31,10 @@ import {
|
||||
} from '@langgenius/dify-ui/tooltip'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useId, useMemo, useState } from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { AppTypeIcon } from '@/app/components/app/type-selector'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { UserAvatarList } from '@/app/components/base/user-avatar-list'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
@@ -209,7 +209,6 @@ const AppCardOperationsMenuContent: React.FC<AppCardOperationsMenuContentProps>
|
||||
|
||||
const AppCard = ({ app, onlineUsers = [], onRefresh, onOpenTagManagement = () => {} }: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const deleteAppNameInputId = useId()
|
||||
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
|
||||
const { isCurrentWorkspaceEditor } = useAppContext()
|
||||
const { onPlanInfoChanged } = useProviderContext()
|
||||
@@ -631,8 +630,8 @@ const AppCard = ({ app, onlineUsers = [], onRefresh, onOpenTagManagement = () =>
|
||||
<AlertDialogDescription className="w-full system-md-regular wrap-break-word whitespace-pre-wrap text-text-tertiary">
|
||||
{t('deleteAppConfirmContent', { ns: 'app' })}
|
||||
</AlertDialogDescription>
|
||||
<div className="mt-2">
|
||||
<label htmlFor={deleteAppNameInputId} className="mb-1 block system-sm-regular text-text-secondary">
|
||||
<FieldRoot name="confirm-app-name" className="mt-2">
|
||||
<FieldLabel className="mb-1 block py-0 system-sm-regular text-text-secondary">
|
||||
<Trans
|
||||
i18nKey="deleteAppConfirmInputLabel"
|
||||
ns="app"
|
||||
@@ -641,19 +640,17 @@ const AppCard = ({ app, onlineUsers = [], onRefresh, onOpenTagManagement = () =>
|
||||
appName: <span className="system-sm-semibold text-text-primary" translate="no" />,
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<Input
|
||||
id={deleteAppNameInputId}
|
||||
name="confirm-app-name"
|
||||
</FieldLabel>
|
||||
<FieldControl
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
placeholder={t('deleteAppConfirmInputPlaceholder', { ns: 'app' })}
|
||||
value={confirmDeleteInput}
|
||||
onChange={e => setConfirmDeleteInput(e.target.value)}
|
||||
onValueChange={setConfirmDeleteInput}
|
||||
className="border-components-input-border-hover bg-components-input-bg-normal focus:border-components-input-border-active focus:bg-components-input-bg-active"
|
||||
/>
|
||||
</div>
|
||||
</FieldRoot>
|
||||
</div>
|
||||
<AlertDialogActions>
|
||||
<AlertDialogCancelButton type="button" disabled={isDeleting}>
|
||||
|
||||
@@ -22,6 +22,11 @@ export const inputVariants = cva(
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* @deprecated Use `@langgenius/dify-ui/input` for primitive inputs and
|
||||
* `@langgenius/dify-ui/field` for form composition. Search inputs should use
|
||||
* a dedicated composition built on the primitive input.
|
||||
*/
|
||||
export type InputProps = {
|
||||
showLeftIcon?: boolean
|
||||
showClearIcon?: boolean
|
||||
@@ -36,6 +41,11 @@ export type InputProps = {
|
||||
|
||||
const removeLeadingZeros = (value: string) => value.replace(/^(-?)0+(?=\d)/, '$1')
|
||||
|
||||
/**
|
||||
* @deprecated Use `@langgenius/dify-ui/input` for primitive inputs and
|
||||
* `@langgenius/dify-ui/field` for form composition. Search inputs should use
|
||||
* a dedicated composition built on the primitive input.
|
||||
*/
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(({
|
||||
size,
|
||||
disabled,
|
||||
|
||||
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event'
|
||||
import CrawledResult from '../base/crawled-result'
|
||||
import CrawledResultItem from '../base/crawled-result-item'
|
||||
import Header from '../base/header'
|
||||
import Input from '../base/input'
|
||||
import Input from '../base/text-input'
|
||||
|
||||
const createCrawlResultItem = (overrides: Partial<CrawlResultItem> = {}): CrawlResultItem => ({
|
||||
title: 'Test Page Title',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import Input from '../input'
|
||||
import Input from '../text-input'
|
||||
|
||||
describe('WebsiteInput', () => {
|
||||
const onChange = vi.fn()
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { FC } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import * as React from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import Input from './input'
|
||||
import Input from './text-input'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import Input from './input'
|
||||
import Input from './text-input'
|
||||
|
||||
const I18N_PREFIX = 'stepOne.website'
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import Input from '../../base/input'
|
||||
|
||||
const I18N_PREFIX = 'stepOne.website'
|
||||
|
||||
@@ -21,9 +21,6 @@ const UrlInput: FC<Props> = ({
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const [url, setUrl] = useState('')
|
||||
const handleUrlChange = useCallback((url: string | number) => {
|
||||
setUrl(url as string)
|
||||
}, [])
|
||||
const handleOnRun = useCallback(() => {
|
||||
if (isRunning)
|
||||
return
|
||||
@@ -34,8 +31,9 @@ const UrlInput: FC<Props> = ({
|
||||
<div className="flex items-center justify-between">
|
||||
<Input
|
||||
value={url}
|
||||
onChange={handleUrlChange}
|
||||
onValueChange={setUrl}
|
||||
placeholder={docLink()}
|
||||
size="small"
|
||||
/>
|
||||
<Button
|
||||
variant="primary"
|
||||
|
||||
@@ -48,10 +48,21 @@ const FLOATING_UI_RESTRICTED_IMPORT_PATTERNS = [
|
||||
},
|
||||
]
|
||||
|
||||
const LEGACY_WEB_INPUT_RESTRICTED_IMPORT_PATTERNS = [
|
||||
{
|
||||
group: [
|
||||
'**/base/input',
|
||||
'**/base/input/*',
|
||||
],
|
||||
message: 'Do not import the deprecated web base Input. Use @langgenius/dify-ui/input for standalone inputs, and @langgenius/dify-ui/field for labelled or validated form composition.',
|
||||
},
|
||||
]
|
||||
|
||||
export const WEB_RESTRICTED_IMPORT_PATTERNS = [
|
||||
...NEXT_PLATFORM_RESTRICTED_IMPORT_PATTERNS,
|
||||
...BASE_UI_RESTRICTED_IMPORT_PATTERNS,
|
||||
...FLOATING_UI_RESTRICTED_IMPORT_PATTERNS,
|
||||
...LEGACY_WEB_INPUT_RESTRICTED_IMPORT_PATTERNS,
|
||||
]
|
||||
|
||||
export const HYOBAN_PREFER_TAILWIND_ICONS_OPTIONS = {
|
||||
|
||||
Reference in New Issue
Block a user