Files
AFFiNE-Mirror/libs/components/editor-blocks/src/blocks/todo/TodoView.tsx
T
2022-08-26 17:26:36 +08:00

182 lines
5.6 KiB
TypeScript

import { TextProps } from '@toeverything/components/common';
import {
AsyncBlock,
BlockPendantProvider,
CreateView,
useOnSelect,
} from '@toeverything/components/editor-core';
import { styled } from '@toeverything/components/ui';
import {
ContentColumnValue,
Protocol,
} from '@toeverything/datasource/db-service';
import { useRef, useState } from 'react';
import { BlockContainer } from '../../components/BlockContainer';
import {
TextManage,
type ExtendedTextUtils,
} from '../../components/text-manage';
import { tabBlock } from '../../utils/indent';
import { CheckBox } from './CheckBox';
import type { TodoAsyncBlock, TodoProperties } from './types';
export const defaultTodoProps: TodoProperties = {
text: { value: [{ text: '' }] },
};
const reset_todo_state = async (block: TodoAsyncBlock) => {
await block.setProperties({
checked: { value: false },
collapsed: { value: false },
});
};
const todoIsEmpty = (contentValue: ContentColumnValue): boolean => {
const todoValue = contentValue.value;
return (
todoValue.length === 0 ||
(todoValue.length === 1 && !todoValue[0]['text'])
);
};
export const TodoView = ({ block, editor }: CreateView) => {
const properties = { ...defaultTodoProps, ...block.getProperties() };
const text_ref = useRef<ExtendedTextUtils>(null);
const [isSelect, setIsSelect] = useState<boolean>(false);
useOnSelect(block.id, (isSelect: boolean) => {
setIsSelect(isSelect);
});
const turn_into_text_block = async () => {
// Convert to text block
await block.setType('text');
await reset_todo_state(block);
if (!text_ref.current) {
throw new Error(
'Failed to set cursor position! text_ref is not exist!'
);
}
const currentSelection = text_ref.current.getCurrentSelection();
if (!currentSelection) {
throw new Error(
'Failed to get cursor selection! currentSelection is not exist!'
);
}
// Update cursor position
editor.selectionManager.setNodeActiveSelection(block.id, {
type: 'Range',
info: currentSelection,
});
};
const on_text_enter: TextProps['handleEnter'] = async props => {
const { splitContents, isShiftKey } = props;
if (isShiftKey) {
return false;
}
const { contentBeforeSelection, contentAfterSelection } = splitContents;
const before = [...contentBeforeSelection.content];
const after = [...contentAfterSelection.content];
if (todoIsEmpty({ value: before } as ContentColumnValue)) {
await turn_into_text_block();
return true;
}
// Move children to new block
const children = (await block.children()).filter(
Boolean
) as AsyncBlock[];
const next_node = await editor.createBlock(Protocol.Block.Type.todo);
if (!next_node) {
throw new Error('Failed to create todo block');
}
await block.removeChildren();
await next_node.append(...children);
await next_node.setProperties({
text: { value: after } as ContentColumnValue,
});
await block.setProperties({
text: { value: before } as ContentColumnValue,
collapsed: { value: false },
});
await block.after(next_node);
editor.selectionManager.activeNodeByNodeId(next_node.id);
return true;
};
const on_tab: TextProps['handleTab'] = async ({ isShiftKey }) => {
await tabBlock(editor, block, isShiftKey);
return true;
};
const on_backspace: TextProps['handleBackSpace'] = async ({
isCollAndStart,
}) => {
if (!isCollAndStart) {
return false;
}
await turn_into_text_block();
return true;
};
const on_checked_change = async (checked: boolean) => {
await block.setProperties({
checked: { value: checked },
});
};
return (
<BlockContainer editor={editor} block={block} selected={isSelect}>
<BlockPendantProvider editor={editor} block={block}>
<TodoBlock>
<div className={'checkBoxContainer'}>
<CheckBox
checked={properties.checked?.value}
onChange={on_checked_change}
/>
</div>
<div className={'textContainer'}>
<TextManage
className={
properties.checked?.value ? 'checked' : ''
}
ref={text_ref}
editor={editor}
block={block}
supportMarkdown
placeholder="To-do"
handleEnter={on_text_enter}
handleBackSpace={on_backspace}
handleTab={on_tab}
/>
</div>
</TodoBlock>
</BlockPendantProvider>
</BlockContainer>
);
};
const TodoBlock = styled('div')({
display: 'flex',
'.checkBoxContainer': {
marginRight: '4px',
padding: '0 4px',
height: '22px',
},
'.textContainer': {
flex: 1,
maxWidth: '100%',
overflowX: 'hidden',
overflowY: 'hidden',
},
'.checked': {
color: '#B9CAD5',
},
});