Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>tags/0.6.14
| @@ -0,0 +1,54 @@ | |||
| import { pinyin } from 'pinyin-pro' | |||
| export const groupItems = (items, getFirstChar) => { | |||
| const groups = items.reduce((acc, item) => { | |||
| const firstChar = getFirstChar(item) | |||
| if (!firstChar || firstChar.length === 0) | |||
| return acc | |||
| let letter | |||
| // transform Chinese to pinyin | |||
| if (/[\u4E00-\u9FA5]/.test(firstChar)) | |||
| letter = pinyin(firstChar, { pattern: 'first', toneType: 'none' })[0].toUpperCase() | |||
| else | |||
| letter = firstChar.toUpperCase() | |||
| if (!/[A-Z]/.test(letter)) | |||
| letter = '#' | |||
| if (!acc[letter]) | |||
| acc[letter] = [] | |||
| acc[letter].push(item) | |||
| return acc | |||
| }, {}) | |||
| const letters = Object.keys(groups).sort() | |||
| // move '#' to the end | |||
| const hashIndex = letters.indexOf('#') | |||
| if (hashIndex !== -1) { | |||
| letters.splice(hashIndex, 1) | |||
| letters.push('#') | |||
| } | |||
| return { letters, groups } | |||
| } | |||
| const IndexBar = ({ letters, itemRefs }) => { | |||
| const handleIndexClick = (letter) => { | |||
| const element = itemRefs.current[letter] | |||
| if (element) | |||
| element.scrollIntoView({ behavior: 'smooth' }) | |||
| } | |||
| return ( | |||
| <div className="index-bar fixed right-4 top-36 flex flex-col items-center text-xs font-medium text-gray-500"> | |||
| {letters.map(letter => ( | |||
| <div className="hover:text-gray-900 cursor-pointer" key={letter} onClick={() => handleIndexClick(letter)}> | |||
| {letter} | |||
| </div> | |||
| ))} | |||
| </div> | |||
| ) | |||
| } | |||
| export default IndexBar | |||
| @@ -1,11 +1,13 @@ | |||
| import { | |||
| memo, | |||
| useCallback, | |||
| useRef, | |||
| } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import BlockIcon from '../block-icon' | |||
| import { BlockEnum } from '../types' | |||
| import type { ToolWithProvider } from '../types' | |||
| import IndexBar, { groupItems } from './index-bar' | |||
| import type { ToolDefaultValue } from './types' | |||
| import Tooltip from '@/app/components/base/tooltip' | |||
| import Empty from '@/app/components/tools/add-tool-modal/empty' | |||
| @@ -24,6 +26,9 @@ const Blocks = ({ | |||
| const { t } = useTranslation() | |||
| const language = useGetLanguage() | |||
| const { letters, groups: groupedTools } = groupItems(tools, tool => tool.label[language][0]) | |||
| const toolRefs = useRef({}) | |||
| const renderGroup = useCallback((toolWithProvider: ToolWithProvider) => { | |||
| const list = toolWithProvider.tools | |||
| @@ -81,6 +86,18 @@ const Blocks = ({ | |||
| ) | |||
| }, [onSelect, language]) | |||
| const renderLetterGroup = (letter) => { | |||
| const tools = groupedTools[letter] | |||
| return ( | |||
| <div | |||
| key={letter} | |||
| ref={el => (toolRefs.current[letter] = el)} | |||
| > | |||
| {tools.map(renderGroup)} | |||
| </div> | |||
| ) | |||
| } | |||
| return ( | |||
| <div className='p-1 max-w-[320px] max-h-[464px] overflow-y-auto'> | |||
| { | |||
| @@ -90,12 +107,11 @@ const Blocks = ({ | |||
| } | |||
| {!tools.length && showWorkflowEmpty && ( | |||
| <div className='py-10'> | |||
| <Empty/> | |||
| <Empty /> | |||
| </div> | |||
| )} | |||
| { | |||
| !!tools.length && tools.map(renderGroup) | |||
| } | |||
| {!!tools.length && letters.map(renderLetterGroup)} | |||
| {tools.length > 10 && <IndexBar letters={letters} itemRefs={toolRefs} />} | |||
| </div> | |||
| ) | |||
| } | |||
| @@ -56,6 +56,7 @@ | |||
| "negotiator": "^0.6.3", | |||
| "next": "^14.1.1", | |||
| "next-nprogress-bar": "^2.3.8", | |||
| "pinyin-pro": "^3.23.0", | |||
| "qrcode.react": "^3.1.0", | |||
| "qs": "^6.11.1", | |||
| "rc-textarea": "^1.5.2", | |||