Skip to content

Commit

Permalink
Backspace to delete elements (#361)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kitenite authored Sep 16, 2024
1 parent b9ddfe8 commit f566f7e
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 5 deletions.
1 change: 1 addition & 0 deletions app/common/hotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class Hotkey {
static readonly INTERACT = new Hotkey('i', 'Interact');
static readonly INSERT_DIV = new Hotkey('r', 'Insert Div');
static readonly INSERT_TEXT = new Hotkey('t', 'Insert Text');
static readonly DELETE = new Hotkey('backspace', 'Delete Div');

// private to disallow creating other instances of this type
private constructor(
Expand Down
4 changes: 4 additions & 0 deletions app/electron/preload/webview/api.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { contextBridge } from 'electron';
import { processDom } from './dom';
import { getElementAtLoc, getElementWithSelector } from './elements';
import { isElementInserted } from './elements/helpers';
import { getInsertedElements, getInsertLocation } from './elements/insert';
import { getMovedElements } from './elements/move';
import { drag, endDrag, startDrag } from './elements/move/drag';
import { getRemoveActionFromSelector } from './elements/remove';
import {
editText,
getTextEditedElements,
Expand All @@ -16,10 +18,12 @@ export function setApi() {
getElementAtLoc: getElementAtLoc,
getElementWithSelector: getElementWithSelector,
processDom: processDom,
isElementInserted: isElementInserted,

// Insert
getInsertLocation: getInsertLocation,
getInsertedElements: getInsertedElements,
getRemoveActionFromSelector: getRemoveActionFromSelector,

// Drag
startDrag: startDrag,
Expand Down
25 changes: 25 additions & 0 deletions app/electron/preload/webview/elements/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { uuid } from '../bundles';
import { getStyles } from './style';
import { ActionElementLocation } from '/common/actions';
import { EditorAttributes } from '/common/constants';
import { getUniqueSelector } from '/common/helpers';
import { InsertPos } from '/common/models';
import { DomElement, ParentDomElement } from '/common/models/element';

export const getDeepElement = (x: number, y: number): Element | undefined => {
Expand Down Expand Up @@ -77,3 +79,26 @@ export function restoreElementStyle(el: HTMLElement) {
export function saveTimestamp(el: HTMLElement) {
el.setAttribute(EditorAttributes.DATA_ONLOOK_TIMESTAMP, Date.now().toString());
}

export function getElementLocation(targetEl: HTMLElement): ActionElementLocation | undefined {
const parent = targetEl.parentElement;
if (!parent) {
return;
}

const parentSelector = getUniqueSelector(parent as HTMLElement);
const location: ActionElementLocation = {
position: InsertPos.INDEX,
targetSelector: parentSelector,
index: Array.from(targetEl.parentElement?.children || []).indexOf(targetEl),
};
return location;
}

export const isElementInserted = (selector: string): boolean => {
const targetEl = document.querySelector(selector);
if (!targetEl) {
return false;
}
return targetEl.hasAttribute(EditorAttributes.DATA_ONLOOK_INSERTED);
};
20 changes: 20 additions & 0 deletions app/electron/preload/webview/elements/insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ export function insertElement(
case InsertPos.AFTER:
targetEl.after(newEl);
break;
case InsertPos.INDEX:
if (location.index === undefined || location.index < 0) {
console.error(`Invalid index: ${location.index}`);
return;
}

if (location.index >= targetEl.children.length) {
targetEl.appendChild(newEl);
} else {
targetEl.insertBefore(newEl, targetEl.children.item(location.index));
}
break;
default:
console.error(`Invalid position: ${location.position}`);
return;
Expand Down Expand Up @@ -103,6 +115,14 @@ export function removeElement(location: ActionElementLocation): DomElement | nul
case InsertPos.AFTER:
elementToRemove = targetEl.nextElementSibling as HTMLElement | null;
break;
case InsertPos.INDEX:
if (location.index !== undefined) {
elementToRemove = targetEl.children.item(location.index) as HTMLElement | null;
} else {
console.error(`Invalid index: ${location.index}`);
return null;
}
break;
default:
console.error(`Invalid position: ${location.position}`);
return null;
Expand Down
43 changes: 43 additions & 0 deletions app/electron/preload/webview/elements/remove.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { getElementLocation } from './helpers';
import { ActionElement, RemoveElementAction } from '/common/actions';

export function getRemoveActionFromSelector(
selector: string,
webviewId: string,
): RemoveElementAction | undefined {
const el = document.querySelector(selector) as HTMLElement | null;
if (!el) {
return;
}

const location = getElementLocation(el);
if (!location) {
return;
}

const actionEl = getActionElement(el);

return {
type: 'remove-element',
targets: [{ webviewId }],
location: location,
element: actionEl,
styles: {},
};
}

function getActionElement(el: HTMLElement): ActionElement {
const children = Array.from(el.children).map((child) => getActionElement(child as HTMLElement));
const textContent = el.textContent || '';
const attributes: Record<string, string> = {};
for (const attr of el.attributes) {
attributes[attr.name] = attr.value;
}

return {
tagName: el.tagName.toLowerCase(),
attributes,
children,
textContent,
};
}
34 changes: 34 additions & 0 deletions app/src/lib/editor/engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { ProjectInfoManager } from './projectinfo';
import { StyleManager } from './style';
import { TextEditingManager } from './text';
import { WebviewManager } from './webview';
import { RemoveElementAction } from '/common/actions';
import { escapeSelector } from '/common/helpers';
import { WebViewElement } from '/common/models/element';

export class EditorEngine {
private editorMode: EditorMode = EditorMode.DESIGN;
Expand Down Expand Up @@ -158,4 +160,36 @@ export class EditorEngine {
}
this.text.start(domEl, webview);
}

async deleteSelectedElement() {
const selected = this.elements.selected;
if (selected.length === 0) {
return;
}
const selectedEl: WebViewElement = selected[0];
const webviewId = selectedEl.webviewId;
const webview = this.webviews.getWebview(webviewId);
if (!webview) {
return;
}

const isElementInserted = await webview.executeJavaScript(
`window.api?.isElementInserted('${escapeSelector(selectedEl.selector)}')`,
);

if (isElementInserted) {
const removeAction = (await webview.executeJavaScript(
`window.api?.getRemoveActionFromSelector('${escapeSelector(selectedEl.selector)}', '${webviewId}')`,
)) as RemoveElementAction | undefined;
if (!removeAction) {
return;
}
this.action.run(removeAction);
} else {
this.style.updateElementStyle('display', {
updated: 'none',
original: selectedEl.styles.display,
});
}
}
}
2 changes: 1 addition & 1 deletion app/src/lib/editor/engine/overlay/textEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export class EditTextInput {
this.setValue(content);
this.onChange = onChange || null;
this.onStop = onStop || null;
this.editorView.focus();
}

updateSize({ width, height, top, left }: RectDimensions) {
Expand Down Expand Up @@ -144,6 +143,7 @@ export class EditTextInput {
this.isDisabled = false;
this.editorView.setProps({ editable: () => true });
this.element.style.pointerEvents = 'auto';
this.editorView.focus();
}
}
}
9 changes: 5 additions & 4 deletions app/src/routes/project/Canvas/HotkeysArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ const HotkeysArea = ({ children, scale, setScale }: HotkeysAreaProps) => {

// Modes
useHotkeys(Hotkey.SELECT.command, () => (editorEngine.mode = EditorMode.DESIGN));
useHotkeys(Hotkey.ESCAPE.command, () => (editorEngine.mode = EditorMode.DESIGN));
useHotkeys(Hotkey.ESCAPE.command, () => {
editorEngine.mode = EditorMode.DESIGN;
!editorEngine.text.isEditing && editorEngine.clear();
});
useHotkeys(Hotkey.PAN.command, () => (editorEngine.mode = EditorMode.PAN));
useHotkeys(Hotkey.INTERACT.command, () => (editorEngine.mode = EditorMode.INTERACT));
useHotkeys(Hotkey.INSERT_DIV.command, () => (editorEngine.mode = EditorMode.INSERT_DIV));
Expand All @@ -34,9 +37,7 @@ const HotkeysArea = ({ children, scale, setScale }: HotkeysAreaProps) => {
useHotkeys(Hotkey.UNDO.command, () => editorEngine.action.undo());
useHotkeys(Hotkey.REDO.command, () => editorEngine.action.redo());
useHotkeys('enter', () => editorEngine.textEditSelectedElement());
useHotkeys('esc', () => {
!editorEngine.text.isEditing && editorEngine.clear();
});
useHotkeys(Hotkey.DELETE.command, () => editorEngine.deleteSelectedElement());

return <>{children}</>;
};
Expand Down

0 comments on commit f566f7e

Please sign in to comment.