diff --git a/packages/dify-ui/README.md b/packages/dify-ui/README.md index f85108ad7a4..8597cf73c9e 100644 --- a/packages/dify-ui/README.md +++ b/packages/dify-ui/README.md @@ -229,7 +229,7 @@ Convert Figma output such as `rounded-[var(--radius/sm, 6px)]` to the mapped Tai ## Overlay & portal contract -Overlay primitives render their floating surfaces inside a [Base UI Portal] attached to `document.body`. This is the Base UI default — see the upstream [Portals][Base UI Portal] docs for the underlying behavior. Convenience content components such as `DialogContent`, `PopoverContent`, and `SelectContent` own their portal internally; primitives with explicit portal anatomy such as `Drawer` expose the matching `DrawerPortal` part so consumers can compose the full Base UI structure. +Overlay primitives render their floating surfaces inside a [Base UI Portal] attached to `document.body`. This is the Base UI default — see the upstream [Portals][Base UI Portal] docs for the underlying behavior. Convenience content components such as `DialogContent`, `PopoverContent`, and `SelectContent` own their portal internally. Primitives with explicit anatomy expose their constituent parts so consumers can compose the Base UI structure and put behavior on its owner. ### Root isolation requirement diff --git a/packages/dify-ui/src/autocomplete/__tests__/index.spec.tsx b/packages/dify-ui/src/autocomplete/__tests__/index.spec.tsx index fb783cbb346..a026df78d6b 100644 --- a/packages/dify-ui/src/autocomplete/__tests__/index.spec.tsx +++ b/packages/dify-ui/src/autocomplete/__tests__/index.spec.tsx @@ -3,7 +3,6 @@ import { render } from 'vitest-browser-react' import { Autocomplete, AutocompleteClear, - AutocompleteContent, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, @@ -13,6 +12,9 @@ import { AutocompleteItemIndicator, AutocompleteItemText, AutocompleteList, + AutocompletePopup, + AutocompletePortal, + AutocompletePositioner, AutocompleteSeparator, AutocompleteStatus, AutocompleteTrigger, @@ -41,28 +43,23 @@ const renderAutocomplete = ({ - - 2 suggestions - - - Workflow - - - - Dataset - - - No suggestions - + + + + 2 suggestions + + + Workflow + + + + Dataset + + + No suggestions + + + )} , @@ -182,30 +179,34 @@ describe('Autocomplete wrappers', () => { .toHaveAttribute('data-align', 'start') }) - it('should apply custom placement side and passthrough popup props', async () => { + it('should apply custom placement and popup props to their owning parts', async () => { const onPopupClick = vi.fn() const screen = await renderWithSafeViewport( - - - - Workflow - - - + + + + + + Workflow + + + + + , ) @@ -223,18 +224,27 @@ describe('Autocomplete wrappers', () => { - - - - Resources - - - Workflow - - - - - + + + + + + + Resources + + + + Workflow + + + + + + + , ) @@ -253,15 +263,19 @@ describe('Autocomplete wrappers', () => { - - > - {(item) => ( - - {item} - - )} - - + + + + > + {(item) => ( + + {item} + + )} + + + + , ) diff --git a/packages/dify-ui/src/autocomplete/index.stories.tsx b/packages/dify-ui/src/autocomplete/index.stories.tsx index 6afe19dd732..1b8e429b177 100644 --- a/packages/dify-ui/src/autocomplete/index.stories.tsx +++ b/packages/dify-ui/src/autocomplete/index.stories.tsx @@ -6,7 +6,6 @@ import { Autocomplete, AutocompleteClear, AutocompleteCollection, - AutocompleteContent, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, @@ -15,6 +14,9 @@ import { AutocompleteItem, AutocompleteItemText, AutocompleteList, + AutocompletePopup, + AutocompletePortal, + AutocompletePositioner, AutocompleteSeparator, AutocompleteStatus, AutocompleteTrigger, @@ -369,12 +371,16 @@ const BasicTagAutocomplete = ({ size = 'medium' }: { size?: 'small' | 'medium' | - - > - {(item) => } - - No tag suggestion. Keep the typed value. - + + + + > + {(item) => } + + No tag suggestion. Keep the typed value. + + + ) @@ -511,15 +517,16 @@ const AsyncSearchDemo = () => { - - {status} - > - {(item) => } - - + ) @@ -642,29 +649,33 @@ const FuzzyMatchingDemo = () => { - - > - {(item) => ( - - {item.icon && ( - - )} - - No workflow suggestion. Keep typing freely. - + + No workflow suggestion. Keep typing freely. + + + ) @@ -729,12 +740,16 @@ export const InlineAutocomplete: Story = { - - > - {(item) => } - - No inline completion. Continue typing freely. - + + + + > + {(item) => } + + No inline completion. Continue typing freely. + + + ), @@ -761,10 +776,14 @@ export const GroupedSuggestions: Story = { - - - No suggestion. Use the text as entered. - + + + + + No suggestion. Use the text as entered. + + + ), @@ -796,15 +815,19 @@ export const LimitResults: Story = { - - - - - > - {(item) => } - - No suggestion. Submit the typed text instead. - + + + + + + + > + {(item) => } + + No suggestion. Submit the typed text instead. + + + ), @@ -867,11 +890,15 @@ const VirtualizedLongSuggestionsDemo = () => { - - - - No suggestion. Free-form text is still valid. - + + + + + + No suggestion. Free-form text is still valid. + + + ) @@ -907,12 +934,18 @@ export const Empty: Story = { - - > - {(item) => } - - No tag suggestion. The custom text remains valid. - + + + + > + {(item) => } + + + No tag suggestion. The custom text remains valid. + + + + ), @@ -933,11 +966,15 @@ export const DisabledAndReadOnly: Story = { - - > - {(item) => } - - + + + + > + {(item) => } + + + + - - > - {(item) => } - - + + + + > + {(item) => } + + + + ), diff --git a/packages/dify-ui/src/autocomplete/index.tsx b/packages/dify-ui/src/autocomplete/index.tsx index 7d5bbe90aab..d878f500f11 100644 --- a/packages/dify-ui/src/autocomplete/index.tsx +++ b/packages/dify-ui/src/autocomplete/index.tsx @@ -250,56 +250,46 @@ function AutocompleteIcon({ className, children, ...props }: AutocompleteIconPro ) } -type AutocompleteContentProps = { - children: React.ReactNode - placement?: Placement - sideOffset?: number - alignOffset?: number +const AutocompletePortal = BaseAutocomplete.Portal +type AutocompletePortalProps = BaseAutocomplete.Portal.Props + +type AutocompletePositionerProps = Omit< + BaseAutocomplete.Positioner.Props, + 'className' | 'side' | 'align' +> & { className?: string - popupClassName?: string - portalProps?: Omit - positionerProps?: Omit< - BaseAutocomplete.Positioner.Props, - 'children' | 'className' | 'side' | 'align' | 'sideOffset' | 'alignOffset' - > - popupProps?: Omit + placement?: Placement } -function AutocompleteContent({ - children, +function AutocompletePositioner({ + className, placement = 'bottom-start', sideOffset = 4, - alignOffset = 0, - className, - popupClassName, - portalProps, - positionerProps, - popupProps, -}: AutocompleteContentProps) { + ...props +}: AutocompletePositionerProps) { const { side, align } = parsePlacement(placement) return ( - - - - {children} - - - + + ) +} + +type AutocompletePopupProps = Omit & { + className?: string +} + +function AutocompletePopup({ className, ...props }: AutocompletePopupProps) { + return ( + ) } @@ -405,7 +395,6 @@ export { Autocomplete, AutocompleteClear, AutocompleteCollection, - AutocompleteContent, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, @@ -416,6 +405,9 @@ export { AutocompleteItemIndicator, AutocompleteItemText, AutocompleteList, + AutocompletePopup, + AutocompletePortal, + AutocompletePositioner, AutocompleteRow, AutocompleteSeparator, AutocompleteStatus, @@ -429,7 +421,6 @@ export type { AutocompleteChangeEventDetails, AutocompleteClearProps, AutocompleteCollectionProps, - AutocompleteContentProps, AutocompleteEmptyProps, AutocompleteFlatProps, AutocompleteGroupedProps, @@ -442,6 +433,9 @@ export type { AutocompleteItemProps, AutocompleteItemTextProps, AutocompleteListProps, + AutocompletePopupProps, + AutocompletePortalProps, + AutocompletePositionerProps, AutocompleteProps, AutocompleteRowProps, AutocompleteSeparatorProps, diff --git a/web/app/education/apply/institution-field.tsx b/web/app/education/apply/institution-field.tsx index 01a8b34fdb1..a0b8366bddf 100644 --- a/web/app/education/apply/institution-field.tsx +++ b/web/app/education/apply/institution-field.tsx @@ -2,13 +2,15 @@ import type { AutocompleteChangeEventDetails } from '@langgenius/dify-ui/autocom import { Autocomplete, AutocompleteCollection, - AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteInputGroup, AutocompleteItem, AutocompleteItemText, AutocompleteList, + AutocompletePopup, + AutocompletePortal, + AutocompletePositioner, AutocompleteStatus, } from '@langgenius/dify-ui/autocomplete' import { Field, FieldLabel } from '@langgenius/dify-ui/field' @@ -101,58 +103,63 @@ const InstitutionField = ({ value, onValueChange }: InstitutionFieldProps) => { placeholder={t(($) => $['form.schoolName.placeholder'], { ns: 'education' })} /> - - - > - {(institution) => ( - - {institution} - - )} - - {footerState !== null ? ( - - ) : null} - - - {shouldShowEmpty ? t(($) => $['form.schoolName.noResults'], { ns: 'education' }) : null} - - - - {shouldOpenPopup && isLoading - ? t(($) => $.loading, { ns: 'common' }) - : footerState === 'error' - ? t(($) => $['dynamicSelect.error'], { ns: 'common' }) + + + {shouldShowEmpty + ? t(($) => $['form.schoolName.noResults'], { ns: 'education' }) : null} - - - + + + + {shouldOpenPopup && isLoading + ? t(($) => $.loading, { ns: 'common' }) + : footerState === 'error' + ? t(($) => $['dynamicSelect.error'], { ns: 'common' }) + : null} + + + + + )