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

Feature/#226 create table rich component #249

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
How this size calculation works {}:

If no width instructions {} are provided, all columns occupy the same space and scale equally.

If instructions are provided that sum to 100% {50,30,20}, each column takes up and scales by that percentage.

If instructions are provided that exceed 100% {80,30,20}: Now 100% is equivalent to 130%, and the columns behave the same as the previous example.

If instructions are provided that are less than 100% {50,20,15}: The remaining 15% is divided equally among the columns, adding 5% to each one.

Now let's talk about null values, which can be indicated with a 0, '', or .

If the values sum to 100% or more, any column with a 0 will not be displayed.

If the values are less than 100%, the number of columns with a null value is calculated, and the remaining percentage is divided among them {50,20,10,,}. Each would receive 10%.

* --> mean take all remaining space avialable
*/

const calculateTotalWidth = (widthRow: string[]): number => {
return widthRow.reduce((acc, width) => acc + parseInt(width), 0);
};

const calculateColumnsWithZeroWidth = (widthRow: string[]): number => {
return widthRow.filter(width => width === '0').length;
};

const generateCellsWidthsCasePercentagesSumTo100OrMore = (
widthRow: string[],
columnCount: number,
restrictedWidth: number
) => {
let cellWidths: number[] = [];

for (let i = 0; i < columnCount; i++) {
cellWidths.push((restrictedWidth * parseInt(widthRow[i])) / 100);
}

return cellWidths;
};

const generateCellsWidthsCasePercentagesSumLessThan100AndNoBlankWidthColumns = (
widthRow: string[],
columnCount: number,
restrictedWidth: number,
remainingWidth: number
): number[] => {
let cellWidths: number[] = [];

const remainWidthCol = (remainingWidth * restrictedWidth) / 100 / columnCount; // Divide el ancho restante entre el número de columnas
for (let i = 0; i < columnCount; i++) {
cellWidths.push(
(restrictedWidth * parseInt(widthRow[i])) / 100 + remainWidthCol
);
}

return cellWidths;
};

const generateCellsWidthsCasePercentagesSumLessThan100AndBlankWidthColumns = (
widthRow: string[],
columnCount: number,
restrictedWidth: number,
remainingWidth: number
): number[] => {
let cellWidths: number[] = [];

for (let i = 0; i < columnCount; i++) {
if (widthRow[i] === '0') {
cellWidths.push(
(restrictedWidth * remainingWidth) /
calculateColumnsWithZeroWidth(widthRow) /
100
);
} else {
cellWidths.push((restrictedWidth * parseInt(widthRow[i])) / 100);
}
}

return cellWidths;
};

const generateCellsWidthsCaseNoWidthRowPresent = (
columnCount: number,
restrictedWidth: number
) => {
let cellWidths: number[] = [];
for (let i = 0; i < columnCount; i++) {
cellWidths.push(restrictedWidth / columnCount);
}

return cellWidths;
};

// TODO: Add unit tests to this function
// #253
// https://github.com/Lemoncode/quickmock/issues/253
export const calculateCellWidths = (
restrictedWidth: number,
columnCount: number,
widthRow: string[] | false
): number[] => {
if (!widthRow) {
// No width row present
return generateCellsWidthsCaseNoWidthRowPresent(
columnCount,
restrictedWidth
);
}

const remainingWidth = 100 - calculateTotalWidth(widthRow);
// The percentages sum to 100% or more
if (remainingWidth <= 0) {
return generateCellsWidthsCasePercentagesSumTo100OrMore(
widthRow,
columnCount,
restrictedWidth
);
}
// The percentages sum to less than 100%
// No columns have a width of 0; the remaining width is divided among all columns
if (calculateColumnsWithZeroWidth(widthRow) === 0) {
return generateCellsWidthsCasePercentagesSumLessThan100AndNoBlankWidthColumns(
widthRow,
columnCount,
restrictedWidth,
remainingWidth
);
}
// There are columns with a width of 0; the remaining width is divided among those columns
return generateCellsWidthsCasePercentagesSumLessThan100AndBlankWidthColumns(
widthRow,
columnCount,
restrictedWidth,
remainingWidth
);
};
137 changes: 78 additions & 59 deletions src/common/components/front-rich-components/table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import {
extractDataRows,
extractHeaderRow,
extractWidthRow,
parseCSVRowsIntoArray,
} from './table.utils';
import { calculateCellWidths } from './table-col-width.utils';

const tableSizeRestrictions: ShapeSizeRestrictions = {
minWidth: 1,
minHeight: 75,
maxWidth: -1,
maxHeight: -1,
defaultWidth: 250,
defaultHeight: 100,
defaultHeight: 150,
};

