123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- import {
- memo,
- useCallback,
- useRef,
- } from 'react'
- import { useTranslation } from 'react-i18next'
- import { useClickAway } from 'ahooks'
- import type { NodeProps } from 'reactflow'
- import NodeResizer from '../nodes/_base/components/node-resizer'
- import {
- useNodeDataUpdate,
- useNodesInteractions,
- } from '../hooks'
- import { useStore } from '../store'
- import {
- NoteEditor,
- NoteEditorContextProvider,
- NoteEditorToolbar,
- } from './note-editor'
- import { THEME_MAP } from './constants'
- import { useNote } from './hooks'
- import type { NoteNodeType } from './types'
- import cn from '@/utils/classnames'
- const Icon = () => {
- return (
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
- <path fillRule="evenodd" clipRule="evenodd" d="M12 9.75V6H13.5V9.75C13.5 11.8211 11.8211 13.5 9.75 13.5H6V12H9.75C10.9926 12 12 10.9926 12 9.75Z" fill="black" fillOpacity="0.16" />
- </svg>
- )
- }
- const NoteNode = ({
- id,
- data,
- }: NodeProps<NoteNodeType>) => {
- const { t } = useTranslation()
- const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey)
- const ref = useRef<HTMLDivElement | null>(null)
- const theme = data.theme
- const {
- handleThemeChange,
- handleEditorChange,
- handleShowAuthorChange,
- } = useNote(id)
- const {
- handleNodesCopy,
- handleNodesDuplicate,
- handleNodeDelete,
- } = useNodesInteractions()
- const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate()
- const handleDeleteNode = useCallback(() => {
- handleNodeDelete(id)
- }, [id, handleNodeDelete])
- useClickAway(() => {
- handleNodeDataUpdateWithSyncDraft({ id, data: { selected: false } })
- }, ref)
- return (
- <div
- className={cn(
- 'flex flex-col relative rounded-md shadow-xs border hover:shadow-md',
- )}
- style={{
- background: THEME_MAP[theme].bg,
- borderColor: data.selected ? THEME_MAP[theme].border : 'rgba(0, 0, 0, 0.05)',
- width: data.width,
- height: data.height,
- }}
- ref={ref}
- >
- <NoteEditorContextProvider
- key={controlPromptEditorRerenderKey}
- value={data.text}
- >
- <>
- <NodeResizer
- nodeId={id}
- nodeData={data}
- icon={<Icon />}
- minWidth={240}
- minHeight={88}
- />
- <div className='shrink-0 h-2 opacity-50 rounded-t-md' style={{ background: THEME_MAP[theme].title }}></div>
- {
- data.selected && (
- <div className='absolute -top-[41px] left-1/2 -translate-x-1/2'>
- <NoteEditorToolbar
- theme={theme}
- onThemeChange={handleThemeChange}
- onCopy={handleNodesCopy}
- onDuplicate={handleNodesDuplicate}
- onDelete={handleDeleteNode}
- showAuthor={data.showAuthor}
- onShowAuthorChange={handleShowAuthorChange}
- />
- </div>
- )
- }
- <div className='grow px-3 py-2.5 overflow-y-auto'>
- <div className={cn(
- data.selected && 'nodrag nopan nowheel cursor-text',
- )}>
- <NoteEditor
- containerElement={ref.current}
- placeholder={t('workflow.nodes.note.editor.placeholder') || ''}
- onChange={handleEditorChange}
- />
- </div>
- </div>
- {
- data.showAuthor && (
- <div className='p-3 pt-0 text-xs text-black/[0.32]'>
- {data.author}
- </div>
- )
- }
- </>
- </NoteEditorContextProvider>
- </div>
- )
- }
- export default memo(NoteNode)
|