Skip to content

Commit

Permalink
Merge branch 'feature/#150-create-line-shape-under-basic-shapes' of g…
Browse files Browse the repository at this point in the history
…ithub.com:Lemoncode/quickmock into feature/#150-create-line-shape-under-basic-shapes
  • Loading branch information
oleojake committed Aug 12, 2024
2 parents 8e6ee87 + d955d2b commit 60a63b9
Show file tree
Hide file tree
Showing 33 changed files with 544 additions and 89 deletions.
16 changes: 16 additions & 0 deletions public/rich-components/accordion.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 96 additions & 0 deletions src/common/components/front-rich-components/accordion.business.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions';
import { ShapeSizeRestrictions } from '@/core/model';

// TODO: Add unit tests #169

interface SizeInfo {
width: number;
height: number;
singleHeaderHeight: number;
accordionShapeSizeRestrictions: ShapeSizeRestrictions;
accordionSelectedBodyHeight: number;
}

export const calculateDynamicContentSizeRestriction = (
sections: string[],
sizeInfo: SizeInfo
) => {
const {
width,
height,
singleHeaderHeight,
accordionShapeSizeRestrictions,
accordionSelectedBodyHeight,
} = sizeInfo;

// Accordion section height:
const accordionsHeadersHeight = singleHeaderHeight * sections.length;

const restrictedSize = fitSizeToShapeSizeRestrictions(
accordionShapeSizeRestrictions,
width,
height
);

restrictedSize.height = accordionsHeadersHeight + accordionSelectedBodyHeight;

return restrictedSize;
};

interface SectionsInfo {
sections: string[];
selectedSectionIndex: number;
}

// TODO: Add Unit tests
// case 1 if text is empty just show default sections
// case 2 if text has 1 section, then show 1 section and selectedSectionIndex = 0
// case 3 if text has 2 sections, and section to starts with [*] then show 2 sections and selectedSectionIndex = 1, and section with removed [*]
// case 4 if text has 2 sections, and no section to starts with [*] then show 2 sections and selectedSectionIndex = 0, and section with removed [*]
// ...
// If there are more than one section selected, pick the first one and remove the [*] from all of them
export const mapTextToSections = (text: string): SectionsInfo => {
if (!text) {
return {
sections: ['Section A', 'Section B'],
selectedSectionIndex: 0,
};
}

let sections: string[] = text.split('\n');

const selectedSectionIndex = sections.findIndex(section =>
section.startsWith('[*]')
);

sections = sections.map(section => section.replace(/^\[\*\]/, ''));

return {
sections,
selectedSectionIndex:
selectedSectionIndex === -1 ? 0 : selectedSectionIndex,
};
};

// TODO: Add unit tests
interface SelectedAccordionSizeInfo {
height: number;
minimumAccordionBodyHeight: number;
singleHeaderHeight: number;
}

export const calculateSelectedAccordionHeight = (
sections: string[],
sizeInfo: SelectedAccordionSizeInfo
) => {
const { height, minimumAccordionBodyHeight, singleHeaderHeight } = sizeInfo;

const accordionsHeadersHeight = singleHeaderHeight * sections.length;
let accordionSelectedBodyHeight = height - accordionsHeadersHeight;

if (accordionSelectedBodyHeight < 0) {
accordionSelectedBodyHeight = minimumAccordionBodyHeight;
}

return accordionSelectedBodyHeight;
};
83 changes: 83 additions & 0 deletions src/common/components/front-rich-components/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Group } from 'react-konva';
import { ShapeSizeRestrictions } from '@/core/model';
import { forwardRef, useEffect, useMemo, useState } from 'react';
import { ShapeProps } from '../front-components/shape.model';
import { AccordionAllParts } from './components';
import {
calculateDynamicContentSizeRestriction,
calculateSelectedAccordionHeight,
mapTextToSections,
} from './accordion.business';

const accordionShapeSizeRestrictions: ShapeSizeRestrictions = {
minWidth: 315,
minHeight: 225,
maxWidth: -1,
maxHeight: -1,
defaultWidth: 315,
defaultHeight: 250,
};

export const getAccordionShapeSizeRestrictions = (): ShapeSizeRestrictions =>
accordionShapeSizeRestrictions;

const singleHeaderHeight = 50;
const minimumAccordionBodyHeight = 60;

