Files
AFFiNE-Mirror/libs/components/editor-blocks/src/blocks/bullet/BulletView.tsx
T
2022-08-24 02:08:00 +08:00

214 lines
7.1 KiB
TypeScript

import type { TextProps } from '@toeverything/components/common';
import {
ContentColumnValue,
Protocol,
services,
} from '@toeverything/datasource/db-service';
import { type CreateView } from '@toeverything/framework/virgo';
import { useEffect, useRef, useState } from 'react';
import {
BlockPendantProvider,
RenderBlockChildren,
supportChildren,
useOnSelect,
} from '@toeverything/components/editor-core';
import { styled } from '@toeverything/components/ui';
import { BlockContainer } from '../../components/BlockContainer';
import { List } from '../../components/style-container';
import {
TextManage,
type ExtendedTextUtils,
} from '../../components/text-manage';
import { tabBlock } from '../../utils/indent';
import { BulletIcon, getChildrenType, NumberType } from './data';
import { BulletBlock, BulletProperties } from './types';
export const defaultBulletProps: BulletProperties = {
text: { value: [{ text: '' }] },
numberType: NumberType.type1,
};
const reset_todo_state = async (block: BulletBlock) => {
await block.setProperties({});
};
const todoIsEmpty = (contentValue: ContentColumnValue): boolean => {
const todoValue = contentValue.value;
return (
todoValue.length === 0 ||
(todoValue.length === 1 && !todoValue[0]['text'])
);
};
const BulletLeft = styled('div')(({ theme }) => ({
height: '22px',
color: theme.affine.typography.body1.color,
}));
export const BulletView = ({ block, editor }: CreateView) => {
// block.remove();
const properties = { ...defaultBulletProps, ...block.getProperties() };
const textRef = useRef<ExtendedTextUtils>(null);
// const [type, set_type] = useState('type-1');
const [isSelect, setIsSelect] = useState<boolean>();
useOnSelect(block.id, (is_select: boolean) => {
setIsSelect(is_select);
});
const turnIntoTextBlock = async () => {
// Convert to text block
await block.setType('text');
await reset_todo_state(block);
if (!textRef.current) {
throw new Error(
'Failed to set cursor position! text_ref is not exist!'
);
}
const currentSelection = textRef.current.getStartSelection();
if (!currentSelection) {
throw new Error(
'Failed to get cursor selection! currentSelection is not exist!'
);
}
editor.selectionManager.setNodeActiveSelection(block.id, {
type: 'Range',
info: currentSelection,
});
// Update cursor position
// editor.selectionManager.setActivatedNodeId(block.id);
// editor.selectionManager.setNodeActiveSelection(block.id, {
// type: 'Range',
// info: currentSelection
// });
};
const i = 0;
const listChange = async () => {
try {
const parentBlock = await block.parent();
if (!parentBlock) {
return;
}
let parentNumberType = parentBlock.getProperty('numberType');
if (
!parentNumberType &&
parentBlock.type === Protocol.Block.Type.bullet
) {
parentNumberType = NumberType.type1;
parentBlock.setProperty('numberType', parentNumberType);
}
block.setProperty('numberType', getChildrenType(parentNumberType));
} catch (e) {
console.warn(
'Failed to update bullet list numbers!',
'This issue may be related to the undo redo. block:',
block
);
console.warn(e);
}
};
useEffect(() => {
listChange();
let obj: any;
const parentChange = async () => {
const parentBlock = await block.parent();
obj = await services.api.editorBlock.observe(
{
workspace: editor.workspace,
id: parentBlock.id,
},
listChange
);
};
parentChange();
return () => {
obj?.();
};
// console.log('_parent_block: ', _parent_block);
}, []);
const onTextEnter: 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 turnIntoTextBlock();
return true;
}
// Move children to new block
const children = await block.children();
await block.removeChildren();
const next_node = await editor.createBlock(Protocol.Block.Type.bullet);
if (!next_node) {
throw new Error('Failed to create todo block');
}
await next_node.append(...children);
await next_node.setProperties({
text: { value: after } as ContentColumnValue,
});
await block.setProperties({
text: { value: before } as ContentColumnValue,
});
await block.after(next_node);
editor.selectionManager.activeNodeByNodeId(next_node.id);
return true;
};
const onTab: TextProps['handleTab'] = async ({ isShiftKey }) => {
if (!isShiftKey) {
const preSiblingBlock = await block.previousSibling();
if (preSiblingBlock && supportChildren(preSiblingBlock)) {
const copyBlock = block;
block.remove();
block.removeChildren();
const blockChildren = await copyBlock.children();
preSiblingBlock.append(copyBlock, ...blockChildren);
}
return true;
} else {
return tabBlock(editor, block, isShiftKey);
}
};
const onBackspace: TextProps['handleBackSpace'] = async ({
isCollAndStart,
}) => {
if (!isCollAndStart) {
return false;
}
await turnIntoTextBlock();
return true;
};
return (
<BlockContainer editor={editor} block={block} selected={isSelect}>
<BlockPendantProvider block={block}>
<List>
<BulletLeft>
<BulletIcon numberType={properties.numberType} />
</BulletLeft>
<div className={'textContainer'}>
<TextManage
ref={textRef}
editor={editor}
block={block}
supportMarkdown
placeholder="/Bullet list"
handleEnter={onTextEnter}
handleBackSpace={onBackspace}
handleTab={onTab}
/>
</div>
</List>
</BlockPendantProvider>
<RenderBlockChildren block={block} />
</BlockContainer>
);
};