Skip to content

Commit 206dab9

Browse files
committed
refactor: tidy grid block templates node view
1 parent 01b4c96 commit 206dab9

7 files changed

Lines changed: 521 additions & 356 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import {Gear, GripHorizontal} from '@gravity-ui/icons';
2+
import {Button, Icon} from '@gravity-ui/uikit';
3+
import type {ClipboardEvent, FocusEvent, MouseEvent, PointerEvent as ReactPointerEvent} from 'react';
4+
5+
import {i18n} from 'src/i18n/grid-block-templates';
6+
7+
import {blockClass} from '../css';
8+
import type {GridBlock} from '../types';
9+
10+
import {STOP_EVENT_CLASSNAME, cnGridBlockTemplates} from './const';
11+
import type {DropTarget} from './drag';
12+
import {getBlockDragAttrs} from './drag';
13+
import {
14+
enableTextNodeEditing,
15+
insertPlainTextAtSelection,
16+
readTextOnlyEditedHtml,
17+
} from './textEditing';
18+
19+
const b = cnGridBlockTemplates;
20+
const stop = STOP_EVENT_CLASSNAME;
21+
22+
interface GridBlockItemProps {
23+
block: GridBlock;
24+
index: number;
25+
isDragged: boolean;
26+
isEditing: boolean;
27+
dropTarget: DropTarget | null;
28+
onBeginDrag: (blockId: string, event: ReactPointerEvent<HTMLButtonElement>) => void;
29+
onOpenSettings: (blockId: string, anchor: HTMLElement) => void;
30+
onStartEdit: (blockId: string) => void;
31+
onCommitContent: (blockId: string, content: string) => void;
32+
}
33+
34+
export const GridBlockItem: React.FC<GridBlockItemProps> = ({
35+
block,
36+
index,
37+
isDragged,
38+
isEditing,
39+
dropTarget,
40+
onBeginDrag,
41+
onOpenSettings,
42+
onStartEdit,
43+
onCommitContent,
44+
}) => {
45+
const number = index + 1;
46+
const isDropBefore = dropTarget?.id === block.id && dropTarget.placement === 'before';
47+
const isDropAfter = dropTarget?.id === block.id && dropTarget.placement === 'after';
48+
49+
const handleOpenSettings = (event: MouseEvent<HTMLElement>) => {
50+
onOpenSettings(block.id, event.currentTarget);
51+
};
52+
53+
const handleDoubleClick = (event: MouseEvent<HTMLDivElement>) => {
54+
onStartEdit(block.id);
55+
enableTextNodeEditing(event.currentTarget);
56+
};
57+
58+
const handlePaste = (event: ClipboardEvent<HTMLDivElement>) => {
59+
if (!isEditing) return;
60+
61+
event.preventDefault();
62+
insertPlainTextAtSelection(event.clipboardData.getData('text/plain'));
63+
};
64+
65+
const handleBlur = (event: FocusEvent<HTMLDivElement>) => {
66+
const nextFocused = event.relatedTarget;
67+
if (nextFocused instanceof window.Node && event.currentTarget.contains(nextFocused)) {
68+
return;
69+
}
70+
71+
onCommitContent(block.id, readTextOnlyEditedHtml(event.currentTarget));
72+
};
73+
74+
const handlePointerDown = (event: ReactPointerEvent<HTMLButtonElement>) => {
75+
onBeginDrag(block.id, event);
76+
};
77+
78+
return (
79+
<div
80+
className={`${b('item', {
81+
dragged: isDragged,
82+
'drop-before': isDropBefore,
83+
'drop-after': isDropAfter,
84+
})} ${stop} ${blockClass(index)}`}
85+
{...getBlockDragAttrs(block.id)}
86+
>
87+
<button
88+
type="button"
89+
className={`${b('item-drag')} ${stop}`}
90+
onPointerDown={handlePointerDown}
91+
aria-label={i18n('drag_block', {index: String(number)})}
92+
>
93+
<Icon data={GripHorizontal} size={14} />
94+
</button>
95+
<Button
96+
view="flat"
97+
size="s"
98+
className={`${b('item-gear')} ${stop}`}
99+
onClick={handleOpenSettings}
100+
aria-label={i18n('block_css', {index: String(number)})}
101+
>
102+
<Icon data={Gear} size={14} className={stop} />
103+
</Button>
104+
<div
105+
className={`${b('item-content', {editing: isEditing})} ${stop}`}
106+
contentEditable={false}
107+
suppressContentEditableWarning
108+
onDoubleClick={handleDoubleClick}
109+
onPaste={handlePaste}
110+
onBlur={handleBlur}
111+
dangerouslySetInnerHTML={{__html: block.content}}
112+
/>
113+
</div>
114+
);
115+
};

0 commit comments

Comments
 (0)