export const AccordionShape = forwardRef<any, ShapeProps>(
({ x, y, width, height, id, onSelected, text, ...shapeProps }, ref) => {
const [sections, setSections] = useState<string[]>([
'[*] Sectión A',
'Sectión B',
]);
const [selectedSectionIndex, setSelectedSectionIndex] = useState(0);

useEffect(() => {
if (text) {
const { sections, selectedSectionIndex } = mapTextToSections(text);
setSections(sections);
setSelectedSectionIndex(selectedSectionIndex);
} else {
setSections([]);
}
}, [text]);

const accordionSelectedBodyHeight = useMemo(() => {
return calculateSelectedAccordionHeight(sections, {
height,
minimumAccordionBodyHeight,
singleHeaderHeight,
});
}, [sections, height]);

const { width: restrictedWidth, height: restrictedHeight } =
calculateDynamicContentSizeRestriction(sections, {
width,
height,
singleHeaderHeight,
accordionShapeSizeRestrictions,
accordionSelectedBodyHeight,
});

return (
<Group
x={x}
y={y}
ref={ref}
width={restrictedWidth}
height={restrictedHeight}
{...shapeProps}
onClick={() => onSelected(id, 'accordion')}
fill="black"
>
<AccordionAllParts
width={restrictedWidth}
singleHeaderHeight={singleHeaderHeight}
accordionSelectedBodyHeight={accordionSelectedBodyHeight}
sections={sections}
selectedSectionIndex={selectedSectionIndex}
/>
</Group>
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { AccordionBody } from './accordion-body.component';
import { AccordionHeader } from './accordion-header.component';

interface Props {
width: number;
singleHeaderHeight: number;
accordionSelectedBodyHeight: number;
sections: string[];
selectedSectionIndex: number;
}

export const AccordionAllParts: React.FC<Props> = props => {
const {
singleHeaderHeight,
accordionSelectedBodyHeight,
sections,
selectedSectionIndex,
width,
} = props;

let accordionBodyAppliedOffset = 0;

const renderAccordionBody = (headerIndex: number) => {
accordionBodyAppliedOffset = accordionSelectedBodyHeight;
const marginLeft = 10;
return (
<AccordionBody
x={marginLeft}
y={(headerIndex + 1) * singleHeaderHeight}
width={width - marginLeft}
height={accordionSelectedBodyHeight}
/>
);
};
const renderAccordion = () => {
const textMarginLeft = 10;
return sections.map((section, index) => (
<>
<AccordionHeader
x={textMarginLeft}
y={singleHeaderHeight * index + accordionBodyAppliedOffset}
width={width - textMarginLeft}
height={singleHeaderHeight}
text={section}
isSelected={selectedSectionIndex === index}
/>
{selectedSectionIndex === index ? renderAccordionBody(index) : null}
</>
));
};

return renderAccordion();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { forwardRef } from 'react';
import { Group, Rect } from 'react-konva';
import { ShapeProps } from '../../front-components/shape.model';

export const AccordionBody = forwardRef<any, ShapeProps>(
({ x, y, width, height, ...shapeProps }, ref) => {
return (
<Group
x={x}
y={y}
ref={ref}
width={width}
height={height}
{...shapeProps}
>
<Rect
x={0}
y={0}
width={width}
height={height}
fill="white"
stroke="black"
strokeWidth={2}
/>
</Group>
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { forwardRef } from 'react';
import { Group, Rect, Text } from 'react-konva';
import { ShapeProps } from '../../front-components/shape.model';
import { TriangleSelector } from './triangle-selector.component';

interface Props extends ShapeProps {
isSelected: boolean;
}

export const AccordionHeader = forwardRef<any, Props>(
({ x, y, width, height, text, isSelected, ...shapeProps }, ref) => {
return (
<Group
x={x}
y={y}
ref={ref}
width={width}
height={height}
{...shapeProps}
>
<Rect
x={0}
y={0}
width={width}
height={height}
fill="#f0f0f0"
stroke="black"
strokeWidth={2}
/>
<TriangleSelector x={5} y={9} isSelected={isSelected} />
<Text
x={40}
y={20}
text={text}
fontFamily="Arial"
fontSize={20}
fill="black"
/>
</Group>
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './accordion-all-parts.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Group, Line } from 'react-konva';

interface Props {
x: number;
y: number;
}

export const TriangleDown: React.FC<Props> = props => {
const points = [
props.x,
props.y,
props.x + 20,
props.y,
props.x + 10,
props.y + 20,
];

return (
<Group x={props.x} y={props.y}>
<Line points={points} fill="black" closed />
</Group>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Group, Line } from 'react-konva';

interface Props {
x: number;
y: number;
}

export const TriangleLeft: React.FC<Props> = ({ x, y }) => {
const points = [x, y, x, y + 20, x + 20, y + 10];

return (
<Group x={x} y={y}>
<Line points={points} fill="black" closed />
</Group>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { TriangleDown } from './triangle-down.component';
import { TriangleLeft } from './triangle-lef.component';

interface Props {
x: number;
y: number;
isSelected: boolean;
}

export const TriangleSelector: React.FC<Props> = props => {
const { x, y, isSelected } = props;

return isSelected ? (
<TriangleDown x={x} y={y} />
) : (
<TriangleLeft x={x} y={y} />
);
};
1 change: 1 addition & 0 deletions src/common/components/front-rich-components/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './video-player';
export * from './accordion';
Loading

0 comments on commit 60a63b9

Please sign in to comment.