Skip to content

Commit 868e5e1

Browse files
committed
feat: improve grid block templates ui
1 parent 6bb30aa commit 868e5e1

11 files changed

Lines changed: 395 additions & 288 deletions

File tree

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,3 @@
11
import type {GridBlockTemplate} from '@gravity-ui/markdown-editor/extensions/additional/GridBlockTemplates/templates/index.js';
22

3-
export const gridBlockTemplates: GridBlockTemplate[] = [
4-
{
5-
id: 'feature-grid',
6-
title: 'Feature grid',
7-
type: 'container',
8-
content: `<div class="grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;padding:16px;background:#f8fafc;border-radius:12px">
9-
<div style="padding:18px;background:#fff;border:1px solid #e5e7eb;border-radius:8px"><strong>Fast setup</strong><p>Start from a reusable layout.</p></div>
10-
<div style="padding:18px;background:#fff;border:1px solid #e5e7eb;border-radius:8px"><strong>Editable blocks</strong><p>Change content inline.</p></div>
11-
<div style="padding:18px;background:#fff;border:1px solid #e5e7eb;border-radius:8px"><strong>Static output</strong><p>Serialize to HTML.</p></div>
12-
</div>`,
13-
containerCss:
14-
'display:grid;grid-template-columns:repeat(3,1fr);gap:16px;padding:16px;background:#f8fafc;border-radius:12px',
15-
blocks: [
16-
{
17-
css: 'padding:18px;background:#fff;border:1px solid #e5e7eb;border-radius:8px',
18-
content: '<strong>Fast setup</strong><p>Start from a reusable layout.</p>',
19-
},
20-
{
21-
css: 'padding:18px;background:#fff;border:1px solid #e5e7eb;border-radius:8px',
22-
content: '<strong>Editable blocks</strong><p>Change content inline.</p>',
23-
},
24-
{
25-
css: 'padding:18px;background:#fff;border:1px solid #e5e7eb;border-radius:8px',
26-
content: '<strong>Static output</strong><p>Serialize to HTML.</p>',
27-
},
28-
],
29-
},
30-
{
31-
id: 'header-two-columns',
32-
title: 'Header and two columns',
33-
type: 'container',
34-
content: `<div class="grid" style="display:grid;grid-template-columns:1fr 1fr;gap:14px;padding:14px">
35-
<div style="grid-column:1 / -1;padding:24px;background:#2563eb;color:#fff;border-radius:10px"><h2>Section title</h2></div>
36-
<div style="padding:18px;background:#eff6ff;border-radius:8px">Left column</div>
37-
<div style="padding:18px;background:#f0fdf4;border-radius:8px">Right column</div>
38-
</div>`,
39-
containerCss: 'display:grid;grid-template-columns:1fr 1fr;gap:14px;padding:14px',
40-
blocks: [
41-
{
42-
css: 'grid-column:1 / -1;padding:24px;background:#2563eb;color:#fff;border-radius:10px',
43-
content: '<h2>Section title</h2>',
44-
},
45-
{
46-
css: 'padding:18px;background:#eff6ff;border-radius:8px',
47-
content: 'Left column',
48-
},
49-
{
50-
css: 'padding:18px;background:#f0fdf4;border-radius:8px',
51-
content: 'Right column',
52-
},
53-
],
54-
},
55-
];
3+
export const gridBlockTemplates: GridBlockTemplate[] = [];

packages/editor/src/extensions/additional/GridBlockTemplates/GridBlockTemplatesNodeView/BlockInsertPopup.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {useMemo, useState} from 'react';
22

3-
import {Code, Plus} from '@gravity-ui/icons';
3+
import {Code} from '@gravity-ui/icons';
44
import {Button, Icon, Menu, Popup, TextInput} from '@gravity-ui/uikit';
55

