Skip to content

Commit

Permalink
Merge pull request #392 from Lemoncode/dev
Browse files Browse the repository at this point in the history
del key and inline edit flag
  • Loading branch information
brauliodiez authored Sep 20, 2024
2 parents 1dc9cd4 + 9b9d9e1 commit 9ac8716
Show file tree
Hide file tree
Showing 12 changed files with 54 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { Group, Rect, Text } from 'react-konva';
import { ShapeSizeRestrictions, ShapeType } from '@/core/model';
import { forwardRef, useEffect, useState } from 'react';
import { forwardRef } from 'react';
import { ShapeProps } from '../../front-components/shape.model';
import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions';
import { BASIC_SHAPE } from '../../front-components/shape.const';
import { useShapeComponentSelection } from '../../shapes/use-shape-selection.hook';
import { mapHorizontalMenuTextToItems } from './hozontal-menu.business';
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import {
extractCSVHeaders,
splitCSVContentIntoRows,
} from '@/common/utils/active-element-selector.utils';

const horizontalMenuShapeSizeRestrictions: ShapeSizeRestrictions = {
minWidth: 75,
minWidth: 200,
minHeight: 25,
maxWidth: -1,
maxHeight: 100,
Expand All @@ -35,36 +38,17 @@ export const HorizontalMenu = forwardRef<any, ShapeProps>((props, ref) => {
...shapeProps
} = props;

const [selectedItem, setSelectedItem] = useState<number | null>(null);
const [items, setItems] = useState<string[]>([
'[*]Home, About, Services, Contact',
]);
const handleClick = (itemIndex: number) => {
setSelectedItem(itemIndex);
onSelected(id, 'horizontal-menu', true);
};
const csvData = splitCSVContentIntoRows(text);
const headers = extractCSVHeaders(csvData[0]);
const itemLabels = headers.map(header => header.text);

useEffect(() => {
if (typeof text === 'string') {
const { items, selectedItemIndex } = mapHorizontalMenuTextToItems(text);
setItems(items);
setSelectedItem(selectedItemIndex);
} else {
setItems([]);
}
}, [text]);
const numberOfItems = itemLabels.length;
const itemSpacing = 10;

const numberOfItems = items.length;
const minItemWidth = 100;
const itemSpacing = 20;
const totalWidth = Math.max(
minItemWidth * numberOfItems + itemSpacing * (numberOfItems + 1),
width
);
const { width: restrictedWidth, height: restrictedHeight } =
fitSizeToShapeSizeRestrictions(
horizontalMenuShapeSizeRestrictions,
totalWidth,
width,
height
);
const totalMargins = restrictedWidth - itemSpacing * (numberOfItems + 1);
Expand All @@ -76,8 +60,11 @@ export const HorizontalMenu = forwardRef<any, ShapeProps>((props, ref) => {
otherProps,
BASIC_SHAPE
);

const itemVerticalPadding = 4;

const activeSelected = otherProps?.activeElement ?? 0;

return (
<Group
x={x}
Expand All @@ -100,21 +87,19 @@ export const HorizontalMenu = forwardRef<any, ShapeProps>((props, ref) => {
cornerRadius={borderRadius}
/>

{items.map((e: string, index: number) => (
<Group key={index} onClick={() => handleClick(index)}>
{itemLabels.map((header, index) => (
<Group key={index}>
<Rect
x={itemSpacing * (index + 1) + itemWidth * index}
y={itemVerticalPadding}
width={itemWidth}
height={restrictedHeight - 2 * itemVerticalPadding}
fill={selectedItem === index ? 'lightblue' : fill}
stroke={selectedItem === index ? 'skyblue' : 'transparent'}
strokeWidth={selectedItem === index ? 1 : 0}
fill={index === activeSelected ? 'lightblue' : fill}
/>
<Text
x={itemSpacing * (index + 1) + itemWidth * index}
y={restrictedHeight / 2 - 8}
text={e}
text={header}
fontFamily="Arial"
fontSize={16}
fill={textColor}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EditType } from '@/core/model';
import { useCanvasContext } from '@/core/providers';
import { useEffect, useRef, useState } from 'react';

interface Configuration {
Expand All @@ -18,6 +19,7 @@ export const useSubmitCancelHook = (
const divRef = useRef<HTMLDivElement>(null);

const [isEditing, setIsEditing] = useState(false);
const { setIsInlineEditing } = useCanvasContext();

const getActiveInputRef = ():
| HTMLInputElement
Expand Down Expand Up @@ -45,6 +47,7 @@ export const useSubmitCancelHook = (
!getActiveInputRef()?.contains(event.target as Node)
) {
setIsEditing(false);
setIsInlineEditing(false);
if (editType === 'input' || editType === 'textarea') {
const inputRef = getActiveInputRef() as any;
onTextSubmit(inputRef?.value || '');
Expand All @@ -55,11 +58,13 @@ export const useSubmitCancelHook = (
const handleKeyDown = (event: KeyboardEvent) => {
if (isEditing && event.key === 'Escape') {
setIsEditing(false);
setIsInlineEditing(false);
setEditText(text);
}

if (editType === 'input' && isEditable && event.key === 'Enter') {
setIsEditing(false);
setIsInlineEditing(false);
if (editType === 'input' || editType === 'textarea') {
const inputRef = getActiveInputRef() as any;
onTextSubmit(inputRef?.value || '');
Expand Down
4 changes: 4 additions & 0 deletions src/common/components/inline-edit/inline-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Group } from 'react-konva';
import { Coord, EditType, Size } from '@/core/model';
import { HtmlEditWidget } from './components';
import { useSubmitCancelHook, usePositionHook } from './hooks';
import { useCanvasContext } from '@/core/providers';

interface Props {
coords: Coord;
Expand Down Expand Up @@ -30,6 +31,7 @@ export const EditableComponent: React.FC<Props> = props => {
} = props;
const [editText, setEditText] = useState(text);
const isInputInitiallyFocused = useRef(false);
const { setIsInlineEditing } = useCanvasContext();

const { inputRef, textAreaRef, divRef, isEditing, setIsEditing } =
useSubmitCancelHook(
Expand All @@ -45,6 +47,7 @@ export const EditableComponent: React.FC<Props> = props => {
const handleDoubleClick = () => {
if (isEditable) {
setIsEditing(true);
setIsInlineEditing(true);
}
};

Expand Down Expand Up @@ -90,6 +93,7 @@ export const EditableComponent: React.FC<Props> = props => {
const handleImageSrcSubmit = (src: string) => {
onImageSrcSubmit(src);
setIsEditing(false);
setIsInlineEditing(false);
};

return (
Expand Down
2 changes: 2 additions & 0 deletions src/core/providers/canvas/canvas.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export interface CanvasContextModel {
copyShapeToClipboard: () => void;
pasteShapeFromClipboard: () => void;
loadDocument: (document: DocumentModel) => void;
isInlineEditing: boolean;
setIsInlineEditing: React.Dispatch<React.SetStateAction<boolean>>;
}

export interface DocumentModel {
Expand Down
3 changes: 3 additions & 0 deletions src/core/providers/canvas/canvas.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const CanvasProvider: React.FC<Props> = props => {

const [scale, setScale] = React.useState(1);
const stageRef = React.useRef<Konva.Stage>(null);
const [isInlineEditing, setIsInlineEditing] = React.useState(false);

const {
addSnapshot,
Expand Down Expand Up @@ -181,6 +182,8 @@ export const CanvasProvider: React.FC<Props> = props => {
stageRef,
deleteSelectedShapes,
loadDocument,
isInlineEditing,
setIsInlineEditing,
}}
>
{children}
Expand Down
10 changes: 9 additions & 1 deletion src/pods/canvas/canvas.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ const generateDefaultTextValue = (shapeType: ShapeType): string | undefined => {
case 'listbox':
return '[*]Item\nItem1\nItem2\nItem3\nItem4\nItem5\nItem6';
case 'horizontal-menu':
return '[*]Home, About, Services, Contact';
return 'Home, About, Services, Contact';
case 'vertical-menu':
return 'Option 1\nOption 2\n----\nOption 3\nOption 4';
case 'heading1':
Expand Down Expand Up @@ -396,6 +396,14 @@ export const generateDefaultOtherProps = (
case 'listbox':
case 'vertical-menu':
case 'horizontal-menu':
return {
stroke: BASIC_SHAPE.DEFAULT_STROKE_COLOR,
backgroundColor: BASIC_SHAPE.DEFAULT_FILL_BACKGROUND,
textColor: BASIC_SHAPE.DEFAULT_FILL_TEXT,
strokeStyle: [],
borderRadius: `${BASIC_SHAPE.DEFAULT_CORNER_RADIUS}`,
activeElement: 0,
};
case 'datepickerinput':
case 'timepickerinput':
return {
Expand Down
2 changes: 0 additions & 2 deletions src/pods/canvas/canvas.pod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ export const CanvasPod = () => {
updateShapePosition(id, { x, y });
};

// TODO: Temporary disabled, conflicts with inline edition
// and likely keboard shortcuts
useKeyboardDisplacement();

{
Expand Down
20 changes: 3 additions & 17 deletions src/pods/canvas/use-keyboard-displacement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { useCanvasContext } from '@/core/providers';
import { Coord } from '@/core/model';

export const useKeyboardDisplacement = () => {
const { selectionInfo, updateShapePosition } = useCanvasContext();
const { selectionInfo, updateShapePosition, isInlineEditing } =
useCanvasContext();

// TODO: move this to business/utils
const updateShapeCollectionPosition = (
Expand All @@ -25,24 +26,9 @@ export const useKeyboardDisplacement = () => {
});
};

const isKeyboardKey = (key: string) => {
return ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(key);
};

useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
// Keydown keys can conflict when we are performing inlne edtiion
// input and textarea will use the cursosr and stage keyboard should not
// the bubble order is Stage >> Input, so we cannot stop propagation
// BUT
// We have added a data attribute to the input and textarea to check
// if the inline edition is on
// here we check if the event target has the data attribute
// then we return and let the input and textare control it
const isInlineEditing =
(event.target as any)?.attributes['data-is-inline-edition-on'] !==
undefined;
if (isInlineEditing || !isKeyboardKey(event.key)) {
if (isInlineEditing) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export const ActiveElementSelector: React.FC<Props> = ({
};

// Checking whether the type is tabsBar and parsing the text
const isElementTypeSupported = type === 'tabsBar' || 'buttonBar';
const isElementTypeSupported =
type === 'tabsBar' || 'buttonBar' || 'horizontal-menu';
const elementNames =
isElementTypeSupported && text ? extractElementNames(text) : [];

Expand Down
4 changes: 2 additions & 2 deletions src/pods/toolbar/shortcut/shortcut.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const SHORTCUTS: Shortcut = {
delete: {
description: 'Delete',
id: 'delete-button-shortcut',
targetKey: ['Ctrl+backspace', 'Meta+backspace'],
targetKeyLabel: 'Ctrl + Backspace',
targetKey: ['backspace', 'delete'],
targetKeyLabel: 'Backspace',
},
copy: {
description: 'Copy',
Expand Down
5 changes: 5 additions & 0 deletions src/pods/toolbar/shortcut/shortcut.hook.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isMacOS } from '@/common/helpers/platform.helpers';
import { useCanvasContext } from '@/core/providers';
import { useEffect } from 'react';

export interface ShortcutHookProps {
Expand All @@ -7,7 +8,11 @@ export interface ShortcutHookProps {
}

export const useShortcut = ({ targetKey, callback }: ShortcutHookProps) => {
const { isInlineEditing } = useCanvasContext();
const handleKeyPress = (event: KeyboardEvent) => {
if (isInlineEditing) {
return;
}
// TODO: later on this needs discussio about shortcut keys
// Right now enable CTRL+C, CTRL+V for windows, linux and mac
//const isAltKeyPressed = event.getModifierState('Alt');
Expand Down

0 comments on commit 9ac8716

Please sign in to comment.