feat: add simple columns block in editor (#1038)

* feat: add simple columns block in editor

* feat: add divider between column block

* fix: add fallback logic in calculate selection function

* feat: add column width resizer

* feat: integrate columns block in editor on desktop
This commit is contained in:
Lucas 2025-02-21 12:05:56 +08:00 • committed by GitHub
commit c1e9d15572
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 562 additions and 29 deletions

View file

@ -136,6 +136,10 @@ class _DesktopEditorState extends State<DesktopEditor> {
Map<String, BlockComponentBuilder> _buildBlockComponentBuilders() {
final map = {
...standardBlockComponentBuilderMap,
// columns block
ColumnBlockKeys.type: ColumnBlockComponentBuilder(),
ColumnsBlockKeys.type: ColumnsBlockComponentBuilder(),
};
// customize the image block component to show a menu
map[ImageBlockKeys.type] = ImageBlockComponentBuilder(
@ -165,7 +169,13 @@ class _DesktopEditorState extends State<DesktopEditor> {
// customize the padding
map.forEach((key, value) {
value.configuration = value.configuration.copyWith(
padding: (_) => const EdgeInsets.symmetric(vertical: 8.0),
padding: (node) {
if (node.type == ColumnsBlockKeys.type ||
node.type == ColumnBlockKeys.type) {
return EdgeInsets.zero;
}
return const EdgeInsets.symmetric(vertical: 8.0);
},
blockSelectionAreaMargin: (_) => const EdgeInsets.symmetric(
vertical: 1.0,
),

View file

@ -287,29 +287,49 @@ class _DragToReorderActionState extends State<DragToReorderAction> {
final (verticalPosition, horizontalPosition, _) = position;
Path newPath = targetNode.path;
// Determine the new path based on drop position
// For VerticalPosition.top, we keep the target node's path
if (verticalPosition == VerticalPosition.bottom) {
newPath = horizontalPosition == HorizontalPosition.left
? newPath.next // Insert after target node
: newPath.child(0); // Insert as first child of target node
}
// Check if the drop should be ignored
if (_shouldIgnoreDrop(node, newPath)) {
debugPrint(
'Drop ignored: node($node, ${node.path}), path($acceptedPath)',
);
return;
}
final realNode = widget.blockComponentContext.node;
debugPrint('Moving node($realNode, ${realNode.path}) to path($newPath)');
// Perform the node move operation
final transaction = editorState.transaction;
transaction.moveNode(newPath, realNode);
await editorState.apply(transaction);
// if the horizontal position is right, we need to create a column block
if (horizontalPosition == HorizontalPosition.right) {
final node = columnsNode(
children: [
columnNode(
children: [targetNode.deepCopy()],
),
columnNode(
children: [realNode.deepCopy()],
),
],
);
final transaction = editorState.transaction;
transaction.insertNode(newPath, node);
transaction.deleteNode(targetNode);
transaction.deleteNode(realNode);
await editorState.apply(transaction);
} else {
// Determine the new path based on drop position
// For VerticalPosition.top, we keep the target node's path
if (verticalPosition == VerticalPosition.bottom) {
newPath = horizontalPosition == HorizontalPosition.left
? newPath.next // Insert after target node
: newPath.child(0); // Insert as first child of target node
}
// Check if the drop should be ignored
if (_shouldIgnoreDrop(node, newPath)) {
debugPrint(
'Drop ignored: node($node, ${node.path}), path($acceptedPath)',
);
return;
}
// Perform the node move operation
final transaction = editorState.transaction;
transaction.moveNode(newPath, realNode);
await editorState.apply(transaction);
}
}
Widget _buildFeedback() {
@ -387,7 +407,7 @@ Widget _buildDropArea(
color: Colors.red,
);
if (horizontalPosition == HorizontalPosition.right) {
if (horizontalPosition == HorizontalPosition.center) {
const breakWidth = 22.0;
const padding = 8.0;
child = Row(
@ -406,6 +426,16 @@ Widget _buildDropArea(
),
],
);
} else if (horizontalPosition == HorizontalPosition.right) {
return Positioned(
top: globalBlockRect.top,
height: globalBlockRect.height,
left: globalBlockRect.right - 2,
child: Container(
width: 2,
color: Colors.red,
),
);
}
return Positioned(
@ -452,9 +482,10 @@ Widget _buildDropArea(
// Horizontal position
if (dragOffset.dx < globalBlockRect.left + 44) {
horizontalPosition = HorizontalPosition.left;
} else {
// ignore the middle here, it's not used in this example
} else if (dragOffset.dx > globalBlockRect.right / 3.0 * 2.0) {
horizontalPosition = HorizontalPosition.right;
} else {
horizontalPosition = HorizontalPosition.center;
}
// Vertical position