Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ability to select several elements and delete them #7

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions src/renderer/ApplicationFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const ApplicationTabs: FC = () => {
onClose={() => closeProject(project)}
selected={history.location.pathname.includes(project.name)}
icon={<IconArtboard size={23} strokeWidth={1.75} />}
key={project.name}
>
{project.name}
</Tab>
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/Pages/Canvas/InstrumentFrameElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ export interface InstrumentFrameElementProps {
instrumentFrame: InstrumentFrame,
zoom: number,
onUpdate: (el: InstrumentFrame) => void,
selected: boolean
}

export const InstrumentFrameElement: FC<InstrumentFrameElementProps> = ({ instrumentFrame, zoom, onUpdate }) => {
export const InstrumentFrameElement: FC<InstrumentFrameElementProps> = ({ instrumentFrame, zoom, onUpdate, selected }) => {
const { project, liveReloadDispatcher, inInteractionMode, setInInteractionMode } = useWorkspace();

const [loadedInstrument] = useState(() => ProjectInstrumentsHandler.loadInstrumentByName(project, instrumentFrame.instrumentName));
Expand Down Expand Up @@ -138,6 +139,7 @@ export const InstrumentFrameElement: FC<InstrumentFrameElementProps> = ({ instru
title={loadedInstrument.config.name}
canvasZoom={zoom}
onUpdate={onUpdate}
selected={selected}
>
<iframe
title="Instrument Frame"
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/Pages/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const RecentProjects: FC = () => {

<div className="w-full flex flex-col gap-y-3">
{projects.map(({ name, location }) => (
<RecentProjectEntry name={name} location={location} />
<RecentProjectEntry name={name} location={location} key={name} />
))}
</div>
</div>
Expand Down
30 changes: 19 additions & 11 deletions src/renderer/Pages/PanelCanvas.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, { useRef, MouseEvent, useState, useEffect, useCallback, WheelEvent, PropsWithChildren } from 'react';
import { ReactZoomPanPinchRef, TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
import { IconTrash, IconArrowsMaximize } from '@tabler/icons';
import { useThrottle } from 'react-use';
import useInterval from '../../utils/useInterval';
import { useWorkspace } from './ProjectHome/WorkspaceContext';
import { PossibleCanvasElements } from '../../shared/types/project/canvas/CanvasSaveFile';
import { GRID_LINE_SIZE, GRID_SVG_SIZE } from './Canvas/Grid';

Expand Down Expand Up @@ -73,7 +71,11 @@ export const PanelCanvas = ({ render }: PanelCanvasProps) => {
}, [currentDoubleClickMode, doEmulateDoubleClick]);

return (
<section className="w-full h-full bg-gray-900 overflow-hidden" ref={transformContainerRef} onWheel={handleWheel}>
<section
className="w-full h-full bg-gray-900 overflow-hidden"
ref={transformContainerRef}
onWheel={handleWheel}
>
<TransformWrapper
ref={transformWrapperRef}
limitToBounds
Expand Down Expand Up @@ -117,6 +119,7 @@ export interface PanelCanvasElementProps<T extends PossibleCanvasElements> {
title?: string;
canvasZoom: number;
onUpdate: (el: T) => void;
selected: boolean;
}

export const PanelCanvasElement = <T extends PossibleCanvasElements>({
Expand All @@ -125,6 +128,7 @@ export const PanelCanvasElement = <T extends PossibleCanvasElements>({
canvasZoom,
onUpdate,
children,
selected,
}: PropsWithChildren<PanelCanvasElementProps<T>>) => {
const [offsetX, setOffsetX] = useState(() => element.position.x);
const [offsetY, setOffsetY] = useState(() => element.position.y);
Expand Down Expand Up @@ -154,11 +158,13 @@ export const PanelCanvasElement = <T extends PossibleCanvasElements>({
setOffsetY(roundToGrid(editPositionY));
}, [editPositionX, editPositionY]);

const { inEditMode } = useWorkspace();

const canvasElementRef = useRef<HTMLDivElement>(null);

const handlePanStart = (event: MouseEvent) => {
if (event.button !== 0) {
return;
}

document.body.addEventListener('mouseup', handlePanStop);
document.body.addEventListener('mousemove', handleMouseMove);
event.stopPropagation();
Expand Down Expand Up @@ -186,7 +192,13 @@ export const PanelCanvasElement = <T extends PossibleCanvasElements>({
};

return (
<span className="absolute" onMouseDown={handleMouseDown}>
<span
className="absolute"
onMouseDown={(event) => {
handleMouseDown(event);
handlePanStart(event);
}}
>
<span
ref={canvasElementRef}
className="shadow-md"
Expand All @@ -198,13 +210,9 @@ export const PanelCanvasElement = <T extends PossibleCanvasElements>({
>
<span className="absolute flex flex-row h-12 -top-16 justify-between items-center">
<h1 style={{ fontSize: `${TITLE_FONTSIZE * (1 / canvasZoom)}px` }}>{title}</h1>

{inEditMode && (
<IconArrowsMaximize className="hover:text-red-500 hover:cursor-pointer" onMouseDown={handlePanStart} />
)}
</span>

<span className="block border border-[#00c2cc] hover:border-green-500 overflow-hidden">
<span className={`block border ${selected && 'border-8'} border-[#00c2cc] hover:border-green-500 overflow-hidden`}>
{children}
</span>
</span>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/Pages/ProjectHome/Components/EditMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const EditMenu = () => {
{availableInstruments.map((instrument) => (
<button
type="button"
key={instrument}
onClick={() => {
addInstrument(instrument);
}}
Expand Down
96 changes: 75 additions & 21 deletions src/renderer/Pages/ProjectHome/ProjectWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,18 @@ export const ProjectWorkspace = () => {

const [inInteractionMode, setInInteractionMode] = useState(false);

const [shift, setShift] = useState(false);
const [control, setControl] = useState(false);

const [canvasElements, setCanvasElements] = useState<PossibleCanvasElements[]>([]);
const [selectedCanvasElements, setSelectedCanvasElements] = useState<PossibleCanvasElements[]>([]);

const dispatch = useAppDispatch();

useChangeDebounce(() => {
dispatch(pushNotification(`Interaction Mode: ${inInteractionMode ? 'ON' : 'OFF'}`));
}, 500, [inInteractionMode]);

useEffect(() => {
const handler = (ev: KeyboardEvent) => {
if (ev.key.toUpperCase() === 'ENTER') {
setInInteractionMode((old) => !old);
}
};

window.addEventListener('keydown', handler, true);

return () => window.removeEventListener('keydown', handler);
}, []);

const [inEditMode, setInEditMode] = useState(false);

const doLoadProjectCanvasSave = useCallback(() => {
Expand All @@ -49,8 +43,6 @@ export const ProjectWorkspace = () => {
setCanvasElements(canvasSave.elements);
}, [project]);

const [canvasElements, setCanvasElements] = useState<PossibleCanvasElements[]>([]);

useEffect(() => {
if (project) {
doLoadProjectCanvasSave();
Expand Down Expand Up @@ -90,6 +82,61 @@ export const ProjectWorkspace = () => {
const [liveReloadConfigHandler, setLiveReloadConfigHandler] = useState<ProjectLiveReloadHandler>(null);
const [liveReloadDispatcher, setLiveReloadDispatcher] = useState<LiveReloadDispatcher>(null);

function tryAddElementToSelected(addition: PossibleCanvasElements) {
if (!selectedCanvasElements.includes(addition)) {
setSelectedCanvasElements((canvasElements) => [...canvasElements, addition]);
}
}

useEffect(() => {
function downHandler(event: KeyboardEvent) {
const k = event.key.toUpperCase();

if (k === 'SHIFT') {
setShift(true);
}
if (k === 'CONTROL') {
setControl(true);
}
if (k === 'ENTER') {
setInInteractionMode((old) => !old);
}
if (k === 'DELETE' || k === 'BACKSPACE') {
for (const element of selectedCanvasElements) {
handleDeleteCanvasElement(element);
}
}
}

function upHandler(event:KeyboardEvent) {
if (event.key.toUpperCase() === 'SHIFT') {
setShift(false);
}
if (event.key.toUpperCase() === 'CONTROL') {
setControl(false);
}
}

window.addEventListener('keydown', downHandler);
window.addEventListener('keyup', upHandler);

return () => {
window.removeEventListener('keydown', downHandler);
window.removeEventListener('keyup', upHandler);
};
}, [selectedCanvasElements]);

function handleElementClick(element: PossibleCanvasElements, event: React.MouseEvent) {
if (event.button === 0) {
setContextMenuOpen(false);
}

if (shift) {
tryAddElementToSelected(element);
} else {
setSelectedCanvasElements([element]);
}
}
const [simVarControlsHandler, setSimVarControlsHandler] = useState<SimVarControlsHandler>(null);

useEffect(() => {
Expand Down Expand Up @@ -130,6 +177,11 @@ export const ProjectWorkspace = () => {
return e.button === 2;
});

if (e.button === 0) {
setSelectedCanvasElements([]);
e.stopPropagation();
}

if (e.button === 2) {
let x: number;
let y: number;
Expand Down Expand Up @@ -194,13 +246,15 @@ export const ProjectWorkspace = () => {
{canvasElements.map((canvasElement) => {
if (canvasElement.__kind === 'instrument') {
return (
<InstrumentFrameElement
key={canvasElement.title}
instrumentFrame={canvasElement}
zoom={zoom}
onDelete={() => handleDeleteCanvasElement(canvasElement)}
onUpdate={handleUpdateCanvasElement}
/>
<div onClick={(event) => handleElementClick(canvasElement, event)}>
<InstrumentFrameElement
key={canvasElement.__uuid}
instrumentFrame={canvasElement}
zoom={zoom}
onUpdate={handleUpdateCanvasElement}
selected={selectedCanvasElements.includes(canvasElement)}
/>
</div>
);
}
return null;
Expand Down