export const getTableSizeRestrictions = (): ShapeSizeRestrictions =>
Expand All @@ -28,10 +30,13 @@ export const Table = forwardRef<any, ShapeProps>(

const rows = parseCSVRowsIntoArray(text);
const headerRow = extractHeaderRow(rows[0]);
const dataRows = extractDataRows(rows);

const columnCount = headerRow.length;
const cellWidth = restrictedWidth / columnCount;
const widthRow: string[] | false = extractWidthRow(rows[rows.length - 1]);
const dataRows = extractDataRows(rows, widthRow);
const cellWidths = calculateCellWidths(
restrictedWidth,
headerRow.length,
widthRow
);
const cellHeight = restrictedHeight / (dataRows.length + 1);

return (
Expand All @@ -45,76 +50,90 @@ export const Table = forwardRef<any, ShapeProps>(
onClick={() => onSelected(id, 'table')}
>
{/* Dibujar celdas de encabezado */}
{headerRow.map((header, colIdx) => (
<Group key={`header-${colIdx}`}>
<Rect
x={colIdx * cellWidth}
y={0}
width={cellWidth}
height={cellHeight}
stroke="black"
strokeWidth={1}
fill="lightgrey"
/>
<Text
x={colIdx * cellWidth + 5}
y={5}
width={cellWidth - 10}
height={cellHeight - 10}
text={header}
fontSize={14}
fontStyle="bold"
align="center"
verticalAlign="middle"
wrap="none"
ellipsis={true}
/>
</Group>
))}
{headerRow.map((header, colIdx) => {
// Calcular la posición acumulativa para la celda
const accumulatedWidth = cellWidths
.slice(0, colIdx)
.reduce((a, b) => a + b, 0);

{/* Dibujar celdas de datos */}
{dataRows.map((row, rowIdx) =>
row.map((cell, colIdx) => (
<Group key={`cell-${rowIdx}-${colIdx}`}>
return (
<Group key={`header-${colIdx}`}>
<Rect
x={colIdx * cellWidth}
y={(rowIdx + 1) * cellHeight}
width={cellWidth}
x={accumulatedWidth}
y={0}
width={cellWidths[colIdx]}
height={cellHeight}
stroke="black"
strokeWidth={1}
fill="white"
fill="lightgrey"
/>
<Text
x={colIdx * cellWidth + 5}
y={(rowIdx + 1) * cellHeight + 5}
width={cellWidth - 10}
x={accumulatedWidth + 5}
y={5}
width={cellWidths[colIdx] - 10}
height={cellHeight - 10}
text={cell}
fontSize={12}
text={header}
fontSize={14}
fontStyle="bold"
align="center"
verticalAlign="middle"
wrap="none"
ellipsis={true}
/>
</Group>
))
)}
);
})}

{/* Dibujar celdas de datos */}
{dataRows.map((row, rowIdx) => {
let accumulatedWidth = 0;
return row.map((cell, colIdx) => {
const currentX = accumulatedWidth;
accumulatedWidth += cellWidths[colIdx];

return (
<Group key={`cell-${rowIdx}-${colIdx}`}>
<Rect
x={currentX}
y={(rowIdx + 1) * cellHeight}
width={cellWidths[colIdx]}
height={cellHeight}
stroke="black"
strokeWidth={1}
fill="white"
/>
<Text
x={currentX + 5}
y={(rowIdx + 1) * cellHeight + 5}
width={cellWidths[colIdx] - 10}
height={cellHeight - 10}
text={cell}
fontSize={12}
align="center"
verticalAlign="middle"
wrap="none"
ellipsis={true}
/>
</Group>
);
});
})}

{/* Dibujar líneas de la cuadrícula verticales */}
{[...Array(columnCount + 1)].map((_, colIdx) => (
<Line
key={`vline-${colIdx}`}
points={[
colIdx * cellWidth,
0,
colIdx * cellWidth,
restrictedHeight,
]}
stroke="black"
strokeWidth={1}
/>
))}
{cellWidths.reduce((lines: JSX.Element[], _width, colIdx) => {
const accumulatedWidth = cellWidths
.slice(0, colIdx)
.reduce((a, b) => a + b, 0);
lines.push(
<Line
key={`vline-${colIdx}`}
points={[accumulatedWidth, 0, accumulatedWidth, restrictedHeight]}
stroke="black"
strokeWidth={1}
/>
);
return lines;
}, [])}

{/* Dibujar líneas de la cuadrícula horizontales */}
{[...Array(dataRows.length + 2)].map((_, rowIdx) => (
Expand Down
31 changes: 27 additions & 4 deletions src/common/components/front-rich-components/table/table.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,31 @@ export const extractHeaderRow = (headerRow: string): string[] => {
return headerRow.split(',').map(header => header.trim());
};

export const extractDataRows = (rows: string[]): string[][] => {
return rows
.slice(1, rows.length)
.map(row => row.split(',').map(cell => cell.trim()));
export const extractDataRows = (
rows: string[],
widthRow: string[] | false
): string[][] => {
return widthRow
? rows
.slice(1, rows.length - 1)
.map(row => row.split(',').map(cell => cell.trim()))
: rows
.slice(1, rows.length)
.map(row => row.split(',').map(cell => cell.trim()));
};

export const extractWidthRow = (lastRow: string): string[] | false => {
return lastRow.startsWith('{') && lastRow.endsWith('}')
? lastRow
.slice(1, -1)
.split(',')
.map(width => {
const trimmedWidth = width.trim();
return trimmedWidth === '0' ||
trimmedWidth === '' ||
trimmedWidth === '*'
? '0'
: trimmedWidth;
})
: false;
};
2 changes: 1 addition & 1 deletion src/pods/canvas/canvas.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ const generateDefaultTextValue = (shapeType: ShapeType): string | undefined => {
case 'paragraph':
return 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nSed do eiusmod tempor incididunt ut labore et dolore magna \naliqua.Ut enim ad minim veniam, quis nostrud exercitation \nullamco laboris nisi ut aliquip ex ea commodo consequat \nDuis aute irure dolor in reprehenderit in voluptate velit\nesse cillum dolore eu fugiat nulla pariatur. \nExcepteur sint occaecat cupidatat non proident, sunt in \nculpa qui officia deserunt mollit anim id est laborum.';
case 'table':
return 'Name , Age , Country\nJohn Doe, 30, USA\nJane Smith, 25, UK\nLuis Gomez, 35, Argentina';
return 'Name , Age , Country\nJohn Doe, 30, USA\nJane Smith, 25, UK\nLuis Gomez, 35, Argentina\n{*,30,20}';
default:
return undefined;
}
Expand Down
Loading