refactor: enhance layout and scrolling behavior in various modals for improved user experience (#36210)

Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Coding On Star 2026-05-15 15:17:23 +08:00 committed by GitHub
parent b41338cd08
commit b04a3851cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 85 additions and 95 deletions

View File

@ -2273,11 +2273,6 @@
"count": 1
}
},
"web/app/components/header/account-setting/members-page/invite-modal/index.tsx": {
"react/set-state-in-effect": {
"count": 3
}
},
"web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx": {
"erasable-syntax-only/enums": {
"count": 1

View File

@ -46,78 +46,80 @@ const CustomizeModal: FC<IShareLinkProps> = ({
return (
<Dialog open={isShow} onOpenChange={open => !open && onClose()}>
<DialogContent className="max-h-[calc(100dvh-2rem)] w-[640px] overflow-visible">
<DialogTitle className="title-2xl-semi-bold text-text-primary">
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[640px] flex-col overflow-hidden!">
<DialogTitle className="shrink-0 title-2xl-semi-bold text-text-primary">
{t(`${prefixCustomize}.title`, { ns: 'appOverview' })}
</DialogTitle>
<DialogDescription className="mt-2 body-md-regular text-text-secondary">
<DialogDescription className="mt-2 shrink-0 body-md-regular text-text-secondary">
{t(`${prefixCustomize}.explanation`, { ns: 'appOverview' })}
</DialogDescription>
<DialogCloseButton />
<div className="mt-4 w-full rounded-lg border-[0.5px] border-components-panel-border px-6 py-5">
<Tag bordered={true} hideBg={true} className="border-text-accent-secondary text-text-accent-secondary uppercase">
{t(`${prefixCustomize}.way`, { ns: 'appOverview' })}
{' '}
1
</Tag>
<p className="my-2 system-sm-medium text-text-secondary">{t(`${prefixCustomize}.way1.name`, { ns: 'appOverview' })}</p>
<div className="flex py-4">
<StepNum>1</StepNum>
<div className="flex flex-col">
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step1`, { ns: 'appOverview' })}</div>
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step1Tip`, { ns: 'appOverview' })}</div>
<Button render={<a href={`https://github.com/langgenius/${isChatApp ? 'webapp-conversation' : 'webapp-text-generator'}`} target="_blank" rel="noopener noreferrer" />}>
<GithubIcon className="mr-2 text-text-secondary" />
{t(`${prefixCustomize}.way1.step1Operation`, { ns: 'appOverview' })}
</Button>
<div className="mt-4 min-h-0 flex-1 overflow-y-auto overscroll-contain">
<div className="w-full rounded-lg border-[0.5px] border-components-panel-border px-6 py-5">
<Tag bordered={true} hideBg={true} className="border-text-accent-secondary text-text-accent-secondary uppercase">
{t(`${prefixCustomize}.way`, { ns: 'appOverview' })}
{' '}
1
</Tag>
<p className="my-2 system-sm-medium text-text-secondary">{t(`${prefixCustomize}.way1.name`, { ns: 'appOverview' })}</p>
<div className="flex py-4">
<StepNum>1</StepNum>
<div className="flex flex-col">
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step1`, { ns: 'appOverview' })}</div>
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step1Tip`, { ns: 'appOverview' })}</div>
<Button render={<a href={`https://github.com/langgenius/${isChatApp ? 'webapp-conversation' : 'webapp-text-generator'}`} target="_blank" rel="noopener noreferrer" />}>
<GithubIcon className="mr-2 text-text-secondary" />
{t(`${prefixCustomize}.way1.step1Operation`, { ns: 'appOverview' })}
</Button>
</div>
</div>
</div>
<div className="flex pt-4">
<StepNum>2</StepNum>
<div className="flex flex-col">
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step2`, { ns: 'appOverview' })}</div>
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step2Tip`, { ns: 'appOverview' })}</div>
<Button render={<a href="https://vercel.com/docs/concepts/deployments/git/vercel-for-github" target="_blank" rel="noopener noreferrer" />}>
<div className="mr-1.5 border-t-0 border-r-[7px] border-b-12 border-l-[7px] border-solid border-text-primary border-t-transparent border-r-transparent border-l-transparent"></div>
<span>{t(`${prefixCustomize}.way1.step2Operation`, { ns: 'appOverview' })}</span>
</Button>
<div className="flex pt-4">
<StepNum>2</StepNum>
<div className="flex flex-col">
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step2`, { ns: 'appOverview' })}</div>
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step2Tip`, { ns: 'appOverview' })}</div>
<Button render={<a href="https://vercel.com/docs/concepts/deployments/git/vercel-for-github" target="_blank" rel="noopener noreferrer" />}>
<div className="mr-1.5 border-t-0 border-r-[7px] border-b-12 border-l-[7px] border-solid border-text-primary border-t-transparent border-r-transparent border-l-transparent"></div>
<span>{t(`${prefixCustomize}.way1.step2Operation`, { ns: 'appOverview' })}</span>
</Button>
</div>
</div>
</div>
<div className="flex py-4">
<StepNum>3</StepNum>
<div className="flex w-full flex-col overflow-hidden">
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step3`, { ns: 'appOverview' })}</div>
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step3Tip`, { ns: 'appOverview' })}</div>
<pre className="box-border overflow-x-scroll rounded-lg border-[0.5px] border-components-panel-border bg-background-section px-4 py-3 text-xs font-medium text-text-secondary select-text">
NEXT_PUBLIC_APP_ID=
{`'${appId}'`}
{' '}
<br />
NEXT_PUBLIC_APP_KEY=
{'\'<Web API Key From Dify>\''}
{' '}
<br />
NEXT_PUBLIC_API_URL=
{`'${api_base_url}'`}
</pre>
<div className="flex py-4">
<StepNum>3</StepNum>
<div className="flex w-full flex-col overflow-hidden">
<div className="text-text-primary">{t(`${prefixCustomize}.way1.step3`, { ns: 'appOverview' })}</div>
<div className="mt-1 mb-2 text-xs text-text-tertiary">{t(`${prefixCustomize}.way1.step3Tip`, { ns: 'appOverview' })}</div>
<pre className="box-border overflow-x-scroll rounded-lg border-[0.5px] border-components-panel-border bg-background-section px-4 py-3 text-xs font-medium text-text-secondary select-text">
NEXT_PUBLIC_APP_ID=
{`'${appId}'`}
{' '}
<br />
NEXT_PUBLIC_APP_KEY=
{'\'<Web API Key From Dify>\''}
{' '}
<br />
NEXT_PUBLIC_API_URL=
{`'${api_base_url}'`}
</pre>
</div>
</div>
</div>
</div>
<div className="mt-4 w-full rounded-lg border-[0.5px] border-components-panel-border px-6 py-5">
<Tag bordered={true} hideBg={true} className="border-text-accent-secondary text-text-accent-secondary uppercase">
{t(`${prefixCustomize}.way`, { ns: 'appOverview' })}
{' '}
2
</Tag>
<p className="my-2 system-sm-medium text-text-secondary">{t(`${prefixCustomize}.way2.name`, { ns: 'appOverview' })}</p>
<Button
render={<a href={apiDocLink} target="_blank" rel="noopener noreferrer" />}
className="mt-2"
>
<span className="text-sm text-text-secondary">{t(`${prefixCustomize}.way2.operation`, { ns: 'appOverview' })}</span>
<span aria-hidden="true" className="ml-1 i-heroicons-arrow-top-right-on-square h-4 w-4 shrink-0 text-text-secondary" />
</Button>
</div>
<div className="mt-4 w-full rounded-lg border-[0.5px] border-components-panel-border px-6 py-5">
<Tag bordered={true} hideBg={true} className="border-text-accent-secondary text-text-accent-secondary uppercase">
{t(`${prefixCustomize}.way`, { ns: 'appOverview' })}
{' '}
2
</Tag>
<p className="my-2 system-sm-medium text-text-secondary">{t(`${prefixCustomize}.way2.name`, { ns: 'appOverview' })}</p>
<Button
render={<a href={apiDocLink} target="_blank" rel="noopener noreferrer" />}
className="mt-2"
>
<span className="text-sm text-text-secondary">{t(`${prefixCustomize}.way2.operation`, { ns: 'appOverview' })}</span>
<span aria-hidden="true" className="ml-1 i-heroicons-arrow-top-right-on-square h-4 w-4 shrink-0 text-text-secondary" />
</Button>
</div>
</div>
</DialogContent>
</Dialog>

