From 3a9e08f84b97cced22b7ae9b3af6fdb0f2e8119e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ruiz?= Date: Mon, 8 Jul 2024 23:06:07 +0200 Subject: [PATCH 1/9] #507 Added autoarrange collection --- .../autoarrange-table.model.ts | 6 ++ .../autoarrange-table.utils.ts | 28 +++++++ src/common/autoarrange-table/index.ts | 84 +++++++++++++++++++ ...set-off-set-zoom-to-coords.helper.spec.tsx | 13 +++ .../canvas-view-settings.model.ts | 3 + .../canvas-view-settings.provider.tsx | 8 ++ src/pods/canvas/canvas.pod.tsx | 29 +++++++ .../add-collection.component.tsx | 61 ++++++++++++-- .../add-collection/add-collection.helper.tsx | 18 ++++ .../add-collection/add-collection.mapper.tsx | 17 ++++ 10 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 src/common/autoarrange-table/autoarrange-table.model.ts create mode 100644 src/common/autoarrange-table/autoarrange-table.utils.ts create mode 100644 src/common/autoarrange-table/index.ts create mode 100644 src/pods/toolbar/components/add-collection/add-collection.helper.tsx create mode 100644 src/pods/toolbar/components/add-collection/add-collection.mapper.tsx diff --git a/src/common/autoarrange-table/autoarrange-table.model.ts b/src/common/autoarrange-table/autoarrange-table.model.ts new file mode 100644 index 00000000..314c11f8 --- /dev/null +++ b/src/common/autoarrange-table/autoarrange-table.model.ts @@ -0,0 +1,6 @@ +export interface Box { + x: number; + y: number; + width: number; + height: number; +} diff --git a/src/common/autoarrange-table/autoarrange-table.utils.ts b/src/common/autoarrange-table/autoarrange-table.utils.ts new file mode 100644 index 00000000..bc1a4166 --- /dev/null +++ b/src/common/autoarrange-table/autoarrange-table.utils.ts @@ -0,0 +1,28 @@ +import { Box } from './autoarrange-table.model'; + +export const getRandomInt = (min: number, max: number): number => { + return Math.floor(Math.random() * (max - min + 1)) + min; +}; + +export const isOverlapping = (box1: Box, box2: Box): boolean => { + return ( + box1.x < box2.x + box2.width && + box1.x + box1.width > box2.x && + box1.y < box2.y + box2.height && + box1.y + box1.height > box2.y + ); +}; + +export const calculateCollisionArea = (box1: Box, box2: Box): number => { + const xOverlap = Math.max( + 0, + Math.min(box1.x + box1.width, box2.x + box2.width) - + Math.max(box1.x, box2.x) + ); + const yOverlap = Math.max( + 0, + Math.min(box1.y + box1.height, box2.y + box2.height) - + Math.max(box1.y, box2.y) + ); + return xOverlap * yOverlap; +}; diff --git a/src/common/autoarrange-table/index.ts b/src/common/autoarrange-table/index.ts new file mode 100644 index 00000000..7d5d8705 --- /dev/null +++ b/src/common/autoarrange-table/index.ts @@ -0,0 +1,84 @@ +import { Size } from '@/core/model'; +import { Box } from './autoarrange-table.model'; +import { + calculateCollisionArea, + isOverlapping, +} from './autoarrange-table.utils'; + +function* spiralPositions( + centerX: number, + centerY: number, + canvasSize: Size +): Generator<[number, number]> { + let x = 0, + y = 0, + dx = 0, + dy = -1; + + for (let i = 0; i < Math.max(canvasSize.width, canvasSize.height) ** 2; i++) { + if ( + -canvasSize.width / 2 < x && + x < canvasSize.width / 2 && + -canvasSize.height / 2 < y && + y < canvasSize.height / 2 + ) { + yield [centerX + x, centerY + y]; + } + if (x === y || (x < 0 && x === -y) || (x > 0 && x === 1 - y)) { + [dx, dy] = [-dy, dx]; + } + x += dx; + y += dy; + } +} + +export function findFreePositionOrMinCollision( + boxes: Box[], + newBoxSize: Size, + canvasSize: Size +): Box | null { + const centerX = Math.floor(canvasSize.width / 2); + const centerY = Math.floor(canvasSize.height / 2); + let minCollisionBox: Box | null = null; + let minCollisionArea = Infinity; + + for (const [x, y] of spiralPositions(centerX, centerY, canvasSize)) { + const newBox = { + x, + y, + width: newBoxSize.width, + height: newBoxSize.height, + }; + if ( + x >= 0 && + y >= 0 && + x + newBoxSize.width <= canvasSize.width && + y + newBoxSize.height <= canvasSize.height + ) { + let collisionArea = 0; + let isFree = true; + + for (const existingBox of boxes) { + if (isOverlapping(newBox, existingBox)) { + isFree = false; + collisionArea += calculateCollisionArea(newBox, existingBox); + } + } + + if (isFree) { + return newBox; + } + + if (collisionArea < minCollisionArea) { + minCollisionArea = collisionArea; + minCollisionBox = newBox; + } + } + } + + if (minCollisionBox !== null) { + return minCollisionBox; + } + // TODO: if no free position is found, return a random one + return null; +} diff --git a/src/common/helpers/set-off-set-zoom-to-coords.helper.spec.tsx b/src/common/helpers/set-off-set-zoom-to-coords.helper.spec.tsx index 40c558de..f5c34c85 100644 --- a/src/common/helpers/set-off-set-zoom-to-coords.helper.spec.tsx +++ b/src/common/helpers/set-off-set-zoom-to-coords.helper.spec.tsx @@ -10,6 +10,7 @@ describe('setOffSetZoomToCoords', () => { const initialContextState: CanvasViewSettingsContextModel = { canvasViewSettings: { canvasSize: { width: 5000, height: 5000 }, + canvasViewSize: { width: 5000, height: 5000 }, viewBoxSize: { width: 20000, height: 20000 }, zoomFactor: 2, scrollPosition: { x: 0, y: 0 }, @@ -25,6 +26,7 @@ describe('setOffSetZoomToCoords', () => { setLoadSample: () => {}, setViewBoxSize: () => {}, setAutoSave: () => {}, + setCanvasViewSize: () => {}, }; // Act @@ -51,6 +53,7 @@ describe('setOffSetZoomToCoords', () => { const initialContextState: CanvasViewSettingsContextModel = { canvasViewSettings: { canvasSize: { width: 10000, height: 10000 }, + canvasViewSize: { width: 5000, height: 5000 }, viewBoxSize: { width: 25000, height: 15000 }, zoomFactor: 2, scrollPosition: { x: 0, y: 0 }, @@ -66,6 +69,7 @@ describe('setOffSetZoomToCoords', () => { setLoadSample: () => {}, setViewBoxSize: () => {}, setAutoSave: () => {}, + setCanvasViewSize: () => {}, }; // Act @@ -92,6 +96,7 @@ describe('setOffSetZoomToCoords', () => { const initialContextState: CanvasViewSettingsContextModel = { canvasViewSettings: { canvasSize: { width: 300, height: 100 }, + canvasViewSize: { width: 5000, height: 5000 }, viewBoxSize: { width: 2000, height: 5000 }, zoomFactor: 5, scrollPosition: { x: 0, y: 0 }, @@ -107,6 +112,7 @@ describe('setOffSetZoomToCoords', () => { setLoadSample: () => {}, setViewBoxSize: () => {}, setAutoSave: () => {}, + setCanvasViewSize: () => {}, }; // Act @@ -135,6 +141,10 @@ describe('setOffSetZoomToCoords', () => { width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER, }, + canvasViewSize: { + width: Number.MAX_SAFE_INTEGER, + height: Number.MAX_SAFE_INTEGER, + }, viewBoxSize: { width: Number.MAX_SAFE_INTEGER, height: Number.MAX_SAFE_INTEGER, @@ -153,6 +163,7 @@ describe('setOffSetZoomToCoords', () => { setLoadSample: () => {}, setViewBoxSize: () => {}, setAutoSave: () => {}, + setCanvasViewSize: () => {}, }; // Act @@ -178,6 +189,7 @@ describe('setOffSetZoomToCoords', () => { const initialContextState: CanvasViewSettingsContextModel = { canvasViewSettings: { canvasSize: { width: 5000, height: 5000 }, + canvasViewSize: { width: 5000, height: 5000 }, viewBoxSize: { width: 20000, height: 20000 }, zoomFactor: 2, scrollPosition: { x: 0, y: 0 }, @@ -193,6 +205,7 @@ describe('setOffSetZoomToCoords', () => { setLoadSample: () => {}, setViewBoxSize: () => {}, setAutoSave: () => {}, + setCanvasViewSize: () => {}, }; // Act diff --git a/src/core/providers/canvas-view-settings/canvas-view-settings.model.ts b/src/core/providers/canvas-view-settings/canvas-view-settings.model.ts index 8ca9b015..54e07945 100644 --- a/src/core/providers/canvas-view-settings/canvas-view-settings.model.ts +++ b/src/core/providers/canvas-view-settings/canvas-view-settings.model.ts @@ -3,6 +3,7 @@ import { Coords, Size } from '@/core/model'; export interface CanvasViewSettingsModel { canvasSize: Size; + canvasViewSize: Size; viewBoxSize: Size; zoomFactor: number; scrollPosition: Coords; @@ -26,6 +27,7 @@ const initialAutoSaveValue = export const createInitialSettings = (DEFAULT_ZOOM_FACTOR: number) => ({ canvasSize: CANVAS_SIZE, + canvasViewSize: CANVAS_SIZE, viewBoxSize: { width: 0, height: 0 }, zoomFactor: DEFAULT_ZOOM_FACTOR, scrollPosition: { x: 0, y: 0 }, @@ -38,6 +40,7 @@ export interface CanvasViewSettingsContextModel { canvasViewSettings: CanvasViewSettingsModel; setScrollPosition: (scrollPosition: Coords) => void; setCanvasSize: (canvasSize: Size) => void; + setCanvasViewSize: (canvasViewSize: Size) => void; setFilename: (filename: string) => void; zoomIn: () => void; zoomOut: () => void; diff --git a/src/core/providers/canvas-view-settings/canvas-view-settings.provider.tsx b/src/core/providers/canvas-view-settings/canvas-view-settings.provider.tsx index 4103500c..b3f2e019 100644 --- a/src/core/providers/canvas-view-settings/canvas-view-settings.provider.tsx +++ b/src/core/providers/canvas-view-settings/canvas-view-settings.provider.tsx @@ -53,6 +53,13 @@ export const CanvasViewSettingsProvider: React.FC = props => { })); }; + const setCanvasViewSize = (canvasSize: Size) => { + setCanvasViewSettings(canvasViewSettings => ({ + ...canvasViewSettings, + canvasViewSize: canvasSize, + })); + }; + const setFilename = (filename: string) => { setCanvasViewSettings(canvasViewSettings => ({ ...canvasViewSettings, @@ -104,6 +111,7 @@ export const CanvasViewSettingsProvider: React.FC = props => { canvasViewSettings, setScrollPosition, setCanvasSize, + setCanvasViewSize, setFilename, zoomIn, zoomOut, diff --git a/src/pods/canvas/canvas.pod.tsx b/src/pods/canvas/canvas.pod.tsx index afcf0168..c60f7dc2 100644 --- a/src/pods/canvas/canvas.pod.tsx +++ b/src/pods/canvas/canvas.pod.tsx @@ -45,6 +45,7 @@ export const CanvasPod: React.FC = () => { } = useCanvasSchemaContext(); const { canvasViewSettings, + setCanvasViewSize, setScrollPosition, setLoadSample, setViewBoxSize, @@ -121,6 +122,34 @@ export const CanvasPod: React.FC = () => { const containerRef = React.useRef(null); + React.useEffect(() => { + if (containerRef.current) { + const container = containerRef.current; + + // TODO: Rename setOffSetZoomToCoords so that it does not only apply to coords + // maybe setOffsetZoom? + + const CANVAS_VIEW_SIZE_WITH_APPLIED_ZOOM_OFFSET = setOffSetZoomToCoords( + container.clientWidth + containerRef.current.scrollLeft * 2, + container.clientHeight + containerRef.current.scrollTop * 2, + viewBoxSize, + canvasSize, + zoomFactor + ); + + setCanvasViewSize({ + width: CANVAS_VIEW_SIZE_WITH_APPLIED_ZOOM_OFFSET.x, + height: CANVAS_VIEW_SIZE_WITH_APPLIED_ZOOM_OFFSET.y, + }); + } + }, [ + containerRef.current?.clientWidth, + containerRef.current?.scrollLeft, + containerRef.current?.clientHeight, + containerRef.current?.scrollTop, + viewBoxSize, + ]); + const handleScroll = () => { if (containerRef.current) { setScrollPosition( diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index c64c43ad..68b306cc 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -10,19 +10,68 @@ import { } from '@/core/providers/canvas-schema'; import { ADD_COLLECTION_TITLE } from '@/common/components/modal-dialog'; import { SHORTCUTS } from '../../shortcut/shortcut.const'; - -const BORDER_MARGIN = 40; +import { findFreePositionOrMinCollision } from '@/common/autoarrange-table'; +import { setOffSetZoomToCoords } from '@/common/helpers/set-off-set-zoom-to-coords.helper'; +import { getTableSize } from './add-collection.helper'; +import { mapTableVMtoBoxVMMapper } from './add-collection.mapper'; export const AddCollection = () => { const { openModal, closeModal } = useModalDialogContext(); const { canvasSchema, addTable } = useCanvasSchemaContext(); const { canvasViewSettings, setLoadSample } = useCanvasViewSettingsContext(); + const { canvasSize, zoomFactor, viewBoxSize } = canvasViewSettings; const handleAddTable = (newTable: TableVm) => { - const updatedTable = { + if (!newTable) { + return; + } + + const tableSize = setOffSetZoomToCoords( + getTableSize(newTable.fields).width, + getTableSize(newTable.fields).height, + viewBoxSize, + canvasSize, + zoomFactor + ); + + const getTableSizeOffSetDependingAtZoom = () => { + return { + width: tableSize.x / getTableSize(newTable.fields).width, + height: tableSize.y / getTableSize(newTable.fields).height, + }; + }; + + const position = findFreePositionOrMinCollision( + mapTableVMtoBoxVMMapper(canvasSchema.tables), + { + width: + getTableSize(newTable.fields).width / + getTableSizeOffSetDependingAtZoom().width, + height: + getTableSize(newTable.fields).height / + getTableSizeOffSetDependingAtZoom().height, + }, + { + width: canvasViewSettings.canvasViewSize.width, + height: canvasViewSettings.canvasViewSize.height, + } + ); + + console.log( + getTableSize(newTable.fields).width / + getTableSizeOffSetDependingAtZoom().width, + getTableSize(newTable.fields).height / + getTableSizeOffSetDependingAtZoom().height + ); + + if (!position) { + return; + } + + const updatedTable: TableVm = { ...newTable, - x: canvasViewSettings.scrollPosition.x + BORDER_MARGIN, - y: canvasViewSettings.scrollPosition.y + BORDER_MARGIN, + x: position.x - getTableSize(newTable.fields).width / 2, + y: position.y - getTableSize(newTable.fields).height / 2, }; addTable(updatedTable); @@ -40,9 +89,11 @@ export const AddCollection = () => { ADD_COLLECTION_TITLE ); }; + const handleCloseModal = () => { closeModal(); }; + return ( } diff --git a/src/pods/toolbar/components/add-collection/add-collection.helper.tsx b/src/pods/toolbar/components/add-collection/add-collection.helper.tsx new file mode 100644 index 00000000..135323c2 --- /dev/null +++ b/src/pods/toolbar/components/add-collection/add-collection.helper.tsx @@ -0,0 +1,18 @@ +import { Size } from '@/core/model'; +import { FieldVm, TABLE_CONST } from '@/core/providers'; + +export const getTableSize = (fields: FieldVm[]): Size => { + const rowHeight = TABLE_CONST.ROW_HEIGHT; + const headerHeight = TABLE_CONST.HEADER_HEIGHT; + const headerTitleGap = TABLE_CONST.HEADER_TITLE_GAP; + const fieldCount = fields.length; + + const totalHeight = + headerHeight + headerTitleGap + fieldCount * rowHeight + 10; + const totalWidth = TABLE_CONST.DEFAULT_TABLE_WIDTH; + + return { + width: totalWidth, + height: totalHeight, + }; +}; diff --git a/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx b/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx new file mode 100644 index 00000000..09bf7f81 --- /dev/null +++ b/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx @@ -0,0 +1,17 @@ +import { TableVm, TABLE_CONST } from '@/core/providers'; +import { getTableSize } from './add-collection.helper'; +import { Box } from '@/common/autoarrange-table/autoarrange-table.model'; + +const tableVMtoBoxVMMapper = (table: TableVm): Box => { + const tableSize = getTableSize(table.fields); + + return { + x: table.x + getTableSize(table.fields).width / 2, + y: table.y + getTableSize(table.fields).height / 2, + width: TABLE_CONST.TABLE_WIDTH, + height: tableSize.height, + }; +}; + +export const mapTableVMtoBoxVMMapper = (tables: TableVm[]): Box[] => + tables.map(tableVMtoBoxVMMapper); From 9acc74424be9d4dcfc33b5e98695457eb6040781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ruiz?= Date: Tue, 9 Jul 2024 14:28:28 +0200 Subject: [PATCH 2/9] Removed useless console.log --- .../components/add-collection/add-collection.component.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index 68b306cc..71a3063c 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -57,13 +57,6 @@ export const AddCollection = () => { } ); - console.log( - getTableSize(newTable.fields).width / - getTableSizeOffSetDependingAtZoom().width, - getTableSize(newTable.fields).height / - getTableSizeOffSetDependingAtZoom().height - ); - if (!position) { return; } From 929835b9d83615d876608454b3d8e4bf65f03a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ruiz?= Date: Tue, 9 Jul 2024 15:16:53 +0200 Subject: [PATCH 3/9] Removing useless function and directly applying the size of the table with the zoom setting at findFreePositionOrMinCollision --- .../add-collection.component.tsx | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index 71a3063c..3feaa89f 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -26,6 +26,9 @@ export const AddCollection = () => { return; } + // Applying a zoom offSet to the table size, + // so that when zoomed the size of the tables is updated + const tableSize = setOffSetZoomToCoords( getTableSize(newTable.fields).width, getTableSize(newTable.fields).height, @@ -34,22 +37,11 @@ export const AddCollection = () => { zoomFactor ); - const getTableSizeOffSetDependingAtZoom = () => { - return { - width: tableSize.x / getTableSize(newTable.fields).width, - height: tableSize.y / getTableSize(newTable.fields).height, - }; - }; - const position = findFreePositionOrMinCollision( mapTableVMtoBoxVMMapper(canvasSchema.tables), { - width: - getTableSize(newTable.fields).width / - getTableSizeOffSetDependingAtZoom().width, - height: - getTableSize(newTable.fields).height / - getTableSizeOffSetDependingAtZoom().height, + width: tableSize.x, + height: tableSize.y, }, { width: canvasViewSettings.canvasViewSize.width, From 761149f9b9bb6765f883c26c333d7cabe88f40ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ruiz?= Date: Tue, 9 Jul 2024 15:22:35 +0200 Subject: [PATCH 4/9] Added comments to facilitate the understanding of the code --- .../components/add-collection/add-collection.component.tsx | 4 ++++ .../components/add-collection/add-collection.mapper.tsx | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index 3feaa89f..6517a098 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -37,6 +37,8 @@ export const AddCollection = () => { zoomFactor ); + // Obtaining free position or with minimum collision for the new table + const position = findFreePositionOrMinCollision( mapTableVMtoBoxVMMapper(canvasSchema.tables), { @@ -53,6 +55,8 @@ export const AddCollection = () => { return; } + // Subtracting the size of the board by two to place the board from the middle + const updatedTable: TableVm = { ...newTable, x: position.x - getTableSize(newTable.fields).width / 2, diff --git a/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx b/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx index 09bf7f81..5f205aa4 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx @@ -5,6 +5,9 @@ import { Box } from '@/common/autoarrange-table/autoarrange-table.model'; const tableVMtoBoxVMMapper = (table: TableVm): Box => { const tableSize = getTableSize(table.fields); + // Adding the table center adjustment so that it only affects the table center + // when it is added and not to collisions + return { x: table.x + getTableSize(table.fields).width / 2, y: table.y + getTableSize(table.fields).height / 2, From b691799624973cd42ac5ce88420b9f3b6152577d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ruiz?= Date: Tue, 9 Jul 2024 16:44:54 +0200 Subject: [PATCH 5/9] Added gap between tables in the placement --- .../add-collection.component.tsx | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index 6517a098..46e8be16 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -15,6 +15,8 @@ import { setOffSetZoomToCoords } from '@/common/helpers/set-off-set-zoom-to-coor import { getTableSize } from './add-collection.helper'; import { mapTableVMtoBoxVMMapper } from './add-collection.mapper'; +const TABLE_GAP = 40; + export const AddCollection = () => { const { openModal, closeModal } = useModalDialogContext(); const { canvasSchema, addTable } = useCanvasSchemaContext(); @@ -29,9 +31,9 @@ export const AddCollection = () => { // Applying a zoom offSet to the table size, // so that when zoomed the size of the tables is updated - const tableSize = setOffSetZoomToCoords( - getTableSize(newTable.fields).width, - getTableSize(newTable.fields).height, + const tableSizeWithZoomOffSet = setOffSetZoomToCoords( + getTableSize(newTable.fields).width + TABLE_GAP, + getTableSize(newTable.fields).height + TABLE_GAP, viewBoxSize, canvasSize, zoomFactor @@ -42,8 +44,8 @@ export const AddCollection = () => { const position = findFreePositionOrMinCollision( mapTableVMtoBoxVMMapper(canvasSchema.tables), { - width: tableSize.x, - height: tableSize.y, + width: tableSizeWithZoomOffSet.x, + height: tableSizeWithZoomOffSet.y, }, { width: canvasViewSettings.canvasViewSize.width, @@ -55,12 +57,22 @@ export const AddCollection = () => { return; } - // Subtracting the size of the board by two to place the board from the middle + // Adding zoom offset to position coords and + // subtracting the size of the board by two + // to place the board from the middle + + const positionWithZoomOffSet = setOffSetZoomToCoords( + position.x - getTableSize(newTable.fields).width / 2, + position.y - getTableSize(newTable.fields).height / 2, + viewBoxSize, + canvasSize, + zoomFactor + ); const updatedTable: TableVm = { ...newTable, - x: position.x - getTableSize(newTable.fields).width / 2, - y: position.y - getTableSize(newTable.fields).height / 2, + x: positionWithZoomOffSet.x, + y: positionWithZoomOffSet.y, }; addTable(updatedTable); From c8682d75e0e1c41d01cef0de382be16526288313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ruiz?= Date: Tue, 9 Jul 2024 16:45:29 +0200 Subject: [PATCH 6/9] Added animation when creating a table --- .../table/database-table.component.tsx | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/pods/canvas/components/table/database-table.component.tsx b/src/pods/canvas/components/table/database-table.component.tsx index f486e995..a24fd208 100644 --- a/src/pods/canvas/components/table/database-table.component.tsx +++ b/src/pods/canvas/components/table/database-table.component.tsx @@ -11,6 +11,7 @@ import { } from './components'; import { renderRows } from './database-table-render-rows.helper'; import classes from './database-table.module.css'; +import { motion } from 'framer-motion'; // TODO: We should add an optional field to indicate FONT_SIZE in case we override the standard class // TODO: There's is a solution more elaborated (using JS) to show elipsis ... if text is too long @@ -82,22 +83,29 @@ export const DatabaseTable: React.FC = ({ }; return ( - | undefined} - > - - - + + | undefined} + initial={{ opacity: 0, scale: 0.8 }} + animate={{ opacity: 1, scale: 1 }} + transition={{ duration: 0.3 }} + > + + + + ); }; From b3ec404e4010f49d49b10d87d8a57f35d728f415 Mon Sep 17 00:00:00 2001 From: Ivanruii Date: Mon, 29 Jul 2024 13:08:07 -0400 Subject: [PATCH 7/9] Fixed getTableSize --- .../add-collection.component.tsx | 37 ++----------------- .../add-collection/add-collection.helper.tsx | 14 ++++++- .../add-collection/add-collection.mapper.tsx | 7 +--- 3 files changed, 18 insertions(+), 40 deletions(-) diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index 46e8be16..ae9c7638 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -11,41 +11,24 @@ import { import { ADD_COLLECTION_TITLE } from '@/common/components/modal-dialog'; import { SHORTCUTS } from '../../shortcut/shortcut.const'; import { findFreePositionOrMinCollision } from '@/common/autoarrange-table'; -import { setOffSetZoomToCoords } from '@/common/helpers/set-off-set-zoom-to-coords.helper'; import { getTableSize } from './add-collection.helper'; import { mapTableVMtoBoxVMMapper } from './add-collection.mapper'; -const TABLE_GAP = 40; - export const AddCollection = () => { const { openModal, closeModal } = useModalDialogContext(); const { canvasSchema, addTable } = useCanvasSchemaContext(); const { canvasViewSettings, setLoadSample } = useCanvasViewSettingsContext(); - const { canvasSize, zoomFactor, viewBoxSize } = canvasViewSettings; const handleAddTable = (newTable: TableVm) => { if (!newTable) { return; } - // Applying a zoom offSet to the table size, - // so that when zoomed the size of the tables is updated - - const tableSizeWithZoomOffSet = setOffSetZoomToCoords( - getTableSize(newTable.fields).width + TABLE_GAP, - getTableSize(newTable.fields).height + TABLE_GAP, - viewBoxSize, - canvasSize, - zoomFactor - ); - - // Obtaining free position or with minimum collision for the new table - const position = findFreePositionOrMinCollision( mapTableVMtoBoxVMMapper(canvasSchema.tables), { - width: tableSizeWithZoomOffSet.x, - height: tableSizeWithZoomOffSet.y, + width: getTableSize(newTable.fields).width, + height: getTableSize(newTable.fields).height, }, { width: canvasViewSettings.canvasViewSize.width, @@ -57,22 +40,10 @@ export const AddCollection = () => { return; } - // Adding zoom offset to position coords and - // subtracting the size of the board by two - // to place the board from the middle - - const positionWithZoomOffSet = setOffSetZoomToCoords( - position.x - getTableSize(newTable.fields).width / 2, - position.y - getTableSize(newTable.fields).height / 2, - viewBoxSize, - canvasSize, - zoomFactor - ); - const updatedTable: TableVm = { ...newTable, - x: positionWithZoomOffSet.x, - y: positionWithZoomOffSet.y, + x: position.x, + y: position.y, }; addTable(updatedTable); diff --git a/src/pods/toolbar/components/add-collection/add-collection.helper.tsx b/src/pods/toolbar/components/add-collection/add-collection.helper.tsx index 135323c2..588d62c5 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.helper.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.helper.tsx @@ -1,14 +1,24 @@ import { Size } from '@/core/model'; import { FieldVm, TABLE_CONST } from '@/core/providers'; +export const getFieldsCount = (fields: FieldVm[]): number => + fields.reduce((acc, field) => { + if (field.children && field.children.length > 0 && !field.isCollapsed) { + return acc + 1 + getFieldsCount(field.children); + } + return acc + 1; + }, 0); + export const getTableSize = (fields: FieldVm[]): Size => { const rowHeight = TABLE_CONST.ROW_HEIGHT; const headerHeight = TABLE_CONST.HEADER_HEIGHT; const headerTitleGap = TABLE_CONST.HEADER_TITLE_GAP; - const fieldCount = fields.length; + const bottomPadding = TABLE_CONST.ROW_PADDING; + + const fieldCount = getFieldsCount(fields); const totalHeight = - headerHeight + headerTitleGap + fieldCount * rowHeight + 10; + headerHeight + headerTitleGap + fieldCount * rowHeight + bottomPadding; const totalWidth = TABLE_CONST.DEFAULT_TABLE_WIDTH; return { diff --git a/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx b/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx index 5f205aa4..f525ccc9 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.mapper.tsx @@ -5,12 +5,9 @@ import { Box } from '@/common/autoarrange-table/autoarrange-table.model'; const tableVMtoBoxVMMapper = (table: TableVm): Box => { const tableSize = getTableSize(table.fields); - // Adding the table center adjustment so that it only affects the table center - // when it is added and not to collisions - return { - x: table.x + getTableSize(table.fields).width / 2, - y: table.y + getTableSize(table.fields).height / 2, + x: table.x, + y: table.y, width: TABLE_CONST.TABLE_WIDTH, height: tableSize.height, }; From 77b5341964eb327974a49b043243304cf810721e Mon Sep 17 00:00:00 2001 From: Ivanruii Date: Mon, 29 Jul 2024 13:18:56 -0400 Subject: [PATCH 8/9] Added table gap --- .../components/add-collection/add-collection.component.tsx | 3 ++- .../components/add-collection/add-collection.helper.tsx | 7 ++++--- .../components/add-collection/add-collection.model.ts | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 src/pods/toolbar/components/add-collection/add-collection.model.ts diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index ae9c7638..eb870072 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -13,6 +13,7 @@ import { SHORTCUTS } from '../../shortcut/shortcut.const'; import { findFreePositionOrMinCollision } from '@/common/autoarrange-table'; import { getTableSize } from './add-collection.helper'; import { mapTableVMtoBoxVMMapper } from './add-collection.mapper'; +import { TABLE_GAP } from './add-collection.model'; export const AddCollection = () => { const { openModal, closeModal } = useModalDialogContext(); @@ -42,7 +43,7 @@ export const AddCollection = () => { const updatedTable: TableVm = { ...newTable, - x: position.x, + x: position.x + TABLE_GAP, y: position.y, }; diff --git a/src/pods/toolbar/components/add-collection/add-collection.helper.tsx b/src/pods/toolbar/components/add-collection/add-collection.helper.tsx index 588d62c5..13a156f7 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.helper.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.helper.tsx @@ -1,5 +1,6 @@ import { Size } from '@/core/model'; import { FieldVm, TABLE_CONST } from '@/core/providers'; +import { TABLE_GAP } from './add-collection.model'; export const getFieldsCount = (fields: FieldVm[]): number => fields.reduce((acc, field) => { @@ -11,15 +12,15 @@ export const getFieldsCount = (fields: FieldVm[]): number => export const getTableSize = (fields: FieldVm[]): Size => { const rowHeight = TABLE_CONST.ROW_HEIGHT; - const headerHeight = TABLE_CONST.HEADER_HEIGHT; + const headerHeight = TABLE_CONST.HEADER_HEIGHT + TABLE_GAP; const headerTitleGap = TABLE_CONST.HEADER_TITLE_GAP; - const bottomPadding = TABLE_CONST.ROW_PADDING; + const bottomPadding = TABLE_CONST.ROW_PADDING + TABLE_GAP; const fieldCount = getFieldsCount(fields); const totalHeight = headerHeight + headerTitleGap + fieldCount * rowHeight + bottomPadding; - const totalWidth = TABLE_CONST.DEFAULT_TABLE_WIDTH; + const totalWidth = TABLE_CONST.DEFAULT_TABLE_WIDTH + TABLE_GAP * 2; return { width: totalWidth, diff --git a/src/pods/toolbar/components/add-collection/add-collection.model.ts b/src/pods/toolbar/components/add-collection/add-collection.model.ts new file mode 100644 index 00000000..838d6bac --- /dev/null +++ b/src/pods/toolbar/components/add-collection/add-collection.model.ts @@ -0,0 +1 @@ +export const TABLE_GAP = 20; From b5c5a08acf5b1462dd37f780d5773c370621b9bb Mon Sep 17 00:00:00 2001 From: Ivanruii Date: Wed, 31 Jul 2024 12:48:53 -0400 Subject: [PATCH 9/9] Added gap and animation ajusments --- .../canvas/components/table/database-table.component.tsx | 7 +++++-- .../components/add-collection/add-collection.model.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pods/canvas/components/table/database-table.component.tsx b/src/pods/canvas/components/table/database-table.component.tsx index a24fd208..ecaa0167 100644 --- a/src/pods/canvas/components/table/database-table.component.tsx +++ b/src/pods/canvas/components/table/database-table.component.tsx @@ -90,8 +90,11 @@ export const DatabaseTable: React.FC = ({ className={classes.tableContainer} ref={ref as React.Ref | undefined} initial={{ opacity: 0, scale: 0.8 }} - animate={{ opacity: 1, scale: 1 }} - transition={{ duration: 0.3 }} + animate={{ opacity: [0, 1, 0, 1], scale: 1 }} + transition={{ + opacity: { duration: 2 }, + scale: { duration: 0.8 }, + }} >