66
import {TextAreaFixed as TextArea} from 'src/forms/TextInput';
@@ -55,7 +55,7 @@ export const BlockInsertPopup: React.FC<BlockInsertPopupProps> = ({
5555
};
5656

5757
return (
58-
<Popup anchorElement={anchor} open={open} onOpenChange={close} placement="bottom-end">
58+
<Popup anchorElement={anchor} open={open} onOpenChange={close} placement="bottom-start">
5959
<div className={b('templates', [stop])}>
6060
{showCustomHtmlEditor ? (
6161
<div className={b('templates-editor')}>
@@ -103,11 +103,14 @@ export const BlockInsertPopup: React.FC<BlockInsertPopupProps> = ({
103103
>
104104
{i18n('custom_html')}
105105
</Menu.Item>
106+
<div
107+
role="separator"
108+
className={b('templates-separator', [stop])}
109+
/>
106110
{filtered.map((template) => (
107111
<Menu.Item
108112
key={template.id}
109113
className={stop}
110-
iconStart={<Icon data={Plus} />}
111114
onClick={() => {
112115
onApplyTemplate(template);
113116
close();

packages/editor/src/extensions/additional/GridBlockTemplates/GridBlockTemplatesNodeView/GridBlockItem.tsx

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import type {
2-
ClipboardEvent,
3-
FocusEvent,
4-
MouseEvent,
5-
PointerEvent as ReactPointerEvent,
6-
} from 'react';
1+
import type {MouseEvent, PointerEvent as ReactPointerEvent} from 'react';
72

83
import {Gear, GripHorizontal} from '@gravity-ui/icons';
94
import {Button, Icon} from '@gravity-ui/uikit';
@@ -16,11 +11,7 @@ import type {GridBlock} from '../types';
1611
import {STOP_EVENT_CLASSNAME, cnGridBlockTemplates} from './const';
1712
import type {DropTarget} from './drag';
1813
import {getBlockDragAttrs} from './drag';
19-
import {
20-
enableTextNodeEditing,
21-
insertPlainTextAtSelection,
22-
readTextOnlyEditedHtml,
23-
} from './textEditing';
14+
import {openInlineTextEditor} from './textEditing';
2415

2516
const b = cnGridBlockTemplates;
2617
const stop = STOP_EVENT_CLASSNAME;
@@ -29,23 +20,19 @@ interface GridBlockItemProps {
2920
block: GridBlock;
3021
index: number;
3122
isDragged: boolean;
32-
isEditing: boolean;
3323
dropTarget: DropTarget | null;
3424
onBeginDrag: (blockId: string, event: ReactPointerEvent<HTMLButtonElement>) => void;
3525
onOpenSettings: (blockId: string, anchor: HTMLElement) => void;
36-
onStartEdit: (blockId: string) => void;
3726
onCommitContent: (blockId: string, content: string) => void;
3827
}
3928

4029
export const GridBlockItem: React.FC<GridBlockItemProps> = ({
4130
block,
4231
index,
4332
isDragged,
44-
isEditing,
4533
dropTarget,
4634
onBeginDrag,
4735
onOpenSettings,
48-
onStartEdit,
4936
onCommitContent,
5037
}) => {
5138
const number = index + 1;
@@ -57,24 +44,11 @@ export const GridBlockItem: React.FC<GridBlockItemProps> = ({
5744
};
5845

5946
const handleDoubleClick = (event: MouseEvent<HTMLDivElement>) => {
60-
onStartEdit(block.id);
61-
enableTextNodeEditing(event.currentTarget);
62-
};
63-
64-
const handlePaste = (event: ClipboardEvent<HTMLDivElement>) => {
65-
if (!isEditing) return;
66-
67-
event.preventDefault();
68-
insertPlainTextAtSelection(event.clipboardData.getData('text/plain'));
69-
};
70-
71-
const handleBlur = (event: FocusEvent<HTMLDivElement>) => {
72-
const nextFocused = event.relatedTarget;
73-
if (nextFocused instanceof window.Node && event.currentTarget.contains(nextFocused)) {
74-
return;
75-
}
76-
77-
onCommitContent(block.id, readTextOnlyEditedHtml(event.currentTarget));
47+
openInlineTextEditor({
48+
root: event.currentTarget,
49+
event,
50+
onCommit: (html) => onCommitContent(block.id, html),
51+
});
7852
};
7953

8054
const handlePointerDown = (event: ReactPointerEvent<HTMLButtonElement>) => {
@@ -108,12 +82,10 @@ export const GridBlockItem: React.FC<GridBlockItemProps> = ({
10882
<Icon data={Gear} size={14} className={stop} />
10983
</Button>
11084
<div
111-
className={`${b('item-content', {editing: isEditing})} ${stop}`}
85+
className={`${b('item-content')} ${stop}`}
11286
contentEditable={false}
11387
suppressContentEditableWarning
11488
onDoubleClick={handleDoubleClick}
115-
onPaste={handlePaste}
116-
onBlur={handleBlur}
11789
dangerouslySetInnerHTML={{__html: block.content}}
11890
/>
11991
</div>

packages/editor/src/extensions/additional/GridBlockTemplates/GridBlockTemplatesNodeView/GridBlockTemplates.scss

Lines changed: 20 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@
1111
position: absolute;
1212
top: 4px;
1313
right: 4px;
14+
left: 4px;
1415

16+
display: flex;
17+
justify-content: space-between;
18+
gap: 2px;
19+
}
20+
21+
&__toolbar-group {
1522
display: flex;
1623
gap: 2px;
1724
}
@@ -105,63 +112,33 @@
105112

106113
outline: none;
107114

108-
&_editing {
109-
outline: 2px solid var(--g-color-line-focus);
110-
outline-offset: 2px;
111-
}
112-
113115
&:empty::before {
114116
content: 'Text...';
115117

116118
color: var(--g-color-text-hint);
117119
}
118120
}
119121

120-
&__editable-text {
121-
outline: 1px solid var(--g-color-line-focus);
122-
outline-offset: 1px;
123-
background: var(--g-color-base-selection);
124-
}
125-
126-
&__add {
127-
display: flex;
128-
justify-content: center;
129-
align-items: center;
130-
122+
&__inline-text-editor {
131123
width: 100%;
132-
min-height: 56px;
133-
margin-top: 8px;
134-
padding: 12px;
135-
136-
cursor: pointer;
137-
138-
color: var(--g-color-text-secondary);
139-
border: 1px dashed var(--g-color-line-generic);
140-
border-radius: var(--g-border-radius-m);
141-
background: transparent;
142-
143-
&:hover {
144-
color: var(--g-color-text-primary);
145-
border-color: var(--g-color-line-generic-hover);
146-
background: var(--g-color-base-generic);
147-
}
148-
}
124+
min-height: 32px;
125+
padding: 4px 6px;
149126

150-
&__css-editor {
151-
width: 320px;
152-
padding: 8px;
127+
font: inherit;
128+
resize: vertical;
153129

154-
.g-text-area__control {
155-
font-family: var(--g-font-family-monospace);
156-
}
130+
color: inherit;
131+
border: 1px solid var(--g-color-line-focus);
132+
border-radius: var(--g-border-radius-xs);
133+
background: var(--g-color-base-background);
157134
}
158135

159-
&__block-settings-editor {
160-
display: flex;
161-
flex-direction: column;
136+
&__settings-editor {
137+
display: grid;
138+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
162139
gap: 10px;
163140

164-
width: 380px;
141+
width: 720px;
165142
max-width: calc(100vw - 32px);
166143
padding: 8px;
167144

0 commit comments

Comments
 (0)