View File

@ -319,12 +319,12 @@ const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, hiddenIn
onClose()
}}
>
<DialogContent className={cn('max-h-[calc(100dvh-2rem)] w-[640px] overflow-visible', className)}>
<DialogTitle className="title-2xl-semi-bold text-text-primary">
<DialogContent className={cn('flex max-h-[calc(100dvh-2rem)] w-[640px] flex-col overflow-hidden!', className)}>
<DialogTitle className="shrink-0 title-2xl-semi-bold text-text-primary">
{t(`${prefixEmbedded}.title`, { ns: 'appOverview' })}
</DialogTitle>
<DialogCloseButton />
<div className="max-h-[calc(90vh-88px)] overflow-y-auto">
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
{isShow && (
<EmbeddedContent
key={`${appBaseUrl ?? ''}:${accessToken ?? ''}:${JSON.stringify(hiddenInputs ?? [])}`}

View File

@ -251,9 +251,9 @@ const SettingsModal: FC<ISettingsModalProps> = ({
return (
<>
<Dialog open={isShow} onOpenChange={open => !open && onHide()}>
<DialogContent className="max-h-[calc(100dvh-2rem)] w-[520px] overflow-visible p-0">
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[520px] flex-col overflow-hidden! p-0!">
{/* header */}
<div className="pt-5 pr-5 pb-3 pl-6">
<div className="shrink-0 pt-5 pr-5 pb-3 pl-6">
<div className="flex items-center gap-1">
<DialogTitle className="grow title-2xl-semi-bold text-text-primary">{t(`${prefixSettings}.title`, { ns: 'appOverview' })}</DialogTitle>
<DialogCloseButton className="relative top-auto right-auto shrink-0" />
@ -263,7 +263,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
</div>
</div>
{/* form body */}
<div className="space-y-5 px-6 py-3">
<div className="min-h-0 flex-1 space-y-5 overflow-y-auto overscroll-contain px-6 py-3">
{/* name & icon */}
<div className="flex gap-4">
<div className="grow">
@ -474,7 +474,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
)}
</div>
{/* footer */}
<div className="flex justify-end p-6 pt-5">
<div className="flex shrink-0 justify-end p-6 pt-5">
<Button className="mr-2" onClick={onHide}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button variant="primary" onClick={onClickSave} loading={saveLoading}>{t('operation.save', { ns: 'common' })}</Button>
</div>

View File

@ -198,7 +198,7 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
>
<DialogContent
backdropProps={{ forceRender: true }}
className="w-[520px] overflow-visible"
className="w-[520px]"
>
<DialogCloseButton
aria-label={t('operation.close', { ns: 'common' })}

View File

@ -121,9 +121,9 @@ const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({ data, onSave, onCan
onCancel()
}}
>
<DialogContent className="w-[480px]! max-w-none! overflow-visible! rounded-2xl! border-[0.5px]! border-components-panel-border! bg-components-panel-bg! p-0! shadow-xl!">
<div className="relative flex w-full flex-col items-start">
<div className="flex flex-col items-start gap-2 self-stretch pt-6 pr-14 pb-3 pl-6">
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[480px]! max-w-none! flex-col overflow-hidden! rounded-2xl! border-[0.5px]! border-components-panel-border! bg-components-panel-bg! p-0! shadow-xl!">
<div className="relative flex min-h-0 w-full flex-1 flex-col items-start">
<div className="flex shrink-0 flex-col items-start gap-2 self-stretch pt-6 pr-14 pb-3 pl-6">
<DialogTitle className="grow self-stretch title-2xl-semi-bold text-text-primary">
{isEditMode ? t('editExternalAPIFormTitle', { ns: 'dataset' }) : t('createExternalAPI', { ns: 'dataset' })}
</DialogTitle>
@ -173,8 +173,8 @@ const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({ data, onSave, onCan
<ActionButton className="absolute top-5 right-5" onClick={onCancel}>
<RiCloseLine className="h-[18px] w-[18px] shrink-0 text-text-tertiary" />
</ActionButton>
<Form value={formData} onChange={handleDataChange} formSchemas={formSchemas} className="flex flex-col items-start justify-center gap-4 self-stretch px-6 py-3" />
<div className="flex items-center justify-end gap-2 self-stretch p-6 pt-5">
<Form value={formData} onChange={handleDataChange} formSchemas={formSchemas} className="min-h-0 w-full flex-1 overflow-y-auto px-6 py-3" />
<div className="flex shrink-0 items-center justify-end gap-2 self-stretch p-6 pt-5">
<Button type="button" variant="secondary" onClick={onCancel}>
{t('externalAPIForm.cancel', { ns: 'dataset' })}
</Button>
@ -194,7 +194,7 @@ const AddExternalAPIModal: FC<AddExternalAPIModalProps> = ({ data, onSave, onCan
{t('externalAPIForm.save', { ns: 'dataset' })}
</Button>
</div>
<div className="flex items-center justify-center gap-1 self-stretch rounded-b-2xl border-t-[0.5px] border-divider-subtle
<div className="flex shrink-0 items-center justify-center gap-1 self-stretch rounded-b-2xl border-t-[0.5px] border-divider-subtle
bg-background-soft px-2 py-3 system-xs-regular text-text-tertiary"
>
<RiLock2Fill className="h-3 w-3 text-text-quaternary" />

View File

@ -60,7 +60,7 @@ const EditWorkspaceModal = ({ onCancel }: IEditWorkspaceModalProps) => {
onCancel()
}}
>
<DialogContent backdropProps={{ forceRender: true }} className="overflow-visible">
<DialogContent backdropProps={{ forceRender: true }}>
<DialogCloseButton />
<form

View File

@ -6,7 +6,7 @@ import { cn } from '@langgenius/dify-ui/cn'
import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ReactMultiEmail } from 'react-multi-email'
import { emailRegex } from '@/config'
@ -31,16 +31,9 @@ const InviteModal = ({
const licenseLimit = useProviderContextSelector(s => s.licenseLimit)
const refreshLicenseLimit = useProviderContextSelector(s => s.refreshLicenseLimit)
const [emails, setEmails] = useState<string[]>([])
const [isLimited, setIsLimited] = useState(false)
const [isLimitExceeded, setIsLimitExceeded] = useState(false)
const [usedSize, setUsedSize] = useState(licenseLimit.workspace_members.size ?? 0)
useEffect(() => {
const limited = licenseLimit.workspace_members.limit > 0
const used = emails.length + licenseLimit.workspace_members.size
setIsLimited(limited)
setUsedSize(used)
setIsLimitExceeded(limited && (used > licenseLimit.workspace_members.limit))
}, [licenseLimit, emails])
const isLimited = licenseLimit.workspace_members.limit > 0
const usedSize = emails.length + licenseLimit.workspace_members.size
const isLimitExceeded = isLimited && (usedSize > licenseLimit.workspace_members.limit)
const locale = useLocale()
const [role, setRole] = useState<RoleKey>('normal')
@ -85,7 +78,7 @@ const InviteModal = ({
>
<DialogContent
backdropProps={{ forceRender: true }}
className="w-[400px] overflow-visible px-8 py-6"
className="w-[400px] px-8 py-6"
>
<DialogCloseButton className="top-6 right-8" />
<div className="mb-2 pr-8">