diff --git a/apps/builder/app/builder/features/workspace/canvas-tools/outline/block-utils.ts b/apps/builder/app/builder/features/workspace/canvas-tools/outline/block-utils.ts index c20096643ceb..5648a0147eda 100644 --- a/apps/builder/app/builder/features/workspace/canvas-tools/outline/block-utils.ts +++ b/apps/builder/app/builder/features/workspace/canvas-tools/outline/block-utils.ts @@ -68,6 +68,75 @@ const getInsertionIndex = ( return insertBefore ? index : index + 1; }; +export const insertListItemAt = (listItemSelector: InstanceSelector) => { + const instances = $instances.get(); + + const parentSelector = listItemSelector.slice(1); + + const parentInstance = instances.get(parentSelector[0]); + + if (parentInstance === undefined) { + return; + } + + const position = + 1 + + parentInstance.children.findIndex( + (child) => child.type === "id" && child.value === listItemSelector[0] + ); + + if (position === 0) { + return; + } + + const target: DroppableTarget = { + parentSelector, + position, + }; + + const fragment = extractWebstudioFragment( + getWebstudioData(), + listItemSelector[0] + ); + + fragment.instances = structuredClone(fragment.instances); + fragment.instances.splice(1); + fragment.instances[0].children = []; + + updateWebstudioData((data) => { + const { newInstanceIds } = insertWebstudioFragmentCopy({ + data, + fragment, + availableDataSources: findAvailableDataSources( + data.dataSources, + data.instances, + target.parentSelector + ), + }); + const newRootInstanceId = newInstanceIds.get(fragment.instances[0].id); + if (newRootInstanceId === undefined) { + return; + } + const children: Instance["children"] = [ + { type: "id", value: newRootInstanceId }, + ]; + + insertInstanceChildrenMutable(data, children, target); + + const selectedInstanceSelector = [ + newRootInstanceId, + ...target.parentSelector, + ]; + + $textEditingInstanceSelector.set({ + selector: selectedInstanceSelector, + reason: "new", + }); + + selectInstance(selectedInstanceSelector); + }); +}; + export const insertTemplateAt = ( templateSelector: InstanceSelector, anchor: InstanceSelector, diff --git a/apps/builder/app/canvas/features/text-editor/text-editor.tsx b/apps/builder/app/canvas/features/text-editor/text-editor.tsx index 3cfdcc4b916d..52ccc31ad4a8 100644 --- a/apps/builder/app/canvas/features/text-editor/text-editor.tsx +++ b/apps/builder/app/canvas/features/text-editor/text-editor.tsx @@ -92,7 +92,10 @@ import { selectInstance, } from "~/shared/awareness"; import { shallowEqual } from "shallow-equal"; -import { insertTemplateAt } from "~/builder/features/workspace/canvas-tools/outline/block-utils"; +import { + insertListItemAt, + insertTemplateAt, +} from "~/builder/features/workspace/canvas-tools/outline/block-utils"; const BindInstanceToNodePlugin = ({ refs, @@ -1075,8 +1078,23 @@ const RichTextContentPluginInternal = ({ if (event.key === "Backspace" || event.key === "Delete") { const rootNodeContent = $getRoot().getTextContent().trim(); - // Delete current + if (rootNodeContent.length === 0) { + const currentInstance = $instances + .get() + .get(rootInstanceSelector[0]); + + if (currentInstance?.component === "ListItem") { + onNext(editor.getEditorState(), { reason: "left" }); + + updateWebstudioData((data) => { + deleteInstanceMutable(data, rootInstanceSelector); + }); + + event.preventDefault(); + return true; + } + const blockChildSelector = findBlockChildSelector(rootInstanceSelector); @@ -1084,7 +1102,7 @@ const RichTextContentPluginInternal = ({ onNext(editor.getEditorState(), { reason: "left" }); updateWebstudioData((data) => { - deleteInstanceMutable(data, rootInstanceSelector); + deleteInstanceMutable(data, blockChildSelector); }); event.preventDefault(); @@ -1095,6 +1113,18 @@ const RichTextContentPluginInternal = ({ if (menuState === "closed") { if (event.key === "Enter" && !event.shiftKey) { + // Custom logic if we are editing ListItem + const currentInstance = $instances + .get() + .get(rootInstanceSelector[0]); + + if (currentInstance?.component === "ListItem") { + // Instead of creating block component we need to add a new ListItem + insertListItemAt(rootInstanceSelector); + event.preventDefault(); + return true; + } + // Check if it pressed on the last line, last symbol const allowedComponents = ["Paragraph", "Text", "Heading"];