mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-09 10:56:52 +08:00
feat: dark mode for knowledge (#15236)
This commit is contained in:
@@ -34,10 +34,11 @@ export type ButtonProps = {
|
||||
destructive?: boolean
|
||||
loading?: boolean
|
||||
styleCss?: CSSProperties
|
||||
spinnerClassName?: string
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, destructive, loading, styleCss, children, ...props }, ref) => {
|
||||
({ className, variant, size, destructive, loading, styleCss, children, spinnerClassName, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
@@ -50,7 +51,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{loading && <Spinner loading={loading} className='!text-white !h-3 !w-3 !border-2 !ml-1' />}
|
||||
{loading && <Spinner loading={loading} className={classNames('!text-white !h-3 !w-3 !border-2 !ml-1', spinnerClassName)} />}
|
||||
</button>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"rx": "5",
|
||||
"fill": "#F2F4F7"
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"width": "233",
|
||||
"height": "10",
|
||||
"rx": "3",
|
||||
"fill": "#EAECF0"
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
@@ -46,7 +46,7 @@
|
||||
"width": "345",
|
||||
"height": "6",
|
||||
"rx": "3",
|
||||
"fill": "#F2F4F7"
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
.setting-icon {
|
||||
background: url(./assets/setting.svg) center center no-repeat;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import s from './base.module.css'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import WorkspaceSelector from './workspace-selector'
|
||||
import SearchInput from './search-input'
|
||||
import PageSelector from './page-selector'
|
||||
import cn from '@/utils/classnames'
|
||||
import { preImportNotionPages } from '@/service/datasets'
|
||||
import { NotionConnector } from '@/app/components/datasets/create/step-one'
|
||||
import type { DataSourceNotionPageMap, DataSourceNotionWorkspace, NotionPage } from '@/models/common'
|
||||
@@ -88,23 +87,24 @@ const NotionPageSelector = ({
|
||||
}, [firstWorkspaceId])
|
||||
|
||||
return (
|
||||
<div className='bg-gray-25 border border-gray-200 rounded-xl'>
|
||||
<div className='bg-background-default-subtle border border-components-panel-border rounded-xl'>
|
||||
{
|
||||
data?.notion_info?.length
|
||||
? (
|
||||
<>
|
||||
<div className='flex items-center pl-[10px] pr-2 h-11 bg-white border-b border-b-gray-200 rounded-t-xl'>
|
||||
<WorkspaceSelector
|
||||
value={currentWorkspaceId || firstWorkspaceId}
|
||||
items={notionWorkspaces}
|
||||
onSelect={handleSelectWorkspace}
|
||||
/>
|
||||
<div className='mx-1 w-[1px] h-3 bg-gray-200' />
|
||||
<div
|
||||
className={cn(s['setting-icon'], 'w-6 h-6 cursor-pointer')}
|
||||
onClick={() => setShowAccountSettingModal({ payload: 'data-source', onCancelCallback: mutate })}
|
||||
/>
|
||||
<div className='grow' />
|
||||
<div className='flex items-center gap-x-2 p-2 h-12 bg-components-panel-bg border-b border-b-divider-regular rounded-t-xl'>
|
||||
<div className='grow flex items-center gap-x-1'>
|
||||
<WorkspaceSelector
|
||||
value={currentWorkspaceId || firstWorkspaceId}
|
||||
items={notionWorkspaces}
|
||||
onSelect={handleSelectWorkspace}
|
||||
/>
|
||||
<div className='mx-1 w-[1px] h-3 bg-divider-regular' />
|
||||
<RiEqualizer2Line
|
||||
className='w-4 h-4 cursor-pointer text-text-tertiary'
|
||||
onClick={() => setShowAccountSettingModal({ payload: 'data-source', onCancelCallback: mutate })}
|
||||
/>
|
||||
</div>
|
||||
<SearchInput
|
||||
value={searchValue}
|
||||
onChange={handleSearchValueChange}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
.arrow {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: url(../assets/down-arrow.svg) center center no-repeat;
|
||||
background-size: 16px 16px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.arrow-expand {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
.preview-item {
|
||||
background-color: #eff4ff;
|
||||
border: 1px solid #D1E0FF;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import { memo, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FixedSizeList as List, areEqual } from 'react-window'
|
||||
import type { ListChildComponentProps } from 'react-window'
|
||||
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||
import Checkbox from '../../checkbox'
|
||||
import NotionIcon from '../../notion-icon'
|
||||
import s from './index.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
||||
|
||||
@@ -94,10 +94,16 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
if (hasChild) {
|
||||
return (
|
||||
<div
|
||||
className={cn(s.arrow, current.expand && s['arrow-expand'], 'shrink-0 mr-1 w-5 h-5 hover:bg-gray-200 rounded-md')}
|
||||
className='flex items-center justify-center shrink-0 mr-1 w-5 h-5 hover:bg-components-button-ghost-bg-hover rounded-md'
|
||||
style={{ marginLeft: current.depth * 8 }}
|
||||
onClick={() => handleToggle(index)}
|
||||
/>
|
||||
>
|
||||
{
|
||||
current.expand
|
||||
? <RiArrowDownSLine className='w-4 h-4 text-text-tertiary' />
|
||||
: <RiArrowRightSLine className='w-4 h-4 text-text-tertiary' />
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (current.parent_id === 'root' || !pagesMap[current.parent_id]) {
|
||||
@@ -112,14 +118,12 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('group flex items-center pl-2 pr-[2px] rounded-md border border-transparent hover:bg-gray-100 cursor-pointer', previewPageId === current.page_id && s['preview-item'])}
|
||||
className={cn('group flex items-center pl-2 pr-[2px] rounded-md hover:bg-state-base-hover cursor-pointer',
|
||||
previewPageId === current.page_id && 'bg-state-base-hover')}
|
||||
style={{ ...style, top: style.top as number + 8, left: 8, right: 8, width: 'calc(100% - 16px)' }}
|
||||
>
|
||||
<Checkbox
|
||||
className={cn(
|
||||
'shrink-0 mr-2 group-hover:border-primary-600 group-hover:border-[2px]',
|
||||
disabled && 'group-hover:border-transparent',
|
||||
)}
|
||||
className='shrink-0 mr-2'
|
||||
checked={checkedIds.has(current.page_id)}
|
||||
disabled={disabled}
|
||||
onCheck={() => {
|
||||
@@ -135,7 +139,7 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
src={current.page_icon}
|
||||
/>
|
||||
<div
|
||||
className='grow text-sm font-medium text-gray-700 truncate'
|
||||
className='grow text-[13px] leading-4 font-medium text-text-secondary truncate'
|
||||
title={current.page_name}
|
||||
>
|
||||
{current.page_name}
|
||||
@@ -143,7 +147,9 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
{
|
||||
canPreview && (
|
||||
<div
|
||||
className='shrink-0 hidden group-hover:flex items-center ml-1 px-2 h-6 rounded-md text-xs font-medium text-gray-500 cursor-pointer hover:bg-gray-50 hover:text-gray-700'
|
||||
className='shrink-0 hidden group-hover:flex items-center ml-1 px-2 h-6 rounded-md text-xs leading-4 font-medium text-components-button-secondary-text
|
||||
cursor-pointer bg-components-button-secondary-bg border-[0.5px] border-components-button-secondary-border shadow-xs shadow-shadow-shadow-3
|
||||
backdrop-blur-[10px] hover:bg-components-button-secondary-bg-hover hover:border-components-button-secondary-border-hover'
|
||||
onClick={() => handlePreview(index)}>
|
||||
{t('common.dataSource.notion.selector.preview')}
|
||||
</div>
|
||||
@@ -152,7 +158,7 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
{
|
||||
searchValue && (
|
||||
<div
|
||||
className='shrink-0 ml-1 max-w-[120px] text-xs text-gray-400 truncate'
|
||||
className='shrink-0 ml-1 max-w-[120px] text-xs text-text-quaternary truncate'
|
||||
title={breadCrumbs.join(' / ')}
|
||||
>
|
||||
{breadCrumbs.join(' / ')}
|
||||
@@ -278,7 +284,7 @@ const PageSelector = ({
|
||||
|
||||
if (!currentDataList.length) {
|
||||
return (
|
||||
<div className='flex items-center justify-center h-[296px] text-[13px] text-gray-500'>
|
||||
<div className='flex items-center justify-center h-[296px] text-[13px] text-text-tertiary'>
|
||||
{t('common.dataSource.notion.selector.noSearchResult')}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.search-icon {
|
||||
background: url(../assets/search.svg) center center;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
background: url(../assets/clear.svg) center center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
flex-basis: 200px;
|
||||
width: 0;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import s from './index.module.css'
|
||||
import { RiCloseCircleFill, RiSearchLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type SearchInputProps = {
|
||||
@@ -19,18 +19,18 @@ const SearchInput = ({
|
||||
}, [onChange])
|
||||
|
||||
return (
|
||||
<div className={cn(s['input-wrapper'], 'flex items-center px-2 h-7 rounded-md', `${value ? 'bg-white' : 'bg-gray-100'}`)}>
|
||||
<div className={cn(s['search-icon'], 'mr-[6px] w-4 h-4')} />
|
||||
<div className={cn('w-[200px] flex items-center p-2 h-8 rounded-lg bg-components-input-bg-normal')}>
|
||||
<RiSearchLine className={'w-4 h-4 mr-0.5 shrink-0 text-components-input-text-placeholder'} />
|
||||
<input
|
||||
className='grow text-[13px] bg-inherit border-0 outline-0 appearance-none'
|
||||
className='min-w-0 grow px-1 text-[13px] leading-[16px] bg-transparent text-components-input-text-filled placeholder:text-components-input-text-placeholder border-0 outline-0 appearance-none'
|
||||
value={value}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
|
||||
placeholder={t('common.dataSource.notion.selector.searchPages') || ''}
|
||||
/>
|
||||
{
|
||||
value && (
|
||||
<div
|
||||
className={cn(s['clear-icon'], 'ml-1 w-4 h-4 cursor-pointer')}
|
||||
<RiCloseCircleFill
|
||||
className={'w-4 h-4 shrink-0 cursor-pointer text-components-input-text-placeholder'}
|
||||
onClick={handleClear}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
.down-arrow {
|
||||
background: url(../assets/down-arrow.svg) center center no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.popup {
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
z-index: 10;
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Fragment } from 'react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import NotionIcon from '../../notion-icon'
|
||||
import s from './index.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { DataSourceNotionWorkspace } from '@/models/common'
|
||||
|
||||
type WorkspaceSelectorProps = {
|
||||
@@ -25,15 +24,15 @@ export default function WorkspaceSelector({
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
<Menu.Button className={`flex items-center justify-center h-7 rounded-md hover:bg-gray-50 ${open && 'bg-gray-50'} cursor-pointer`}>
|
||||
<Menu.Button className={`flex items-center justify-center h-7 p-1 pr-2 rounded-md hover:bg-state-base-hover ${open && 'bg-state-base-hover'} cursor-pointer`}>
|
||||
<NotionIcon
|
||||
className='ml-1 mr-2'
|
||||
className='mr-2'
|
||||
src={currentWorkspace?.workspace_icon}
|
||||
name={currentWorkspace?.workspace_name}
|
||||
/>
|
||||
<div className='mr-1 w-[90px] text-left text-sm font-medium text-gray-700 truncate' title={currentWorkspace?.workspace_name}>{currentWorkspace?.workspace_name}</div>
|
||||
<div className='mr-1 px-1 h-[18px] bg-primary-50 rounded-lg text-xs font-medium text-primary-600'>{currentWorkspace?.pages.length}</div>
|
||||
<div className={cn(s['down-arrow'], 'mr-2 w-3 h-3')} />
|
||||
<div className='mr-1 w-[90px] text-left text-sm font-medium text-text-secondary truncate' title={currentWorkspace?.workspace_name}>{currentWorkspace?.workspace_name}</div>
|
||||
{/* <div className='mr-1 px-1 h-[18px] bg-primary-50 rounded-lg text-xs font-medium text-text-accent'>{currentWorkspace?.pages.length}</div> */}
|
||||
<RiArrowDownSLine className='w-4 h-4 text-text-secondary' />
|
||||
</Menu.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@@ -45,19 +44,16 @@ export default function WorkspaceSelector({
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
className={cn(
|
||||
s.popup,
|
||||
`absolute left-0 top-8 w-80
|
||||
origin-top-right rounded-lg bg-white
|
||||
border-[0.5px] border-gray-200`,
|
||||
)}
|
||||
className='absolute left-0 top-8 z-10 w-80
|
||||
origin-top-right rounded-lg bg-components-panel-bg-blur
|
||||
border-[0.5px] border-components-panel-border shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'
|
||||
>
|
||||
<div className="p-1 max-h-50 overflow-auto">
|
||||
{
|
||||
items.map(item => (
|
||||
<Menu.Item key={item.workspace_id}>
|
||||
<div
|
||||
className='flex items-center px-3 h-9 hover:bg-gray-50 cursor-pointer'
|
||||
className='flex items-center px-3 h-9 rounded-lg hover:bg-state-base-hover cursor-pointer'
|
||||
onClick={() => onSelect(item.workspace_id)}
|
||||
>
|
||||
<NotionIcon
|
||||
@@ -65,8 +61,8 @@ export default function WorkspaceSelector({
|
||||
src={item.workspace_icon}
|
||||
name={item.workspace_name}
|
||||
/>
|
||||
<div className='grow mr-2 text-sm text-gray-700 truncate' title={item.workspace_name}>{item.workspace_name}</div>
|
||||
<div className='shrink-0 text-xs font-medium text-primary-600'>
|
||||
<div className='grow mr-2 system-sm-medium text-text-secondary truncate' title={item.workspace_name}>{item.workspace_name}</div>
|
||||
<div className='shrink-0 system-xs-medium text-text-accent'>
|
||||
{item.pages.length} {t('common.dataSource.notion.selector.pageSelected')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { FC } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiSearchLine } from '@remixicon/react'
|
||||
import { RiCloseCircleFill, RiSearchLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
|
||||
type SearchInputProps = {
|
||||
placeholder?: string
|
||||
@@ -27,22 +26,19 @@ const SearchInput: FC<SearchInputProps> = ({
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
'group flex items-center px-2 h-8 rounded-lg bg-gray-200 hover:bg-gray-300 border border-transparent overflow-hidden',
|
||||
focus && '!bg-white hover:bg-white shadow-xs !border-gray-300',
|
||||
!focus && value && 'hover:!bg-gray-200 hover:!shadow-xs hover:!border-black/5',
|
||||
'group flex items-center px-2 h-8 rounded-lg bg-components-input-bg-normal hover:bg-components-input-bg-hover border-none overflow-hidden',
|
||||
focus && '!bg-components-input-bg-active',
|
||||
white && '!bg-white hover:!bg-white shadow-xs !border-gray-300 hover:!border-gray-300',
|
||||
className,
|
||||
)}>
|
||||
<div className="pointer-events-none shrink-0 flex items-center mr-1.5 justify-center w-4 h-4">
|
||||
<RiSearchLine className="h-3.5 w-3.5 text-gray-500" aria-hidden="true" />
|
||||
<RiSearchLine className="h-4 w-4 text-components-input-text-placeholder" aria-hidden="true" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="query"
|
||||
className={cn(
|
||||
'grow block h-[18px] bg-gray-200 border-0 text-gray-700 text-[13px] placeholder:text-gray-500 appearance-none outline-none group-hover:bg-gray-300 caret-blue-600',
|
||||
focus && '!bg-white hover:bg-white group-hover:bg-white placeholder:!text-gray-400',
|
||||
!focus && value && 'hover:!bg-gray-200 group-hover:!bg-gray-200',
|
||||
'grow block h-[18px] bg-transparent border-0 text-components-input-text-filled system-sm-regular placeholder:text-components-input-text-placeholder appearance-none outline-none caret-#295EFF',
|
||||
white && '!bg-white hover:!bg-white group-hover:!bg-white placeholder:!text-gray-400',
|
||||
)}
|
||||
placeholder={placeholder || t('common.operation.search')!}
|
||||
@@ -71,7 +67,7 @@ const SearchInput: FC<SearchInputProps> = ({
|
||||
setInternalValue('')
|
||||
}}
|
||||
>
|
||||
<XCircle className='w-3.5 h-3.5 text-gray-400 group-hover/clear:text-gray-600' />
|
||||
<RiCloseCircleFill className='w-4 h-4 text-text-quaternary group-hover/clear:text-text-tertiary' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -234,8 +234,8 @@ const TagSelector: FC<TagSelectorProps> = ({
|
||||
<div className={cn(
|
||||
'group/tip relative w-full flex items-center gap-1 px-2 py-[7px] rounded-md cursor-pointer hover:bg-state-base-hover',
|
||||
)}>
|
||||
<Tag01 className='shrink-0 w-3 h-3' />
|
||||
<div className='grow text-xs text-start leading-[18px] font-normal truncate'>
|
||||
<Tag01 className='shrink-0 w-3 h-3 text-components-input-text-placeholder' />
|
||||
<div className='grow text-start system-sm-regular text-components-input-text-placeholder truncate'>
|
||||
{!triggerContent ? t('common.tag.addTag') : triggerContent}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user