From d4b34e095df59b330fe4664adb3a081670c3f03d Mon Sep 17 00:00:00 2001 From: Dominik Zborowski Date: Fri, 18 Oct 2024 11:36:18 +0200 Subject: [PATCH 1/9] Grid columns config --- components/ModalTemplateAddEditGrid.vue | 55 ++- domains/grid/components/AddWidgetBasic.vue | 9 +- .../components/AddWidgetGenericPlatform.vue | 9 +- domains/grid/components/AddWidgetText.vue | 9 +- domains/grid/components/GridTabs.vue | 3 +- domains/grid/components/GridTabsItem.vue | 4 +- domains/grid/components/GridView.vue | 60 +-- domains/grid/components/GridWidget.vue | 29 +- domains/grid/components/GridWidgetImage.vue | 29 +- domains/grid/composables/useGrid.ts | 29 +- domains/grid/shared/config.ts | 15 +- .../grid/utils/__tests__/buildLayout.spec.ts | 391 ++++++++++++------ .../grid/utils/__tests__/compareGrids.spec.ts | 26 +- .../utils/__tests__/configToLayout.spec.ts | 6 + .../grid/utils/__tests__/createGridId.spec.ts | 10 +- .../utils/__tests__/layoutToConfig.spec.ts | 3 +- domains/grid/utils/buildLayout.ts | 25 +- domains/grid/utils/compareGrids.ts | 7 +- domains/grid/utils/configToLayout.ts | 15 +- domains/grid/utils/defaultGridConfig.ts | 1 + domains/grid/utils/layoutToConfig.ts | 1 + package.json | 2 +- stores/app.ts | 8 +- stores/grid.ts | 5 - translations/en_US.json | 4 +- types/grid.ts | 1 + yarn.lock | 10 +- 27 files changed, 535 insertions(+), 231 deletions(-) diff --git a/components/ModalTemplateAddEditGrid.vue b/components/ModalTemplateAddEditGrid.vue index c8a35fa2..d2ef02cc 100644 --- a/components/ModalTemplateAddEditGrid.vue +++ b/components/ModalTemplateAddEditGrid.vue @@ -1,5 +1,6 @@ diff --git a/domains/grid/composables/useGrid.ts b/domains/grid/composables/useGrid.ts index 1d637028..3dbfd7e0 100644 --- a/domains/grid/composables/useGrid.ts +++ b/domains/grid/composables/useGrid.ts @@ -5,16 +5,15 @@ export const useGrid = () => { isConnected, connectedProfileAddress, isConnectedUserViewingOwnProfile, + isMobile, } = storeToRefs(useAppStore()) const { isEditingGrid, hasUnsavedGrid, viewedGridLayout, tempGridLayout, - gridColumns, isSavingGrid, selectedLayoutId, - gridColumnsLarge, } = storeToRefs(useGridStore()) const canEditGrid = computed( @@ -25,10 +24,10 @@ export const useGrid = () => { ) const getGridById = (grids: Grid[], id?: string) => - grids.find(grid => grid.id === id)?.grid || [] + grids.find(grid => grid.id === id) const getSelectedLayout = (layouts: Grid[]): GridWidget[] => - getGridById(layouts, selectedLayoutId.value) + getGridById(layouts, selectedLayoutId.value)?.grid || [] const updateSelectedLayout = (layout: GridWidget[]): Grid[] => { const updatedLayouts = tempGridLayout.value.map(item => { @@ -37,6 +36,7 @@ export const useGrid = () => { id: item.id, title: item.title, grid: layout, + gridColumns: item.gridColumns, } } @@ -84,7 +84,7 @@ export const useGrid = () => { const userLayout = await getUserLayout(address) layout = buildLayout( userLayout, - gridColumns.value, + isMobile.value, withAddContentPlaceholder ) @@ -110,7 +110,7 @@ export const useGrid = () => { if (canEditGrid.value) { layout = buildLayout( tempGridLayout.value, - gridColumns.value, + isMobile.value, withAddContentPlaceholder ) @@ -130,14 +130,19 @@ export const useGrid = () => { addGridLayoutItem: ( newItem: GridWidgetWithoutCords, - grid: GridWidget[] + grid?: Grid ) => { if (!canEditGrid.value) { console.warn('User cannot edit grid') return } - placeWidgetInLayout(newItem, grid, gridColumns.value) + if (!grid) { + console.warn('Grid is required') + return + } + + placeWidgetInLayout(newItem, grid.grid, grid.gridColumns) }, updateGridLayoutItem: (id?: string, widget?: Partial) => { @@ -211,7 +216,7 @@ export const useGrid = () => { // rebuild layout to ensure that all widgets are in the correct position const layout = buildLayout( tempGridLayout.value, - gridColumns.value, + isMobile.value, canEditGrid.value ) @@ -285,12 +290,6 @@ export const useGrid = () => { tempGridLayout.value = tempGridLayout.value.filter(item => item.id !== id) }, - - getGridColumns: computed(() => (width: number): number => { - return width > GRID_BREAKPOINT_PX - ? gridColumnsLarge.value - : DEFAULT_SMALL_COLUMN_NUMBER - }), getSelectedLayout, updateSelectedLayout, canEditGrid, diff --git a/domains/grid/shared/config.ts b/domains/grid/shared/config.ts index 2f7e5519..961aae53 100644 --- a/domains/grid/shared/config.ts +++ b/domains/grid/shared/config.ts @@ -68,13 +68,7 @@ export const WIDGET_TYPE_PROPERTIES: Record< [GRID_WIDGET_TYPE.WARPCAST]: [{ key: 'src', type: 'url' }], } -// default number of columns in "large" mode -export const DEFAULT_LARGE_COLUMN_NUMBER = 2 - -// default number of columns in "small" mode -export const DEFAULT_SMALL_COLUMN_NUMBER = 1 - -// grid breakpoint where the grid switches from "large" to "small" mode +// grid breakpoint where the grid switches into mobile mode export const GRID_BREAKPOINT_PX = 768 // default title for the grid, this usually is the title of the first grid @@ -92,3 +86,10 @@ export const DEFAULT_GRID_ROW_HEIGHT_RATIO = 0.75 // maximum rotation angle in degrees for a widget while moving export const MAX_WIDGET_ROTATE_WHILE_MOVE_DEG = 20 + +// default number of columns in "small" mode +export const DEFAULT_SMALL_COLUMN_NUMBER = 1 + +// minimum and maximum number of columns in the grid +export const GRID_COLUMNS_MIN = 2 +export const GRID_COLUMNS_MAX = 4 diff --git a/domains/grid/utils/__tests__/buildLayout.spec.ts b/domains/grid/utils/__tests__/buildLayout.spec.ts index 3bea1746..e2cf87fa 100644 --- a/domains/grid/utils/__tests__/buildLayout.spec.ts +++ b/domains/grid/utils/__tests__/buildLayout.spec.ts @@ -5,7 +5,7 @@ import { buildLayout } from '../buildLayout' describe('buildLayout', () => { it('should return an empty layout when given an empty layout', () => { const layout: Grid[] = [] - const result = buildLayout(layout, 1) + const result = buildLayout(layout) expect(result).toEqual([]) }) @@ -43,9 +43,10 @@ describe('buildLayout', () => { y: 1, }, ], + gridColumns: 1, }, ] - const result = buildLayout(layout, 1) + const result = buildLayout(layout) expect(result).toEqual([ { id: '1', @@ -79,6 +80,7 @@ describe('buildLayout', () => { y: 2, }, ], + gridColumns: 1, }, ]) }) @@ -109,9 +111,10 @@ describe('buildLayout', () => { properties: {}, }, ], + gridColumns: 1, }, ] - const result = buildLayout(layout, 1) + const result = buildLayout(layout) expect(result).toEqual([ { id: '1', @@ -127,6 +130,7 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 1, }, ]) }) @@ -147,9 +151,10 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 1, }, ] - const result = buildLayout(layout, 1, true) + const result = buildLayout(layout, undefined, true) expect(result).toEqual([ { id: '1', @@ -175,49 +180,13 @@ describe('buildLayout', () => { isResizable: false, }, ], + gridColumns: 1, }, ]) }) }) describe('layout 1', () => { - const layout: Grid[] = [ - { - id: '1', - title: 'Test Grid', - grid: [ - { - i: '1', - type: GRID_WIDGET_TYPE.TITLE_LINK, - w: 1, - h: 2, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '2', - type: GRID_WIDGET_TYPE.TEXT, - w: 1, - h: 1, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '3', - type: GRID_WIDGET_TYPE.IFRAME, - w: 1, - h: 2, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '4', - type: GRID_WIDGET_TYPE.IMAGE, - w: 1, - h: 1, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - ], - }, - ] - it('should build 1 column layout', () => { /* Column 0 @@ -229,7 +198,45 @@ describe('buildLayout', () => { | | | D | <-- height 1 */ - expect(buildLayout(layout, 1)).toEqual([ + const layout: Grid[] = [ + { + id: '1', + title: 'Test Grid', + grid: [ + { + i: '1', + type: GRID_WIDGET_TYPE.TITLE_LINK, + w: 1, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '2', + type: GRID_WIDGET_TYPE.TEXT, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '3', + type: GRID_WIDGET_TYPE.IFRAME, + w: 1, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '4', + type: GRID_WIDGET_TYPE.IMAGE, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + ], + gridColumns: 1, + }, + ] + + expect(buildLayout(layout)).toEqual([ { id: '1', title: 'Test Grid', @@ -271,6 +278,7 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 1, }, ]) }) @@ -284,7 +292,45 @@ describe('buildLayout', () => { |-------| | C | <-- C: height 2 | D | | | <-- D: height 1 */ - expect(buildLayout(layout, 2)).toEqual([ + const layout: Grid[] = [ + { + id: '1', + title: 'Test Grid', + grid: [ + { + i: '1', + type: GRID_WIDGET_TYPE.TITLE_LINK, + w: 1, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '2', + type: GRID_WIDGET_TYPE.TEXT, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '3', + type: GRID_WIDGET_TYPE.IFRAME, + w: 1, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '4', + type: GRID_WIDGET_TYPE.IMAGE, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + ], + gridColumns: 2, + }, + ] + + expect(buildLayout(layout)).toEqual([ { id: '1', title: 'Test Grid', @@ -326,6 +372,7 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 2, }, ]) }) @@ -338,7 +385,45 @@ describe('buildLayout', () => { | | |-------| | | |-------| | D | |-------| */ - expect(buildLayout(layout, 3)).toEqual([ + const layout: Grid[] = [ + { + id: '1', + title: 'Test Grid', + grid: [ + { + i: '1', + type: GRID_WIDGET_TYPE.TITLE_LINK, + w: 1, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '2', + type: GRID_WIDGET_TYPE.TEXT, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '3', + type: GRID_WIDGET_TYPE.IFRAME, + w: 1, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '4', + type: GRID_WIDGET_TYPE.IMAGE, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + ], + gridColumns: 3, + }, + ] + + expect(buildLayout(layout)).toEqual([ { id: '1', title: 'Test Grid', @@ -380,42 +465,13 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 3, }, ]) }) }) describe('layout 2', () => { - const layout: Grid[] = [ - { - id: '1', - title: 'Test Grid', - grid: [ - { - i: '1', - type: GRID_WIDGET_TYPE.IMAGE, - w: 2, - h: 2, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '2', - type: GRID_WIDGET_TYPE.TEXT, - w: 1, - h: 1, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '3', - type: GRID_WIDGET_TYPE.IMAGE, - w: 1, - h: 1, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - ], - }, - ] - it('should build 1 column layout', () => { /* Column 0 @@ -427,7 +483,38 @@ describe('buildLayout', () => { |-------| | C | <-- height 1 */ - expect(buildLayout(layout, 1)).toEqual([ + const layout: Grid[] = [ + { + id: '1', + title: 'Test Grid', + grid: [ + { + i: '1', + type: GRID_WIDGET_TYPE.IMAGE, + w: 2, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '2', + type: GRID_WIDGET_TYPE.TEXT, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '3', + type: GRID_WIDGET_TYPE.IMAGE, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + ], + gridColumns: 1, + }, + ] + + expect(buildLayout(layout)).toEqual([ { id: '1', title: 'Test Grid', @@ -461,6 +548,7 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 1, }, ]) }) @@ -473,7 +561,38 @@ describe('buildLayout', () => { |-------| |-------| | B | | C | <-- height 1 each */ - expect(buildLayout(layout, 2)).toEqual([ + const layout: Grid[] = [ + { + id: '1', + title: 'Test Grid', + grid: [ + { + i: '1', + type: GRID_WIDGET_TYPE.IMAGE, + w: 2, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '2', + type: GRID_WIDGET_TYPE.TEXT, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '3', + type: GRID_WIDGET_TYPE.IMAGE, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + ], + gridColumns: 2, + }, + ] + + expect(buildLayout(layout)).toEqual([ { id: '1', title: 'Test Grid', @@ -506,49 +625,13 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 2, }, ]) }) }) describe('layout 3', () => { - const layout: Grid[] = [ - { - id: '1', - title: 'Test Grid', - grid: [ - { - i: '1', - type: GRID_WIDGET_TYPE.IMAGE, - w: 1, - h: 1, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '2', - type: GRID_WIDGET_TYPE.IFRAME, - w: 2, - h: 2, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '3', - type: GRID_WIDGET_TYPE.TEXT, - w: 1, - h: 1, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - { - i: '4', - type: GRID_WIDGET_TYPE.IMAGE, - w: 2, - h: 1, - properties: { prop1: 'value1', prop2: 'value2' }, - }, - ], - }, - ] - it('should build 1 column layout', () => { /* Column 0 @@ -562,7 +645,45 @@ describe('buildLayout', () => { |-------| | D | <-- height 1 */ - expect(buildLayout(layout, 1)).toEqual([ + const layout: Grid[] = [ + { + id: '1', + title: 'Test Grid', + grid: [ + { + i: '1', + type: GRID_WIDGET_TYPE.IMAGE, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '2', + type: GRID_WIDGET_TYPE.IFRAME, + w: 2, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '3', + type: GRID_WIDGET_TYPE.TEXT, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '4', + type: GRID_WIDGET_TYPE.IMAGE, + w: 2, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + ], + gridColumns: 1, + }, + ] + + expect(buildLayout(layout)).toEqual([ { id: '1', title: 'Test Grid', @@ -606,6 +727,7 @@ describe('buildLayout', () => { originalWidth: 2, }, ], + gridColumns: 1, }, ]) }) @@ -622,7 +744,45 @@ describe('buildLayout', () => { |-------|-------------| | D | D | <-- height 1 (width spans 2 columns) */ - expect(buildLayout(layout, 2)).toEqual([ + const layout: Grid[] = [ + { + id: '1', + title: 'Test Grid', + grid: [ + { + i: '1', + type: GRID_WIDGET_TYPE.IMAGE, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '2', + type: GRID_WIDGET_TYPE.IFRAME, + w: 2, + h: 2, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '3', + type: GRID_WIDGET_TYPE.TEXT, + w: 1, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + { + i: '4', + type: GRID_WIDGET_TYPE.IMAGE, + w: 2, + h: 1, + properties: { prop1: 'value1', prop2: 'value2' }, + }, + ], + gridColumns: 2, + }, + ] + + expect(buildLayout(layout)).toEqual([ { id: '1', title: 'Test Grid', @@ -664,6 +824,7 @@ describe('buildLayout', () => { properties: { prop1: 'value1', prop2: 'value2' }, }, ], + gridColumns: 2, }, ]) }) diff --git a/domains/grid/utils/__tests__/compareGrids.spec.ts b/domains/grid/utils/__tests__/compareGrids.spec.ts index 5af5385d..92e96785 100644 --- a/domains/grid/utils/__tests__/compareGrids.spec.ts +++ b/domains/grid/utils/__tests__/compareGrids.spec.ts @@ -18,6 +18,7 @@ describe('compareGrids', () => { properties: {}, }, ], + gridColumns: 2, }, ] const gridB: Grid[] = [ @@ -35,6 +36,7 @@ describe('compareGrids', () => { properties: {}, }, ], + gridColumns: 2, }, ] @@ -44,10 +46,27 @@ describe('compareGrids', () => { it('should return changes for grids with different titles', () => { const gridA: Grid[] = [ - { id: 'grid-1', title: 'Grid 1', grid: [] }, + { id: 'grid-1', title: 'Grid 1', grid: [], gridColumns: 2 }, ] const gridB: Grid[] = [ - { id: 'grid-2', title: 'Grid 2', grid: [] }, + { id: 'grid-2', title: 'Grid 2', grid: [], gridColumns: 2 }, + ] + + const result = compareGrids(gridA, gridB) + expect(result).toEqual([ + { + oldGrid: gridA[0], + newGrid: gridB[0], + }, + ]) + }) + + it('should return changes for grids with different column numbers', () => { + const gridA: Grid[] = [ + { id: 'grid-1', title: 'Grid 1', grid: [], gridColumns: 2 }, + ] + const gridB: Grid[] = [ + { id: 'grid-1', title: 'Grid 1', grid: [], gridColumns: 3 }, ] const result = compareGrids(gridA, gridB) @@ -75,6 +94,7 @@ describe('compareGrids', () => { properties: {}, }, ], + gridColumns: 2, }, ] const gridB: Grid[] = [ @@ -92,6 +112,7 @@ describe('compareGrids', () => { properties: {}, }, ], + gridColumns: 2, }, ] @@ -121,6 +142,7 @@ describe('compareGrids', () => { properties: {}, }, ], + gridColumns: 2, }, ] diff --git a/domains/grid/utils/__tests__/configToLayout.spec.ts b/domains/grid/utils/__tests__/configToLayout.spec.ts index 0b7e5090..82c5546b 100644 --- a/domains/grid/utils/__tests__/configToLayout.spec.ts +++ b/domains/grid/utils/__tests__/configToLayout.spec.ts @@ -11,10 +11,12 @@ describe('configToLayout', () => { { title: 'default', grid: [], + gridColumns: 2, }, { title: 'default', grid: [], + gridColumns: 2, }, ] const result = [ @@ -22,11 +24,13 @@ describe('configToLayout', () => { id: 'default', title: 'default', grid: [], + gridColumns: 2, }, { id: 'default-0', title: 'default', grid: [], + gridColumns: 2, }, ] @@ -69,6 +73,7 @@ describe('configToLayout', () => { }, }, ], + gridColumns: 2, }, ] const result = [ @@ -106,6 +111,7 @@ describe('configToLayout', () => { i: 'test-id', }, ], + gridColumns: 2, }, ] diff --git a/domains/grid/utils/__tests__/createGridId.spec.ts b/domains/grid/utils/__tests__/createGridId.spec.ts index 78a117ca..8d6d1b5d 100644 --- a/domains/grid/utils/__tests__/createGridId.spec.ts +++ b/domains/grid/utils/__tests__/createGridId.spec.ts @@ -3,7 +3,7 @@ import { createGridId } from '../createGridId' describe('createGridId', () => { it('should generate a unique ID based on the title', () => { - const gridItem = { title: 'Test Title' } + const gridItem = { title: 'Test Title', gridColumns: 2 } const config: Grid[] = [] const id = createGridId(gridItem, config) @@ -11,9 +11,9 @@ describe('createGridId', () => { }) it('should append a number to the ID if the title is not unique', () => { - const gridItem = { title: 'Test Title' } + const gridItem = { title: 'Test Title', gridColumns: 2 } const config: Grid[] = [ - { id: 'test-title', title: 'Test Title', grid: [] }, + { id: 'test-title', title: 'Test Title', grid: [], gridColumns: 2 }, ] const id = createGridId(gridItem, config) @@ -21,7 +21,7 @@ describe('createGridId', () => { }) it('should handle an empty title', () => { - const gridItem = { title: '' } + const gridItem = { title: '', gridColumns: 2 } const config: Grid[] = [] const id = createGridId(gridItem, config) @@ -29,7 +29,7 @@ describe('createGridId', () => { }) it('should handle special characters in the title', () => { - const gridItem = { title: 'Test @ Title!' } + const gridItem = { title: 'Test @ Title!', gridColumns: 2 } const config: Grid[] = [] const id = createGridId(gridItem, config) diff --git a/domains/grid/utils/__tests__/layoutToConfig.spec.ts b/domains/grid/utils/__tests__/layoutToConfig.spec.ts index 44f26041..d9ff041f 100644 --- a/domains/grid/utils/__tests__/layoutToConfig.spec.ts +++ b/domains/grid/utils/__tests__/layoutToConfig.spec.ts @@ -48,12 +48,12 @@ describe('layoutToConfig', () => { y: 2, }, ], + gridColumns: 2, }, ] const result = [ { title: 'single', - grid: [ { type: 'IFRAME', @@ -82,6 +82,7 @@ describe('layoutToConfig', () => { }, }, ], + gridColumns: 2, }, ] diff --git a/domains/grid/utils/buildLayout.ts b/domains/grid/utils/buildLayout.ts index 622da163..fe170144 100644 --- a/domains/grid/utils/buildLayout.ts +++ b/domains/grid/utils/buildLayout.ts @@ -1,11 +1,12 @@ export const buildLayout = ( layout: Grid[] | Grid[], - gridColumns: number, + isMobile?: boolean, withAddContentPlaceholder?: boolean ): Grid[] => { const _buildLayout = ( layout: GridWidgetWithoutCords[], - updatedLayout: GridWidget[] + updatedLayout: GridWidget[], + gridColumns: number ) => { // remove "add widget" placeholder from layout let _layout = layout.filter( @@ -50,10 +51,28 @@ export const buildLayout = ( return layout.map(item => { const updatedLayout: GridWidget[] = [] + const gridColumns = getGridColumns(item.gridColumns) return { id: item.id, title: item.title, - grid: _buildLayout(item.grid, updatedLayout), + grid: _buildLayout( + item.grid, + updatedLayout, + isMobile ? DEFAULT_SMALL_COLUMN_NUMBER : gridColumns + ), + gridColumns, } }) } + +const getGridColumns = (gridColumns?: number) => { + if (!gridColumns) { + return GRID_COLUMNS_MIN + } + + if (gridColumns > GRID_COLUMNS_MAX) { + return GRID_COLUMNS_MAX + } + + return gridColumns +} diff --git a/domains/grid/utils/compareGrids.ts b/domains/grid/utils/compareGrids.ts index 7a5fbd4f..16a16859 100644 --- a/domains/grid/utils/compareGrids.ts +++ b/domains/grid/utils/compareGrids.ts @@ -18,7 +18,12 @@ export const compareGrids = ( // compare differences in title or grid array const comparedWidgets = compareLayouts(oldGrid?.grid, newGrid?.grid) - if (oldGrid?.title !== newGrid?.title || comparedWidgets.length > 0) { + + if ( + oldGrid?.title !== newGrid?.title || + comparedWidgets.length > 0 || + oldGrid?.gridColumns !== newGrid?.gridColumns + ) { result.push({ oldGrid: gridA[i], newGrid: gridB[i], diff --git a/domains/grid/utils/configToLayout.ts b/domains/grid/utils/configToLayout.ts index d41b51e1..9f7f43a6 100644 --- a/domains/grid/utils/configToLayout.ts +++ b/domains/grid/utils/configToLayout.ts @@ -5,7 +5,6 @@ import type { GridConfigItem } from '@/types/grid' * * @param config * @param columns - * @returns */ export const configToLayout = ( config: PartialBy, 'id'>[] @@ -24,8 +23,22 @@ export const configToLayout = ( h: widget.height, }) }), + gridColumns: getGridColumns(gridItem.gridColumns), }) } return layout } + +/** + * Get grid column number + * + * @param gridColumns + */ +const getGridColumns = (gridColumns?: number): number => { + if (!gridColumns) { + return GRID_COLUMNS_MIN + } + + return gridColumns +} diff --git a/domains/grid/utils/defaultGridConfig.ts b/domains/grid/utils/defaultGridConfig.ts index 35cd74aa..04f281db 100644 --- a/domains/grid/utils/defaultGridConfig.ts +++ b/domains/grid/utils/defaultGridConfig.ts @@ -34,6 +34,7 @@ export const defaultGridConfig = ( properties: { src: 'https://via.placeholder.com/150' }, }, ], + gridColumns: 2, }, ] } diff --git a/domains/grid/utils/layoutToConfig.ts b/domains/grid/utils/layoutToConfig.ts index 2d610c03..b3d87d42 100644 --- a/domains/grid/utils/layoutToConfig.ts +++ b/domains/grid/utils/layoutToConfig.ts @@ -36,6 +36,7 @@ export const layoutToConfig = ( return { title: item.title, grid: convertGrid(item.grid), + gridColumns: item.gridColumns, } }) } diff --git a/package.json b/package.json index e913fe55..48ecb09a 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@formatjs/intl": "^2.9.9", "@google/model-viewer": "^3.4.0", "@lukso/lsp-smart-contracts": "0.15.0-rc.5", - "@lukso/web-components": "1.90.0", + "@lukso/web-components": "1.91.0", "@nuxt/test-utils": "^3.13.1", "@nuxtjs/algolia": "^1.10.2", "@nuxtjs/device": "^3.1.1", diff --git a/stores/app.ts b/stores/app.ts index b841e7df..35e1b06d 100644 --- a/stores/app.ts +++ b/stores/app.ts @@ -1,3 +1,5 @@ +import { useWindowSize } from '@vueuse/core' + import type { Modal } from '@/types/modal' import type { NetworkId, NetworkInfo } from '@/types/network' import type EthereumProvider from '@walletconnect/ethereum-provider' @@ -81,7 +83,11 @@ export const useAppStore = defineStore( const isMobile = computed(() => { const { isMobile } = useDevice() - return isMobile + const { width } = useWindowSize() + const isSmallScreen = width.value < GRID_BREAKPOINT_PX + + // since device plugin check only User-Agent, we need to check also screen width + return isMobile || isSmallScreen }) const isMobileOrTablet = computed(() => { diff --git a/stores/grid.ts b/stores/grid.ts index 6513a63a..7660dc73 100644 --- a/stores/grid.ts +++ b/stores/grid.ts @@ -5,8 +5,6 @@ export const useGridStore = defineStore( const hasUnsavedGrid = ref(false) const viewedGridLayout = ref[]>([]) const tempGridLayout = ref[]>([]) - const gridColumns = ref(DEFAULT_LARGE_COLUMN_NUMBER) - const gridColumnsLarge = ref(DEFAULT_LARGE_COLUMN_NUMBER) const isSavingGrid = ref(false) const selectedLayoutId = ref() const gridRowHeightRatio = ref(DEFAULT_GRID_ROW_HEIGHT_RATIO) @@ -17,10 +15,8 @@ export const useGridStore = defineStore( hasUnsavedGrid, viewedGridLayout, tempGridLayout, - gridColumns, isSavingGrid, selectedLayoutId, - gridColumnsLarge, gridRowHeightRatio, gridChainId, } @@ -31,7 +27,6 @@ export const useGridStore = defineStore( 'isEditingGrid', 'hasUnsavedGrid', 'selectedLayoutId', - 'gridColumnsLarge', 'gridRowHeightRatio', 'gridChainId', 'tempGridLayout', diff --git a/translations/en_US.json b/translations/en_US.json index eaca794f..71f4fdc0 100644 --- a/translations/en_US.json +++ b/translations/en_US.json @@ -61,6 +61,7 @@ "error_no_accounts": "Account not found.", "asset_all_creators_unverified": "Un-verified creators", "add_widget_text_text_label": "Description", + "add_grid_columns_label": "Number of columns", "edit_widget_text_title": "Edit Text widget", "add_widget_description": "Embedding a link will show its content to any visitor of your profile.", "profile_tab_grid": "Grid", @@ -82,6 +83,7 @@ "dapp_showcase_01_title": "universal.page", "add_widget_spotify_title": "Add Spotify widget", "not_up_title": "Not a Universal Profile", + "add_grid_title_label": "Title", "add_widget_text_text_placeholder": "Description text", "dapp_showcase_08_url": "https://lsp8.app/", "no_asset_balance": "You don't own this asset.", @@ -237,7 +239,7 @@ "token_details_show_collection": "View collection", "send_card_return_to_profile": "Return to profile", "filters_order_by_lastly_added": "Lastly added", - "grid_tabs_menu_edit": "Rename", + "grid_tabs_menu_edit": "Edit", "widget_type_youtube": "Youtube", "edit_widget_iframe_title": "Edit Iframe widget", "filters_order_by_name_asc": "Title A-Z", diff --git a/types/grid.ts b/types/grid.ts index 9952110c..b349f327 100644 --- a/types/grid.ts +++ b/types/grid.ts @@ -4,6 +4,7 @@ export type Grid = { title: string grid: T[] id: string + gridColumns: number } export type GridConfigItem = { diff --git a/yarn.lock b/yarn.lock index 53e56f47..57d048e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4723,9 +4723,9 @@ __metadata: languageName: node linkType: hard -"@lukso/web-components@npm:1.90.0": - version: 1.90.0 - resolution: "@lukso/web-components@npm:1.90.0" +"@lukso/web-components@npm:1.91.0": + version: 1.91.0 + resolution: "@lukso/web-components@npm:1.91.0" dependencies: clsx: "npm:^2.0.0" ethereum-blockies-base64: "npm:^1.0.2" @@ -4733,7 +4733,7 @@ __metadata: tailwind-variants: "npm:^0.2.1" tippy.js: "npm:^6.3.7" web3-utils: "npm:^4.3.1" - checksum: 43835310dec14eb7156cee85d84d1ea1eeb02f7100921df8932a491e332a8feb4160551ecd22edddbd495938b8b681ca0a3554a81968e55987bf9143ce80d109 + checksum: 5d27750457228c6acaad3182aa81fb745fa3b297cf7a720e4d306346ee2156179b28c9cd6ab12e4aa72ac95160e9b66e976b5eb903cc57d05125494c504e04cd languageName: node linkType: hard @@ -24231,7 +24231,7 @@ __metadata: "@formatjs/intl": "npm:^2.9.9" "@google/model-viewer": "npm:^3.4.0" "@lukso/lsp-smart-contracts": "npm:0.15.0-rc.5" - "@lukso/web-components": "npm:1.90.0" + "@lukso/web-components": "npm:1.91.0" "@nuxt/test-utils": "npm:^3.13.1" "@nuxtjs/algolia": "npm:^1.10.2" "@nuxtjs/device": "npm:^3.1.1" From cc640cc5fb42590c447fb52482305074c8794dd6 Mon Sep 17 00:00:00 2001 From: Dominik Zborowski Date: Fri, 18 Oct 2024 16:23:49 +0200 Subject: [PATCH 2/9] Refactor grid --- app.vue | 4 +- components/ModalTemplateAddEditGrid.vue | 6 +- components/ModalTemplateDeleteGridWidget.vue | 4 +- components/ModalTemplateMoveGridWidget.vue | 22 ++- domains/grid/components/AddWidgetBasic.vue | 11 +- .../components/AddWidgetGenericPlatform.vue | 10 +- domains/grid/components/AddWidgetText.vue | 11 +- domains/grid/components/GridTabs.vue | 4 +- domains/grid/components/GridTabsItem.vue | 8 +- domains/grid/components/GridView.vue | 116 +++++++------- domains/grid/components/GridWidget.vue | 10 +- domains/grid/composables/useGrid.ts | 147 +++++++++--------- ...{buildLayout.spec.ts => buildGrid.spec.ts} | 74 ++++----- ...uts.spec.ts => compareGridWidgets.spec.ts} | 90 +++++------ ...configToLayout.spec.ts => configToGrid.ts} | 17 +- ...tToConfig.spec.ts => gridToConfig.spec.ts} | 18 +-- .../utils/{buildLayout.ts => buildGrid.ts} | 36 ++--- ...ompareLayouts.ts => compareGridWidgets.ts} | 26 ++-- domains/grid/utils/compareGrids.ts | 2 +- .../{configToLayout.ts => configToGrid.ts} | 10 +- domains/grid/utils/defaultGridConfig.ts | 2 +- domains/grid/utils/findBestPosition.ts | 28 ++++ .../grid/utils/getColumnHeightsFromGrid.ts | 29 ++++ domains/grid/utils/getUserGrid.ts | 24 +++ domains/grid/utils/gridLayout.ts | 108 ------------- domains/grid/utils/gridParser.ts | 14 +- .../{layoutToConfig.ts => gridToConfig.ts} | 21 ++- domains/grid/utils/placeWidgetInGrid.ts | 31 ++++ domains/grid/utils/updateColumnHeights.ts | 18 +++ stores/grid.ts | 16 +- types/grid.ts | 4 +- 31 files changed, 459 insertions(+), 462 deletions(-) rename domains/grid/utils/__tests__/{buildLayout.spec.ts => buildGrid.spec.ts} (91%) rename domains/grid/utils/__tests__/{compareLayouts.spec.ts => compareGridWidgets.spec.ts} (77%) rename domains/grid/utils/__tests__/{configToLayout.spec.ts => configToGrid.ts} (85%) rename domains/grid/utils/__tests__/{layoutToConfig.spec.ts => gridToConfig.spec.ts} (79%) rename domains/grid/utils/{buildLayout.ts => buildGrid.ts} (59%) rename domains/grid/utils/{compareLayouts.ts => compareGridWidgets.ts} (58%) rename domains/grid/utils/{configToLayout.ts => configToGrid.ts} (81%) create mode 100644 domains/grid/utils/findBestPosition.ts create mode 100644 domains/grid/utils/getColumnHeightsFromGrid.ts create mode 100644 domains/grid/utils/getUserGrid.ts delete mode 100644 domains/grid/utils/gridLayout.ts rename domains/grid/utils/{layoutToConfig.ts => gridToConfig.ts} (54%) create mode 100644 domains/grid/utils/placeWidgetInGrid.ts create mode 100644 domains/grid/utils/updateColumnHeights.ts diff --git a/app.vue b/app.vue index 2938f538..d19d60dc 100644 --- a/app.vue +++ b/app.vue @@ -23,7 +23,7 @@ const { cacheValue } = useCache() const { currencyList } = storeToRefs(useCurrencyStore()) const { initProvider, reconnect } = useWalletConnectProvider() const { formatMessage } = useIntl() -const { gridChainId, tempGridLayout } = storeToRefs(useGridStore()) +const { gridChainId, tempGrid } = storeToRefs(useGridStore()) const setupTranslations = () => { useIntl().setupIntl(defaultConfig) @@ -117,7 +117,7 @@ const setupNetwork = async () => { // reset temp grid layout if network is changed if (gridChainId.value !== selectedChainId.value) { - tempGridLayout.value = [] + tempGrid.value = [] } gridChainId.value = selectedChainId.value diff --git a/components/ModalTemplateAddEditGrid.vue b/components/ModalTemplateAddEditGrid.vue index d2ef02cc..562da840 100644 --- a/components/ModalTemplateAddEditGrid.vue +++ b/components/ModalTemplateAddEditGrid.vue @@ -4,7 +4,7 @@ import type { SelectStringOption } from '@lukso/web-components' const { modal, closeModal } = useModal() const { formatMessage } = useIntl() -const { tempGridLayout, selectedLayoutId } = storeToRefs(useGridStore()) +const { tempGrid, selectedGridId } = storeToRefs(useGridStore()) const { addGrid, updateGrid } = useGrid() const INPUT_FOCUS_DELAY = 10 // small delay for focusing input after element render @@ -73,13 +73,13 @@ const handleSave = () => { }) } else { const newGrid: Grid = { - id: createGridId(grid, tempGridLayout.value), + id: createGridId(grid, tempGrid.value), ...grid, grid: [], } addGrid(newGrid) - selectedLayoutId.value = newGrid.id + selectedGridId.value = newGrid.id } handleCancel() diff --git a/components/ModalTemplateDeleteGridWidget.vue b/components/ModalTemplateDeleteGridWidget.vue index aee48cbb..ae681a14 100644 --- a/components/ModalTemplateDeleteGridWidget.vue +++ b/components/ModalTemplateDeleteGridWidget.vue @@ -1,10 +1,10 @@ diff --git a/components/ModalTemplateMoveGridWidget.vue b/components/ModalTemplateMoveGridWidget.vue index 47df8ca2..bbe8d4a0 100644 --- a/components/ModalTemplateMoveGridWidget.vue +++ b/components/ModalTemplateMoveGridWidget.vue @@ -3,25 +3,21 @@ import type { SelectStringOption } from '@lukso/web-components' const { modal, closeModal } = useModal() const { formatMessage } = useIntl() -const { tempGridLayout, selectedLayoutId } = storeToRefs(useGridStore()) -const { addGridLayoutItem, removeGridLayoutItem, getGridById } = useGrid() +const { tempGrid, selectedGridId } = storeToRefs(useGridStore()) +const { addGridWidget, removeGridWidget, getGridById } = useGrid() const selectedOption = ref() -const canSubmit = computed( - () => selectedOption.value !== selectedLayoutId.value -) +const canSubmit = computed(() => selectedOption.value !== selectedGridId.value) const options = computed(() => { - return tempGridLayout.value.map(grid => ({ + return tempGrid.value.map(grid => ({ id: grid.id, value: grid.title, })) }) const selectedOptionValue = computed(() => { - const grid = tempGridLayout.value.find( - grid => grid.id === selectedOption.value - ) + const grid = tempGrid.value.find(grid => grid.id === selectedOption.value) return { id: grid?.id, value: grid?.title, @@ -50,16 +46,16 @@ const handleSave = () => { h: modal?.data?.h, }) - removeGridLayoutItem(modal?.data?.id) - addGridLayoutItem( + removeGridWidget(modal?.data?.id) + addGridWidget( duplicatedWidget, - getGridById(tempGridLayout.value, selectedOption.value) + getGridById(tempGrid.value, selectedOption.value) ) handleCancel() } onMounted(() => { - selectedOption.value = selectedLayoutId.value + selectedOption.value = selectedGridId.value }) diff --git a/domains/grid/components/AddWidgetBasic.vue b/domains/grid/components/AddWidgetBasic.vue index e0fab5ab..4832740a 100644 --- a/domains/grid/components/AddWidgetBasic.vue +++ b/domains/grid/components/AddWidgetBasic.vue @@ -10,8 +10,8 @@ type Props = { const props = defineProps() const { formatMessage } = useIntl() const { closeModal, showModal } = useModal() -const { addGridLayoutItem, updateGridLayoutItem, getGridById } = useGrid() -const { tempGridLayout, selectedLayoutId } = storeToRefs(useGridStore()) +const { addGridWidget, updateGridWidget, getGridById } = useGrid() +const { tempGrid, selectedGridId } = storeToRefs(useGridStore()) const TEXTAREA_FOCUS_DELAY = 10 // small delay for focusing textarea after element render const inputValue = ref('') @@ -30,7 +30,7 @@ const handleSave = () => { } if (isEdit.value) { - updateGridLayoutItem(props.id, { + updateGridWidget(props.id, { properties, w: props.width, h: props.height, @@ -43,10 +43,7 @@ const handleSave = () => { h: props.height, }) - addGridLayoutItem( - newWidget, - getGridById(tempGridLayout.value, selectedLayoutId.value) - ) + addGridWidget(newWidget, getGridById(tempGrid.value, selectedGridId.value)) } handleCancel() diff --git a/domains/grid/components/AddWidgetGenericPlatform.vue b/domains/grid/components/AddWidgetGenericPlatform.vue index 334a8f67..22f8e035 100644 --- a/domains/grid/components/AddWidgetGenericPlatform.vue +++ b/domains/grid/components/AddWidgetGenericPlatform.vue @@ -10,8 +10,8 @@ type Props = { const props = defineProps() const { formatMessage } = useIntl() const { closeModal, showModal } = useModal() -const { addGridLayoutItem, updateGridLayoutItem, getGridById } = useGrid() -const { tempGridLayout, selectedLayoutId } = storeToRefs(useGridStore()) +const { addGridWidget, updateGridWidget, getGridById } = useGrid() +const { tempGrid, selectedGridId } = storeToRefs(useGridStore()) const TEXTAREA_FOCUS_DELAY = 10 // small delay for focusing textarea after element render const inputValue = ref('') @@ -32,7 +32,7 @@ const handleSave = async () => { ) if (isEdit.value) { - updateGridLayoutItem(props.id, { + updateGridWidget(props.id, { properties, w: props.width, h: props.height, @@ -45,9 +45,9 @@ const handleSave = async () => { h: props.height, }) - addGridLayoutItem( + addGridWidget( newWidget, - getGridById(tempGridLayout.value, selectedLayoutId.value) + getGridById(tempGrid.value, selectedGridId.value) ) } diff --git a/domains/grid/components/AddWidgetText.vue b/domains/grid/components/AddWidgetText.vue index cbdd9133..c9bc67e0 100644 --- a/domains/grid/components/AddWidgetText.vue +++ b/domains/grid/components/AddWidgetText.vue @@ -12,8 +12,8 @@ type Props = { const props = defineProps() const { formatMessage } = useIntl() const { closeModal, showModal } = useModal() -const { addGridLayoutItem, updateGridLayoutItem, getGridById } = useGrid() -const { tempGridLayout, selectedLayoutId } = storeToRefs(useGridStore()) +const { addGridWidget, updateGridWidget, getGridById } = useGrid() +const { tempGrid, selectedGridId } = storeToRefs(useGridStore()) const INPUT_FOCUS_DELAY = 10 // small delay for focusing input after element render const DEFAULT_PROPERTIES = { @@ -41,7 +41,7 @@ const handleSave = () => { const properties = toRaw(inputValues) if (isEdit.value) { - updateGridLayoutItem(props.id, { + updateGridWidget(props.id, { properties, w: props.width, h: props.height, @@ -54,10 +54,7 @@ const handleSave = () => { h: props.height, }) - addGridLayoutItem( - newWidget, - getGridById(tempGridLayout.value, selectedLayoutId.value) - ) + addGridWidget(newWidget, getGridById(tempGrid.value, selectedGridId.value)) } handleCancel() diff --git a/domains/grid/components/GridTabs.vue b/domains/grid/components/GridTabs.vue index 2677f15c..4a3602f4 100644 --- a/domains/grid/components/GridTabs.vue +++ b/domains/grid/components/GridTabs.vue @@ -8,7 +8,7 @@ type Props = { } const props = defineProps() -const { selectedLayoutId } = storeToRefs(useGridStore()) +const { selectedGridId } = storeToRefs(useGridStore()) const { formatMessage } = useIntl() const { showModal } = useModal() const { canEditGrid } = useGrid() @@ -40,7 +40,7 @@ const handleAddGrid = () => { v-for="tab in tabs" :key="tab.grid.id" :grid="tab.grid" - :is-active="tab.grid.id === selectedLayoutId" + :is-active="tab.grid.id === selectedGridId" />
  • () const { formatMessage } = useIntl() const { canEditGrid, addGrid } = useGrid() const { showModal } = useModal() -const { selectedLayoutId, tempGridLayout } = storeToRefs(useGridStore()) +const { selectedGridId, tempGrid } = storeToRefs(useGridStore()) const dropdownId = `dropdown-${generateItemId()}` const styleVariants = tv({ @@ -60,18 +60,18 @@ const handleDelete = () => { const handleDuplicate = () => { const newGrid: Grid = { - id: createGridId(props.grid, tempGridLayout.value), + id: createGridId(props.grid, tempGrid.value), title: formatMessage('grid_tabs_copy_of', { title: props.grid.title }), grid: props.grid.grid, gridColumns: props.grid.gridColumns, } addGrid(newGrid) - selectedLayoutId.value = newGrid.id + selectedGridId.value = newGrid.id } const handleSelectTab = (id: string) => { - selectedLayoutId.value = id + selectedGridId.value = id } diff --git a/domains/grid/components/GridView.vue b/domains/grid/components/GridView.vue index 371abb63..d012a2bb 100644 --- a/domains/grid/components/GridView.vue +++ b/domains/grid/components/GridView.vue @@ -5,49 +5,49 @@ import { GridItem, GridLayout } from 'grid-layout-plus' const { isConnected, isMobile } = storeToRefs(useAppStore()) const { isEditingGrid, - tempGridLayout, - viewedGridLayout, + tempGrid, + viewedGrid, hasUnsavedGrid, gridRowHeightRatio, - selectedLayoutId, + selectedGridId, } = storeToRefs(useGridStore()) const { - initializeGridLayout, - saveGridLayout, + initializeGrid, + saveGrid, canEditGrid, - getSelectedLayout, - updateSelectedLayout, - initSelectedLayoutId, + getSelectedGridWidgets, + updateSelectedGrid, + initSelectedGridId, getGridById, } = useGrid() const gridContainer = ref(null) const { width } = useElementSize(gridContainer) -const layout = ref([]) +const gridWidgets = ref([]) const movementX = ref(0) const address = computed(() => getCurrentProfileAddress()) -const currentLayout = computed(() => { - // when user is editing and has unsaved changes, use temp layout +const currentGrid = computed(() => { + // when user is editing and has unsaved changes, use temp grid if (canEditGrid.value && hasUnsavedGrid.value) { - return tempGridLayout.value + return tempGrid.value } - return viewedGridLayout.value + return viewedGrid.value }) -const layoutRowHeight = computed(() => { - const columnSpacing = GRID_SPACING_PX * (layoutColumnNumber.value - 1) +const gridRowHeight = computed(() => { + const columnSpacing = GRID_SPACING_PX * (gridColumnNumber.value - 1) const columnWidth = width.value - columnSpacing const rowHeight = - (columnWidth / layoutColumnNumber.value) * gridRowHeightRatio.value + (columnWidth / gridColumnNumber.value) * gridRowHeightRatio.value return rowHeight }) -const layoutColumnNumber = computed(() => +const gridColumnNumber = computed(() => isMobile.value ? 1 - : getGridById(currentLayout.value, selectedLayoutId.value)?.gridColumns || + : getGridById(currentGrid.value, selectedGridId.value)?.gridColumns || GRID_COLUMNS_MIN ) @@ -73,35 +73,33 @@ const handleMouseMove = (event: MouseEvent) => { movementX.value = event.movementX } -const handleUpdateLayout = (newLayout: GridWidget[]) => { +const handleUpdateGrid = (newGrid: GridWidget[]) => { if (gridLog.enabled) { - gridLog('Layout updated', toRaw(newLayout)) + gridLog('Grid updated', toRaw(newGrid)) } - layout.value = newLayout + gridWidgets.value = newGrid } -const handleSaveLayout = async () => { +const handleSaveGrid = async () => { if (!canEditGrid.value) { return } - await saveGridLayout(tempGridLayout.value) + await saveGrid(tempGrid.value) } -const handleResetLayout = async () => { - const userLayout = await getUserLayout(address.value) - const _layout = buildLayout(userLayout, isMobile.value, canEditGrid.value) +const handleResetGrid = async () => { + const userGrid = await getUserGrid(address.value) + const _grid = buildGrid(userGrid, isMobile.value, canEditGrid.value) - tempGridLayout.value = cloneObject(_layout) - viewedGridLayout.value = cloneObject(_layout) - layout.value = getSelectedLayout(cloneObject(_layout)) + tempGrid.value = cloneObject(_grid) + viewedGrid.value = cloneObject(_grid) + gridWidgets.value = getSelectedGridWidgets(cloneObject(_grid)) - // if selected layout is affected by reset, select first layout - if ( - !viewedGridLayout.value.some(item => item.id === selectedLayoutId.value) - ) { - selectedLayoutId.value = viewedGridLayout.value[0]?.id + // if selected grid is affected by reset, select first grid + if (!viewedGrid.value.some(item => item.id === selectedGridId.value)) { + selectedGridId.value = viewedGrid.value[0]?.id } } @@ -115,7 +113,7 @@ const handleItemMove = () => { const handleItemMoved = () => { clearSelection() - tempGridLayout.value = updateSelectedLayout(layout.value) + tempGrid.value = updateSelectedGrid(gridWidgets.value) } const handleItemResize = () => { @@ -124,36 +122,36 @@ const handleItemResize = () => { const handleItemResized = () => { clearSelection() - tempGridLayout.value = updateSelectedLayout(layout.value) + tempGrid.value = updateSelectedGrid(gridWidgets.value) } -// rebuild layout and track unsaved state when: +// rebuild grid and track unsaved state when: // - user make modifications in widgets (add/edit/remove/resize) // - user toggles edit mode watch( - [tempGridLayout, isEditingGrid, selectedLayoutId, isMobile], + [tempGrid, isEditingGrid, selectedGridId, isMobile], async () => { await nextTick() - const updatedViewedLayout = buildLayout( - viewedGridLayout.value, + const updatedViewedGrid = buildGrid( + viewedGrid.value, isMobile.value, canEditGrid.value ) - const updatedTempLayout = buildLayout( - tempGridLayout.value, + const updatedTempGrid = buildGrid( + tempGrid.value, isMobile.value, canEditGrid.value ) - // if user is in edit mode we use temp layout, otherwise viewed layout + // if user is in edit mode we use temp grid, otherwise viewed grid if (isEditingGrid.value) { - layout.value = getSelectedLayout(updatedTempLayout) + gridWidgets.value = getSelectedGridWidgets(updatedTempGrid) } else { - layout.value = getSelectedLayout(updatedViewedLayout) + gridWidgets.value = getSelectedGridWidgets(updatedViewedGrid) } - const changes = compareGrids(updatedViewedLayout, updatedTempLayout) + const changes = compareGrids(updatedViewedGrid, updatedTempGrid) if (changes.length > 0) { hasUnsavedGrid.value = true @@ -161,18 +159,18 @@ watch( hasUnsavedGrid.value = false } - // re-init selected layout id when user toggles edit mode in case the selected grid was changed - initSelectedLayoutId() + // re-init selected grid id when user toggles edit mode in case the selected grid was changed + initSelectedGridId() }, { deep: true } ) -// initialize layout on initial render and when user connects/disconnects +// initialize grid on initial render and when user connects/disconnects watch( [isConnected], async () => { - await initializeGridLayout(address.value, canEditGrid.value) - layout.value = getSelectedLayout(currentLayout.value) + await initializeGrid(address.value, canEditGrid.value) + gridWidgets.value = getSelectedGridWidgets(currentGrid.value) }, { immediate: true } ) @@ -189,11 +187,11 @@ onUnmounted(() => { diff --git a/domains/grid/components/GridWidget.vue b/domains/grid/components/GridWidget.vue index 27a80fbb..ccd595f8 100644 --- a/domains/grid/components/GridWidget.vue +++ b/domains/grid/components/GridWidget.vue @@ -7,13 +7,12 @@ type Props = { const props = defineProps() const widgetComponent = shallowRef() -const { canEditGrid, addGridLayoutItem, getGridById } = useGrid() +const { canEditGrid, addGridWidget, getGridById } = useGrid() const { formatMessage } = useIntl() const { showModal } = useModal() const { isConnected, isMobile, isConnectedUserViewingOwnProfile } = storeToRefs(useAppStore()) -const { isEditingGrid, tempGridLayout, selectedLayoutId } = - storeToRefs(useGridStore()) +const { isEditingGrid, tempGrid, selectedGridId } = storeToRefs(useGridStore()) const { connect } = useBaseProvider() const { browserSupportExtension } = useBrowser() const dropdownId = `dropdown-${generateItemId()}` @@ -118,10 +117,7 @@ const handleClone = async () => { w: props.widget.w, h: props.widget.h, }) - addGridLayoutItem( - clonedWidget, - getGridById(tempGridLayout.value, selectedLayoutId.value) - ) + addGridWidget(clonedWidget, getGridById(tempGrid.value, selectedGridId.value)) isEditingGrid.value = true // we enable edit mode so user is aware about unsaved state if (!isConnectedUserViewingOwnProfile.value) { diff --git a/domains/grid/composables/useGrid.ts b/domains/grid/composables/useGrid.ts index 3dbfd7e0..6f821365 100644 --- a/domains/grid/composables/useGrid.ts +++ b/domains/grid/composables/useGrid.ts @@ -10,10 +10,10 @@ export const useGrid = () => { const { isEditingGrid, hasUnsavedGrid, - viewedGridLayout, - tempGridLayout, + viewedGrid, + tempGrid, isSavingGrid, - selectedLayoutId, + selectedGridId, } = storeToRefs(useGridStore()) const canEditGrid = computed( @@ -26,16 +26,18 @@ export const useGrid = () => { const getGridById = (grids: Grid[], id?: string) => grids.find(grid => grid.id === id) - const getSelectedLayout = (layouts: Grid[]): GridWidget[] => - getGridById(layouts, selectedLayoutId.value)?.grid || [] + const getSelectedGridWidgets = (grids: Grid[]): GridWidget[] => + getGridById(grids, selectedGridId.value)?.grid || [] - const updateSelectedLayout = (layout: GridWidget[]): Grid[] => { - const updatedLayouts = tempGridLayout.value.map(item => { - if (item.id === selectedLayoutId.value) { + const updateSelectedGrid = ( + gridWidgets: GridWidget[] + ): Grid[] => { + const updatedGrids = tempGrid.value.map(item => { + if (item.id === selectedGridId.value) { return { id: item.id, title: item.title, - grid: layout, + grid: gridWidgets, gridColumns: item.gridColumns, } } @@ -43,93 +45,84 @@ export const useGrid = () => { return item }) - return updatedLayouts + return updatedGrids } - const initSelectedLayoutId = () => { - const currentLayout = canEditGrid.value - ? tempGridLayout.value - : viewedGridLayout.value + const initSelectedGridId = () => { + const currentGrid = canEditGrid.value ? tempGrid.value : viewedGrid.value if ( - !selectedLayoutId.value || - (selectedLayoutId.value && - !currentLayout.some(item => item.id === selectedLayoutId.value)) + !selectedGridId.value || + (selectedGridId.value && + !currentGrid.some(item => item.id === selectedGridId.value)) ) { - selectedLayoutId.value = currentLayout[0]?.id + selectedGridId.value = currentGrid[0]?.id } } const gridCount = computed(() => { if (canEditGrid.value) { - return tempGridLayout.value.length + return tempGrid.value.length } - return viewedGridLayout.value.length + return viewedGrid.value.length }) return { - initializeGridLayout: async ( + initializeGrid: async ( address?: Address, withAddContentPlaceholder?: boolean ) => { - let layout: Grid[] = [] + let grid: Grid[] = [] if (!address) { return [] } - // initialize user layout from the config stored in UP - const _initUserLayout = async () => { - const userLayout = await getUserLayout(address) - layout = buildLayout( - userLayout, - isMobile.value, - withAddContentPlaceholder - ) + // initialize user grid from the config stored in UP + const _initUserGrid = async () => { + const userGrid = await getUserGrid(address) + grid = buildGrid(userGrid, isMobile.value, withAddContentPlaceholder) if (gridLog.enabled) { - gridLog('Initialize user grid', userLayout) + gridLog('Initialize user grid', userGrid) } - viewedGridLayout.value = cloneObject(layout) + viewedGrid.value = cloneObject(grid) } - // in case we don't have a temp layout yet we initialize it - const _initTempLayout = () => { - if ( - tempGridLayout.value.length === 0 && - viewedGridLayout.value.length !== 0 - ) { - tempGridLayout.value = cloneObject(layout) + // in case we don't have a temp grid yet we initialize it + const _initTempGrid = () => { + if (tempGrid.value.length === 0 && viewedGrid.value.length !== 0) { + tempGrid.value = cloneObject(grid) } } - // in edit mode we initialize from temp layout + // in edit mode we initialize from temp grid const _initEditMode = () => { if (canEditGrid.value) { - layout = buildLayout( - tempGridLayout.value, + grid = buildGrid( + tempGrid.value, isMobile.value, withAddContentPlaceholder ) if (gridLog.enabled) { - gridLog('Initialize temp grid', layout) + gridLog('Initialize temp grid', grid) } - tempGridLayout.value = cloneObject(layout) + tempGrid.value = cloneObject(grid) } } - await _initUserLayout() - _initTempLayout() + await _initUserGrid() + _initTempGrid() _initEditMode() - initSelectedLayoutId() + initSelectedGridId() }, - addGridLayoutItem: ( - newItem: GridWidgetWithoutCords, + addGridWidget: ( + widget: GridWidgetWithoutCords, grid?: Grid ) => { if (!canEditGrid.value) { @@ -142,10 +135,10 @@ export const useGrid = () => { return } - placeWidgetInLayout(newItem, grid.grid, grid.gridColumns) + placeWidgetInGrid(widget, grid.grid, grid.gridColumns) }, - updateGridLayoutItem: (id?: string, widget?: Partial) => { + updateGridWidget: (id?: string, widget?: Partial) => { if (!canEditGrid.value) { console.warn('User cannot edit grid') return @@ -156,22 +149,22 @@ export const useGrid = () => { return } - const layout = getSelectedLayout(tempGridLayout.value) - const widgetIndex = layout.findIndex(({ i }) => i === id) + const gridWidgets = getSelectedGridWidgets(tempGrid.value) + const widgetIndex = gridWidgets.findIndex(({ i }) => i === id) if (widgetIndex === -1) { console.warn('Widget not found', id) return } - layout[widgetIndex] = { - ...layout[widgetIndex], + gridWidgets[widgetIndex] = { + ...gridWidgets[widgetIndex], ...widget, } - tempGridLayout.value = updateSelectedLayout(layout) + tempGrid.value = updateSelectedGrid(gridWidgets) }, - removeGridLayoutItem: (id: string | number) => { + removeGridWidget: (id: string | number) => { if (!canEditGrid.value) { console.warn('User cannot edit grid') return @@ -181,22 +174,22 @@ export const useGrid = () => { return } - tempGridLayout.value = updateSelectedLayout( - getSelectedLayout(tempGridLayout.value).filter(item => item.i !== id) + tempGrid.value = updateSelectedGrid( + getSelectedGridWidgets(tempGrid.value).filter(item => item.i !== id) ) }, - saveGridLayout: async (layout?: Grid[]) => { + saveGrid: async (grid?: Grid[]) => { if (!canEditGrid.value) { console.warn('User cannot edit grid') return } - if (!layout || !connectedProfileAddress.value) { + if (!grid || !connectedProfileAddress.value) { return } - const config = layoutToConfig(layout) + const config = gridToConfig(grid) if (!isConfigValid(config)) { console.warn('Invalid schema') @@ -213,20 +206,20 @@ export const useGrid = () => { isSavingGrid.value = false isEditingGrid.value = false - // rebuild layout to ensure that all widgets are in the correct position - const layout = buildLayout( - tempGridLayout.value, + // rebuild grid to ensure that all widgets are in the correct position + const grid = buildGrid( + tempGrid.value, isMobile.value, canEditGrid.value ) - tempGridLayout.value = cloneObject(layout) - viewedGridLayout.value = cloneObject(layout) + tempGrid.value = cloneObject(grid) + viewedGrid.value = cloneObject(grid) }) ?.on('receipt', (receipt: any) => { if (gridLog.enabled) { - gridLog('Layout saved', toRaw(layout), receipt) + gridLog('Grid saved', toRaw(grid), receipt) } }) ?.on('error', (error: Error) => { @@ -234,7 +227,7 @@ export const useGrid = () => { isSavingGrid.value = false }) } catch (error) { - console.warn('Error saving layout', error) + console.warn('Error saving grid', error) isSavingGrid.value = false } }, @@ -245,7 +238,7 @@ export const useGrid = () => { return } - tempGridLayout.value.push(grid) + tempGrid.value.push(grid) }, updateGrid: ( @@ -267,15 +260,15 @@ export const useGrid = () => { return } - const index = tempGridLayout.value.findIndex(item => item.id === id) + const index = tempGrid.value.findIndex(item => item.id === id) if (index === -1) { console.warn('Grid not found', id) return } - tempGridLayout.value[index] = { - ...tempGridLayout.value[index], + tempGrid.value[index] = { + ...tempGrid.value[index], ...{ ...grid, }, @@ -288,12 +281,12 @@ export const useGrid = () => { return } - tempGridLayout.value = tempGridLayout.value.filter(item => item.id !== id) + tempGrid.value = tempGrid.value.filter(item => item.id !== id) }, - getSelectedLayout, - updateSelectedLayout, + getSelectedGridWidgets, + updateSelectedGrid, canEditGrid, - initSelectedLayoutId, + initSelectedGridId, getGridById, gridCount, } diff --git a/domains/grid/utils/__tests__/buildLayout.spec.ts b/domains/grid/utils/__tests__/buildGrid.spec.ts similarity index 91% rename from domains/grid/utils/__tests__/buildLayout.spec.ts rename to domains/grid/utils/__tests__/buildGrid.spec.ts index e2cf87fa..901fc3f6 100644 --- a/domains/grid/utils/__tests__/buildLayout.spec.ts +++ b/domains/grid/utils/__tests__/buildGrid.spec.ts @@ -1,16 +1,16 @@ import { e } from 'pinia-orm/dist/shared/pinia-orm.ed84a779' import { describe, expect, it } from 'vitest' -import { buildLayout } from '../buildLayout' +import { buildGrid } from '../buildGrid' -describe('buildLayout', () => { - it('should return an empty layout when given an empty layout', () => { - const layout: Grid[] = [] - const result = buildLayout(layout) +describe('buildGrid', () => { + it('should return an empty grid when given an empty grid', () => { + const grid: Grid[] = [] + const result = buildGrid(grid) expect(result).toEqual([]) }) - it('should re-order layout based on x/y cords', () => { - const layout: Grid[] = [ + it('should re-order grid based on x/y cords', () => { + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -46,7 +46,7 @@ describe('buildLayout', () => { gridColumns: 1, }, ] - const result = buildLayout(layout) + const result = buildGrid(grid) expect(result).toEqual([ { id: '1', @@ -86,8 +86,8 @@ describe('buildLayout', () => { }) describe('withAddContentPlaceholder', () => { - it('should remove "add content" placeholder from layout', () => { - const layout: Grid[] = [ + it('should remove "add content" placeholder from grid', () => { + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -114,7 +114,7 @@ describe('buildLayout', () => { gridColumns: 1, }, ] - const result = buildLayout(layout) + const result = buildGrid(grid) expect(result).toEqual([ { id: '1', @@ -136,7 +136,7 @@ describe('buildLayout', () => { }) it('should add placeholder', () => { - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -154,7 +154,7 @@ describe('buildLayout', () => { gridColumns: 1, }, ] - const result = buildLayout(layout, undefined, true) + const result = buildGrid(grid, undefined, true) expect(result).toEqual([ { id: '1', @@ -186,8 +186,8 @@ describe('buildLayout', () => { }) }) - describe('layout 1', () => { - it('should build 1 column layout', () => { + describe('grid 1', () => { + it('should build 1 column grid', () => { /* Column 0 --------- @@ -198,7 +198,7 @@ describe('buildLayout', () => { | | | D | <-- height 1 */ - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -236,7 +236,7 @@ describe('buildLayout', () => { }, ] - expect(buildLayout(layout)).toEqual([ + expect(buildGrid(grid)).toEqual([ { id: '1', title: 'Test Grid', @@ -283,7 +283,7 @@ describe('buildLayout', () => { ]) }) - it('should build 2 column layout', () => { + it('should build 2 column grid', () => { /* Column 0 Column 1 ---------------------- @@ -292,7 +292,7 @@ describe('buildLayout', () => { |-------| | C | <-- C: height 2 | D | | | <-- D: height 1 */ - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -330,7 +330,7 @@ describe('buildLayout', () => { }, ] - expect(buildLayout(layout)).toEqual([ + expect(buildGrid(grid)).toEqual([ { id: '1', title: 'Test Grid', @@ -377,7 +377,7 @@ describe('buildLayout', () => { ]) }) - it('should build 3 column layout', () => { + it('should build 3 column grid', () => { /* Column 0 Column 1 Column 2 ----------------------------------- @@ -385,7 +385,7 @@ describe('buildLayout', () => { | | |-------| | | |-------| | D | |-------| */ - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -423,7 +423,7 @@ describe('buildLayout', () => { }, ] - expect(buildLayout(layout)).toEqual([ + expect(buildGrid(grid)).toEqual([ { id: '1', title: 'Test Grid', @@ -471,8 +471,8 @@ describe('buildLayout', () => { }) }) - describe('layout 2', () => { - it('should build 1 column layout', () => { + describe('grid 2', () => { + it('should build 1 column grid', () => { /* Column 0 --------- @@ -483,7 +483,7 @@ describe('buildLayout', () => { |-------| | C | <-- height 1 */ - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -514,7 +514,7 @@ describe('buildLayout', () => { }, ] - expect(buildLayout(layout)).toEqual([ + expect(buildGrid(grid)).toEqual([ { id: '1', title: 'Test Grid', @@ -553,7 +553,7 @@ describe('buildLayout', () => { ]) }) - it('should build 2 column layout', () => { + it('should build 2 column grid', () => { /* Column 0 Column 1 ---------------------- @@ -561,7 +561,7 @@ describe('buildLayout', () => { |-------| |-------| | B | | C | <-- height 1 each */ - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -592,7 +592,7 @@ describe('buildLayout', () => { }, ] - expect(buildLayout(layout)).toEqual([ + expect(buildGrid(grid)).toEqual([ { id: '1', title: 'Test Grid', @@ -631,8 +631,8 @@ describe('buildLayout', () => { }) }) - describe('layout 3', () => { - it('should build 1 column layout', () => { + describe('grid 3', () => { + it('should build 1 column grid', () => { /* Column 0 --------- @@ -645,7 +645,7 @@ describe('buildLayout', () => { |-------| | D | <-- height 1 */ - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -683,7 +683,7 @@ describe('buildLayout', () => { }, ] - expect(buildLayout(layout)).toEqual([ + expect(buildGrid(grid)).toEqual([ { id: '1', title: 'Test Grid', @@ -732,7 +732,7 @@ describe('buildLayout', () => { ]) }) - it('should build 2 column layout', () => { + it('should build 2 column grid', () => { /* Column 0 Column 1 ---------------------- @@ -744,7 +744,7 @@ describe('buildLayout', () => { |-------|-------------| | D | D | <-- height 1 (width spans 2 columns) */ - const layout: Grid[] = [ + const grid: Grid[] = [ { id: '1', title: 'Test Grid', @@ -782,7 +782,7 @@ describe('buildLayout', () => { }, ] - expect(buildLayout(layout)).toEqual([ + expect(buildGrid(grid)).toEqual([ { id: '1', title: 'Test Grid', diff --git a/domains/grid/utils/__tests__/compareLayouts.spec.ts b/domains/grid/utils/__tests__/compareGridWidgets.spec.ts similarity index 77% rename from domains/grid/utils/__tests__/compareLayouts.spec.ts rename to domains/grid/utils/__tests__/compareGridWidgets.spec.ts index 8592bf52..db699371 100644 --- a/domains/grid/utils/__tests__/compareLayouts.spec.ts +++ b/domains/grid/utils/__tests__/compareGridWidgets.spec.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest' -import { compareLayouts } from '../compareLayouts' +import { compareGridWidgets } from '../compareGridWidgets' -describe('compareLayouts', () => { - it('returns empty array when both layouts are the same', () => { - const layoutA = [ +describe('compareGridWidgets', () => { + it('returns empty array when both grids are the same', () => { + const gridA = [ { i: 'widget-1', x: 0, @@ -16,7 +16,7 @@ describe('compareLayouts', () => { }, }, ] - const layoutB = [ + const gridB = [ { i: 'widget-1', x: 0, @@ -30,12 +30,12 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([]) - expect(compareLayouts([], [])).toEqual([]) + expect(compareGridWidgets(gridA, gridB)).toEqual([]) + expect(compareGridWidgets([], [])).toEqual([]) }) it('returns array of changes when widgets change positions', () => { - const layoutA = [ + const gridA = [ { i: 'widget-1', x: 0, @@ -48,7 +48,7 @@ describe('compareLayouts', () => { }, }, ] - const layoutB = [ + const gridB = [ { i: 'widget-1', x: 1, @@ -62,16 +62,16 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([ + expect(compareGridWidgets(gridA, gridB)).toEqual([ { - oldWidget: layoutA[0], - newWidget: layoutB[0], + oldWidget: gridA[0], + newWidget: gridB[0], }, ]) }) it('returns array of changes when widgets change sizes', () => { - const layoutA = [ + const gridA = [ { i: 'widget-1', x: 0, @@ -84,7 +84,7 @@ describe('compareLayouts', () => { }, }, ] - const layoutB = [ + const gridB = [ { i: 'widget-1', x: 0, @@ -98,16 +98,16 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([ + expect(compareGridWidgets(gridA, gridB)).toEqual([ { - oldWidget: layoutA[0], - newWidget: layoutB[0], + oldWidget: gridA[0], + newWidget: gridB[0], }, ]) }) it('returns array of changes when widgets change types', () => { - const layoutA = [ + const gridA = [ { i: 'widget-1', x: 0, @@ -120,7 +120,7 @@ describe('compareLayouts', () => { }, }, ] - const layoutB = [ + const gridB = [ { i: 'widget-1', x: 0, @@ -134,16 +134,16 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([ + expect(compareGridWidgets(gridA, gridB)).toEqual([ { - oldWidget: layoutA[0], - newWidget: layoutB[0], + oldWidget: gridA[0], + newWidget: gridB[0], }, ]) }) it('returns array of changes when widgets change properties', () => { - const layoutA = [ + const gridA = [ { i: 'widget-1', x: 0, @@ -156,7 +156,7 @@ describe('compareLayouts', () => { }, }, ] - const layoutB = [ + const gridB = [ { i: 'widget-1', x: 0, @@ -170,17 +170,17 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([ + expect(compareGridWidgets(gridA, gridB)).toEqual([ { - oldWidget: layoutA[0], - newWidget: layoutB[0], + oldWidget: gridA[0], + newWidget: gridB[0], }, ]) }) it('returns array of changes when widgets are added', () => { - const layoutA = [] as GridWidget[] - const layoutB = [ + const gridA = [] as GridWidget[] + const gridB = [ { i: 'widget-1', x: 0, @@ -194,16 +194,16 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([ + expect(compareGridWidgets(gridA, gridB)).toEqual([ { oldWidget: null, - newWidget: layoutB[0], + newWidget: gridB[0], }, ]) }) it('returns array of changes when widgets are removed', () => { - const layoutA = [ + const gridA = [ { i: 'widget-1', x: 0, @@ -216,18 +216,18 @@ describe('compareLayouts', () => { }, }, ] - const layoutB = [] as GridWidget[] + const gridB = [] as GridWidget[] - expect(compareLayouts(layoutA, layoutB)).toEqual([ + expect(compareGridWidgets(gridA, gridB)).toEqual([ { - oldWidget: layoutA[0], + oldWidget: gridA[0], newWidget: null, }, ]) }) it('returns array of changes when multiple widgets change', () => { - const layoutA = [ + const gridA = [ { i: 'widget-1', x: 0, @@ -262,7 +262,7 @@ describe('compareLayouts', () => { }, }, ] - const layoutB = [ + const gridB = [ { i: 'widget-1', x: 1, @@ -298,20 +298,20 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([ + expect(compareGridWidgets(gridA, gridB)).toEqual([ { - oldWidget: layoutA[0], - newWidget: layoutB[0], + oldWidget: gridA[0], + newWidget: gridB[0], }, { - oldWidget: layoutA[1], - newWidget: layoutB[1], + oldWidget: gridA[1], + newWidget: gridB[1], }, ]) }) it('ignores ADD_CONTENT widgets', () => { - const layoutA = [ + const gridA = [ { i: 'widget-1', x: 0, @@ -333,7 +333,7 @@ describe('compareLayouts', () => { properties: {}, }, ] - const layoutB = [ + const gridB = [ { i: 'widget-1', x: 0, @@ -347,6 +347,6 @@ describe('compareLayouts', () => { }, ] - expect(compareLayouts(layoutA, layoutB)).toEqual([]) + expect(compareGridWidgets(gridA, gridB)).toEqual([]) }) }) diff --git a/domains/grid/utils/__tests__/configToLayout.spec.ts b/domains/grid/utils/__tests__/configToGrid.ts similarity index 85% rename from domains/grid/utils/__tests__/configToLayout.spec.ts rename to domains/grid/utils/__tests__/configToGrid.ts index 82c5546b..81db49c1 100644 --- a/domains/grid/utils/__tests__/configToLayout.spec.ts +++ b/domains/grid/utils/__tests__/configToGrid.ts @@ -1,11 +1,12 @@ import { describe, expect, it, vi } from 'vitest' -import { configToLayout } from '../configToLayout' + +import { configToGrid } from '../configToGrid' vi.mock('/domains/grid/utils/generateItemId', () => ({ generateItemId: () => 'test-id', })) -describe('configToLayout', () => { +describe('configToGrid', () => { it('should create unique id for each grid item', () => { const config = [ { @@ -34,11 +35,11 @@ describe('configToLayout', () => { }, ] - expect(configToLayout(config)).toEqual(result) + expect(configToGrid(config)).toEqual(result) }) - it('should return an empty layout for an empty config', () => { - expect(configToLayout([])).toEqual([]) + it('should return an empty grid for an empty config', () => { + expect(configToGrid([])).toEqual([]) }) it('should correctly handle a single item config', () => { @@ -60,7 +61,7 @@ describe('configToLayout', () => { height: 1, properties: { title: 'Hey', - text: 'Customize your grid layout!', + text: 'Customize your grid grid!', backgroundColor: '#9db9b9', }, }, @@ -94,7 +95,7 @@ describe('configToLayout', () => { type: 'TEXT', properties: { title: 'Hey', - text: 'Customize your grid layout!', + text: 'Customize your grid grid!', backgroundColor: '#9db9b9', }, w: 1, @@ -115,6 +116,6 @@ describe('configToLayout', () => { }, ] - expect(configToLayout(config)).toEqual(result) + expect(configToGrid(config)).toEqual(result) }) }) diff --git a/domains/grid/utils/__tests__/layoutToConfig.spec.ts b/domains/grid/utils/__tests__/gridToConfig.spec.ts similarity index 79% rename from domains/grid/utils/__tests__/layoutToConfig.spec.ts rename to domains/grid/utils/__tests__/gridToConfig.spec.ts index d9ff041f..d68fdb0d 100644 --- a/domains/grid/utils/__tests__/layoutToConfig.spec.ts +++ b/domains/grid/utils/__tests__/gridToConfig.spec.ts @@ -1,13 +1,13 @@ import { describe, expect, it, vi } from 'vitest' -import { layoutToConfig } from '../layoutToConfig' +import { gridToConfig } from '../gridToConfig' -describe('layoutToConfig', () => { - it('should return an empty config for an empty layout', () => { - expect(layoutToConfig([])).toEqual([]) +describe('gridToConfig', () => { + it('should return an empty config for an empty grid', () => { + expect(gridToConfig([])).toEqual([]) }) - it('should correctly handle a single item layout', () => { - const layout = [ + it('should correctly handle a single item grid', () => { + const grid = [ { id: 'single', title: 'single', @@ -27,7 +27,7 @@ describe('layoutToConfig', () => { type: GRID_WIDGET_TYPE.TEXT, properties: { title: 'Hey', - text: 'Customize your grid layout!', + text: 'Customize your grid grid!', backgroundColor: '#9db9b9', }, w: 1, @@ -69,7 +69,7 @@ describe('layoutToConfig', () => { height: 1, properties: { title: 'Hey', - text: 'Customize your grid layout!', + text: 'Customize your grid grid!', backgroundColor: '#9db9b9', }, }, @@ -86,6 +86,6 @@ describe('layoutToConfig', () => { }, ] - expect(layoutToConfig(layout)).toEqual(result) + expect(gridToConfig(grid)).toEqual(result) }) }) diff --git a/domains/grid/utils/buildLayout.ts b/domains/grid/utils/buildGrid.ts similarity index 59% rename from domains/grid/utils/buildLayout.ts rename to domains/grid/utils/buildGrid.ts index fe170144..b2304731 100644 --- a/domains/grid/utils/buildLayout.ts +++ b/domains/grid/utils/buildGrid.ts @@ -1,20 +1,18 @@ -export const buildLayout = ( - layout: Grid[] | Grid[], +export const buildGrid = ( + grid: Grid[] | Grid[], isMobile?: boolean, withAddContentPlaceholder?: boolean ): Grid[] => { - const _buildLayout = ( - layout: GridWidgetWithoutCords[], - updatedLayout: GridWidget[], + const _buildGrid = ( + grid: GridWidgetWithoutCords[], + updatedGrid: GridWidget[], gridColumns: number ) => { - // remove "add widget" placeholder from layout - let _layout = layout.filter( - item => item.type !== GRID_WIDGET_TYPE.ADD_CONTENT - ) + // remove "add widget" placeholder from grid + let _grid = grid.filter(item => item.type !== GRID_WIDGET_TYPE.ADD_CONTENT) - // if items already have x/y cords we re-order layout to reflect that - _layout = _layout.slice().sort((a, b) => { + // if items already have x/y cords we re-order grid to reflect that + _grid = _grid.slice().sort((a, b) => { if ( a.x === undefined || b.x === undefined || @@ -33,7 +31,7 @@ export const buildLayout = ( // re-add placeholder if (withAddContentPlaceholder) { - _layout.push( + _grid.push( createWidgetObject({ i: 'placeholder', type: GRID_WIDGET_TYPE.ADD_CONTENT, @@ -42,22 +40,22 @@ export const buildLayout = ( ) } - for (const widget of _layout) { - placeWidgetInLayout(widget, updatedLayout, gridColumns) + for (const widget of _grid) { + placeWidgetInGrid(widget, updatedGrid, gridColumns) } - return updatedLayout + return updatedGrid } - return layout.map(item => { - const updatedLayout: GridWidget[] = [] + return grid.map(item => { + const updatedGrid: GridWidget[] = [] const gridColumns = getGridColumns(item.gridColumns) return { id: item.id, title: item.title, - grid: _buildLayout( + grid: _buildGrid( item.grid, - updatedLayout, + updatedGrid, isMobile ? DEFAULT_SMALL_COLUMN_NUMBER : gridColumns ), gridColumns, diff --git a/domains/grid/utils/compareLayouts.ts b/domains/grid/utils/compareGridWidgets.ts similarity index 58% rename from domains/grid/utils/compareLayouts.ts rename to domains/grid/utils/compareGridWidgets.ts index 95acb194..f0fa688b 100644 --- a/domains/grid/utils/compareLayouts.ts +++ b/domains/grid/utils/compareGridWidgets.ts @@ -1,13 +1,13 @@ /** - * Compare two grid layouts and return an array of changes. + * Compare two grids and return an array of changes. * - * @param layoutA - Old grid layout. - * @param layoutB - New grid layout. + * @param gridA - Old grid. + * @param gridB - New grid. * @returns Array of changes. */ -export const compareLayouts = ( - _layoutA?: GridWidget[], - _layoutB?: GridWidget[] +export const compareGridWidgets = ( + _gridA?: GridWidget[], + _gridB?: GridWidget[] ): GridWidgetChange[] => { const result: GridWidgetChange[] = [] const propertiesToCheck: (keyof GridWidget)[] = [ @@ -18,15 +18,15 @@ export const compareLayouts = ( 'type', 'properties', ] - const layoutA = - _layoutA?.filter(item => item.type !== GRID_WIDGET_TYPE.ADD_CONTENT) || [] - const layoutB = - _layoutB?.filter(item => item.type !== GRID_WIDGET_TYPE.ADD_CONTENT) || [] - const maxLength = Math.max(layoutA.length, layoutB.length) + const gridA = + _gridA?.filter(item => item.type !== GRID_WIDGET_TYPE.ADD_CONTENT) || [] + const gridB = + _gridB?.filter(item => item.type !== GRID_WIDGET_TYPE.ADD_CONTENT) || [] + const maxLength = Math.max(gridA.length, gridB.length) for (let i = 0; i < maxLength; i++) { - const oldWidget = layoutA[i] - const newWidget = layoutB[i] + const oldWidget = gridA[i] + const newWidget = gridB[i] if (!oldWidget) { result.push({ oldWidget: null, newWidget }) diff --git a/domains/grid/utils/compareGrids.ts b/domains/grid/utils/compareGrids.ts index 16a16859..469afb87 100644 --- a/domains/grid/utils/compareGrids.ts +++ b/domains/grid/utils/compareGrids.ts @@ -17,7 +17,7 @@ export const compareGrids = ( const newGrid = gridB[i] as Grid | undefined // compare differences in title or grid array - const comparedWidgets = compareLayouts(oldGrid?.grid, newGrid?.grid) + const comparedWidgets = compareGridWidgets(oldGrid?.grid, newGrid?.grid) if ( oldGrid?.title !== newGrid?.title || diff --git a/domains/grid/utils/configToLayout.ts b/domains/grid/utils/configToGrid.ts similarity index 81% rename from domains/grid/utils/configToLayout.ts rename to domains/grid/utils/configToGrid.ts index 9f7f43a6..8523665b 100644 --- a/domains/grid/utils/configToLayout.ts +++ b/domains/grid/utils/configToGrid.ts @@ -6,14 +6,14 @@ import type { GridConfigItem } from '@/types/grid' * @param config * @param columns */ -export const configToLayout = ( +export const configToGrid = ( config: PartialBy, 'id'>[] ): Grid[] => { - const layout: Grid[] = [] + const grid: Grid[] = [] for (const gridItem of config) { - layout.push({ - id: createGridId(gridItem, layout), + grid.push({ + id: createGridId(gridItem, grid), title: gridItem.title, grid: gridItem.grid.map(widget => { return createWidgetObject({ @@ -27,7 +27,7 @@ export const configToLayout = ( }) } - return layout + return grid } /** diff --git a/domains/grid/utils/defaultGridConfig.ts b/domains/grid/utils/defaultGridConfig.ts index 04f281db..2026b705 100644 --- a/domains/grid/utils/defaultGridConfig.ts +++ b/domains/grid/utils/defaultGridConfig.ts @@ -23,7 +23,7 @@ export const defaultGridConfig = ( height: 1, properties: { title: 'Hey', - text: 'Customize your grid layout!', + text: 'Customize your grid!', backgroundColor: '#9db9b9', }, }, diff --git a/domains/grid/utils/findBestPosition.ts b/domains/grid/utils/findBestPosition.ts new file mode 100644 index 00000000..9b5aff97 --- /dev/null +++ b/domains/grid/utils/findBestPosition.ts @@ -0,0 +1,28 @@ +/** + * Find the best position for a widget in the grid + * + * @param widget + * @param columnHeights + * @param gridColumns + */ +export const findBestPosition = ( + widget: GridWidgetWithoutCords, + columnHeights: number[], + gridColumns: number +): { x: number; y: number } => { + let bestY = Number.MAX_SAFE_INTEGER + let bestX = 0 + + // Iterate to strictly find left-to-right placement + for (let x = 0; x <= gridColumns - widget.w; x++) { + const maxY = Math.max(...columnHeights.slice(x, x + widget.w)) + + // Find the earliest leftmost column available + if (maxY < bestY || (maxY === bestY && x < bestX)) { + bestY = maxY + bestX = x + } + } + + return { x: bestX, y: bestY } +} diff --git a/domains/grid/utils/getColumnHeightsFromGrid.ts b/domains/grid/utils/getColumnHeightsFromGrid.ts new file mode 100644 index 00000000..aa8b94d2 --- /dev/null +++ b/domains/grid/utils/getColumnHeightsFromGrid.ts @@ -0,0 +1,29 @@ +/** + * Get the heights of each column in a grid + * + * @param grid + * @param gridColumns + */ +export const getColumnHeightsFromGrid = ( + grid: GridWidget[], + gridColumns: number +): number[] => { + const columnHeights = Array(gridColumns).fill(0) + + // Filter widgets that impact column heights most + const sortedWidgets = grid.slice().sort((a, b) => b.y + b.h - (a.y + a.h)) + + // Iterate through the sorted widgets until all columns are covered + for (const widget of sortedWidgets) { + for (let x = widget.x; x < widget.x + widget.w; x++) { + if (columnHeights[x] < widget.y + widget.h) { + columnHeights[x] = widget.y + widget.h + } + } + + // Early exit if all columns have been covered + if (Math.min(...columnHeights) > 0) break + } + + return columnHeights +} diff --git a/domains/grid/utils/getUserGrid.ts b/domains/grid/utils/getUserGrid.ts new file mode 100644 index 00000000..a707b79d --- /dev/null +++ b/domains/grid/utils/getUserGrid.ts @@ -0,0 +1,24 @@ +/** + * Get grid for a given user address + * + * @param address + */ +export const getUserGrid = async ( + address: Address +): Promise[]> => { + let config: PartialBy, 'id'>[] + const userConfig = await getGridConfig(address) + + // if user config is invalid we load default one + if (isConfigValid(userConfig)) { + config = userConfig as Grid[] + } else { + if (gridLog.enabled) { + gridLog('Invalid config', userConfig) + } + + config = defaultGridConfig(address) + } + + return configToGrid(config) +} diff --git a/domains/grid/utils/gridLayout.ts b/domains/grid/utils/gridLayout.ts deleted file mode 100644 index c22f73f7..00000000 --- a/domains/grid/utils/gridLayout.ts +++ /dev/null @@ -1,108 +0,0 @@ -export const placeWidgetInLayout = ( - widget: GridWidgetWithoutCords, - layout: GridWidget[], - gridColumns: number -): void => { - const columnHeights = getColumnHeightsFromLayout(layout, gridColumns) - const w = Math.min(widget.w, gridColumns) - - const { x, y } = findBestPosition( - { ...widget, w }, - columnHeights, - gridColumns - ) - - const newWidget: GridWidget = { - ...widget, - x, - y, - w, - originalWidth: w < widget.w ? widget.w : undefined, - } - - layout.push(newWidget) -} - -export const findBestPosition = ( - widget: GridWidgetWithoutCords, - columnHeights: number[], - gridColumns: number -): { x: number; y: number } => { - let bestY = Number.MAX_SAFE_INTEGER - let bestX = 0 - - // Iterate to strictly find left-to-right placement - for (let x = 0; x <= gridColumns - widget.w; x++) { - const maxY = Math.max(...columnHeights.slice(x, x + widget.w)) - - // Find the earliest leftmost column available - if (maxY < bestY || (maxY === bestY && x < bestX)) { - bestY = maxY - bestX = x - } - } - - return { x: bestX, y: bestY } -} - -export const updateColumnHeights = ( - columnHeights: number[], - x: number, - width: number, - newHeight: number -): void => { - for (let i = x; i < x + width; i++) { - columnHeights[i] = newHeight - } -} - -export const getColumnHeightsFromLayout = ( - layout: GridWidget[], - gridColumns: number -): number[] => { - const columnHeights = Array(gridColumns).fill(0) - - // Filter widgets that impact column heights most - const sortedWidgets = layout.slice().sort((a, b) => b.y + b.h - (a.y + a.h)) - - // Iterate through the sorted widgets until all columns are covered - for (const widget of sortedWidgets) { - for (let x = widget.x; x < widget.x + widget.w; x++) { - if (columnHeights[x] < widget.y + widget.h) { - columnHeights[x] = widget.y + widget.h - } - } - - // Early exit if all columns have been covered - if (Math.min(...columnHeights) > 0) break - } - - return columnHeights -} - -/** - * Get grid layout for a given user address and grid column number - * - * @param address - * @param columns - * @returns - */ -export const getUserLayout = async ( - address: Address -): Promise[]> => { - let config: PartialBy, 'id'>[] - const userConfig = await getGridConfig(address) - - // if user config is invalid we load default one - if (isConfigValid(userConfig)) { - config = userConfig as Grid[] - } else { - if (gridLog.enabled) { - gridLog('Invalid config', userConfig) - } - - config = defaultGridConfig(address) - } - - return configToLayout(config) -} diff --git a/domains/grid/utils/gridParser.ts b/domains/grid/utils/gridParser.ts index c90c9a40..d4461480 100644 --- a/domains/grid/utils/gridParser.ts +++ b/domains/grid/utils/gridParser.ts @@ -1,7 +1,7 @@ export const parsePlatformInput = async ( platform: GridWidgetType, input: string -): Promise => { +): Promise => { switch (platform) { case GRID_WIDGET_TYPE.X: return parseXWidgetInput(input) @@ -18,7 +18,7 @@ export const parsePlatformInput = async ( } } -const parseYoutubeWidgetInput = (input: string): LayoutItemExtended | never => { +const parseYoutubeWidgetInput = (input: string): GridWidgetExtended | never => { const YOUTUBE_URL_REGEX = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/ const YOUTUBE_EMBED_REGEX = @@ -53,7 +53,7 @@ const parseYoutubeWidgetInput = (input: string): LayoutItemExtended | never => { throw new Error('Invalid YouTube input') } -const parseXWidgetInput = (input: string): LayoutItemExtended | never => { +const parseXWidgetInput = (input: string): GridWidgetExtended | never => { const X_POST_REGEX = /https?:\/\/(?:www\.)?(?:x\.com|twitter\.com)\/(\w+)\/status\/(\d+)(?:\?ref_src=twsrc%5Etfw)?/ const X_TIMELINE_REGEX = @@ -86,7 +86,7 @@ const parseXWidgetInput = (input: string): LayoutItemExtended | never => { throw new Error('Invalid X input') } -const parseSpotifyWidgetInput = (input: string): LayoutItemExtended | never => { +const parseSpotifyWidgetInput = (input: string): GridWidgetExtended | never => { const SPOTIFY_URL_REGEX = /https?:\/\/(?:open\.)?spotify\.com\/(?:embed\/)?(?track|playlist|artist)\/(?[^?]+)(?:\?utm_source=generator)?(?:&theme=(?\d))?/ const SPOTIFY_IFRAME_ALLOW = @@ -113,7 +113,7 @@ const parseSpotifyWidgetInput = (input: string): LayoutItemExtended | never => { const parseSoundCloudWidgetInput = async ( input: string -): Promise => { +): Promise => { const SOUNDCLOUD_SHARE_URL_REGEX = /https:\/\/soundcloud\.com\/([a-zA-Z0-9_-]+)(?:\/(sets\/[a-zA-Z0-9_-]+|[a-zA-Z0-9_-]+))?\/?/ @@ -137,7 +137,7 @@ const parseSoundCloudWidgetInput = async ( const parseSoundCloudWidgetInputFromEmbed = ( input: string -): LayoutItemExtended | never => { +): GridWidgetExtended | never => { const SOUNDCLOUD_EMBED_URL_REGEX = /https?:\/\/w\.soundcloud\.com\/player\/\?(?:(?!url=https).)*url=https(?::|%3A)(?:\/|%2F){2}api\.soundcloud\.com(?:\/|%2F)(tracks|playlists|users)(?:\/|%2F)\d+(?:[^"]*)?/ const SOUNDCLOUD_IFRAME_ALLOW = @@ -178,7 +178,7 @@ const getSoundCloudEmbedUrl = async ( const parseInstagramWidgetInput = ( input: string -): LayoutItemExtended | never => { +): GridWidgetExtended | never => { const INSTAGRAM_URL_REGEX = /https:\/\/www\.instagram\.com\/(p|reel|profile|tv)\/([\w-]+)\/(\?[^"]*)?/ diff --git a/domains/grid/utils/layoutToConfig.ts b/domains/grid/utils/gridToConfig.ts similarity index 54% rename from domains/grid/utils/layoutToConfig.ts rename to domains/grid/utils/gridToConfig.ts index b3d87d42..e6ba8782 100644 --- a/domains/grid/utils/layoutToConfig.ts +++ b/domains/grid/utils/gridToConfig.ts @@ -1,20 +1,19 @@ /** - * Convert grid layout to LSP27 config + * Convert grid to LSP27 config * - * @param layout - * @returns + * @param _grid */ -export const layoutToConfig = ( - _layout: Grid[] +export const gridToConfig = ( + _grid: Grid[] ): PartialBy, 'id'>[] => { - const convertGrid = (layout: GridWidget[]): GridConfigItem[] => { - // remove "add content" widget from layout before saving - const layoutWithoutAddContentWidget = layout.filter( + const convertGrid = (grid: GridWidget[]): GridConfigItem[] => { + // remove "add content" widget from grid before saving + const gridWithoutAddContentWidget = grid.filter( item => item.type !== GRID_WIDGET_TYPE.ADD_CONTENT ) // sort by y and then x to get the correct order - const orderedLayout = layoutWithoutAddContentWidget.sort((a, b) => { + const orderedGrid = gridWithoutAddContentWidget.sort((a, b) => { if (a.y === b.y) { return a.x - b.x } @@ -22,7 +21,7 @@ export const layoutToConfig = ( return a.y - b.y }) - return orderedLayout.map(item => { + return orderedGrid.map(item => { return { type: item.type, width: item.originalWidth || item.w, @@ -32,7 +31,7 @@ export const layoutToConfig = ( }) } - return _layout.map(item => { + return _grid.map(item => { return { title: item.title, grid: convertGrid(item.grid), diff --git a/domains/grid/utils/placeWidgetInGrid.ts b/domains/grid/utils/placeWidgetInGrid.ts new file mode 100644 index 00000000..01043e0a --- /dev/null +++ b/domains/grid/utils/placeWidgetInGrid.ts @@ -0,0 +1,31 @@ +/** + * Place a widget in a grid + * + * @param widget + * @param grid + * @param gridColumns + */ +export const placeWidgetInGrid = ( + widget: GridWidgetWithoutCords, + grid: GridWidget[], + gridColumns: number +): void => { + const columnHeights = getColumnHeightsFromGrid(grid, gridColumns) + const w = Math.min(widget.w, gridColumns) + + const { x, y } = findBestPosition( + { ...widget, w }, + columnHeights, + gridColumns + ) + + const newWidget: GridWidget = { + ...widget, + x, + y, + w, + originalWidth: w < widget.w ? widget.w : undefined, + } + + grid.push(newWidget) +} diff --git a/domains/grid/utils/updateColumnHeights.ts b/domains/grid/utils/updateColumnHeights.ts new file mode 100644 index 00000000..86f8de53 --- /dev/null +++ b/domains/grid/utils/updateColumnHeights.ts @@ -0,0 +1,18 @@ +/** + * Update the column heights array with the new height + * + * @param columnHeights + * @param x + * @param width + * @param newHeight + */ +export const updateColumnHeights = ( + columnHeights: number[], + x: number, + width: number, + newHeight: number +): void => { + for (let i = x; i < x + width; i++) { + columnHeights[i] = newHeight + } +} diff --git a/stores/grid.ts b/stores/grid.ts index 7660dc73..ac24b9f0 100644 --- a/stores/grid.ts +++ b/stores/grid.ts @@ -3,20 +3,20 @@ export const useGridStore = defineStore( () => { const isEditingGrid = ref(false) const hasUnsavedGrid = ref(false) - const viewedGridLayout = ref[]>([]) - const tempGridLayout = ref[]>([]) + const viewedGrid = ref[]>([]) + const tempGrid = ref[]>([]) const isSavingGrid = ref(false) - const selectedLayoutId = ref() + const selectedGridId = ref() const gridRowHeightRatio = ref(DEFAULT_GRID_ROW_HEIGHT_RATIO) const gridChainId = ref(DEFAULT_NETWORK_CHAIN_ID) return { isEditingGrid, hasUnsavedGrid, - viewedGridLayout, - tempGridLayout, + viewedGrid, + tempGrid, isSavingGrid, - selectedLayoutId, + selectedGridId, gridRowHeightRatio, gridChainId, } @@ -26,10 +26,10 @@ export const useGridStore = defineStore( paths: [ 'isEditingGrid', 'hasUnsavedGrid', - 'selectedLayoutId', + 'selectedGridId', 'gridRowHeightRatio', 'gridChainId', - 'tempGridLayout', + 'tempGrid', ], key: STORAGE_KEY.GRID_STORE, }, diff --git a/types/grid.ts b/types/grid.ts index b349f327..23701d73 100644 --- a/types/grid.ts +++ b/types/grid.ts @@ -14,13 +14,13 @@ export type GridConfigItem = { properties: GridWidgetProperties } -export type LayoutItemExtended = { +export type GridWidgetExtended = { type: GridWidgetType properties: GridWidgetProperties originalWidth?: number } -export type GridWidget = LayoutItem & LayoutItemExtended +export type GridWidget = LayoutItem & GridWidgetExtended export type GridWidgetWithoutCords = PartialBy From ffcd631e521290f7aaef0f47d56dac09c2fe4ab1 Mon Sep 17 00:00:00 2001 From: Dominik Zborowski Date: Fri, 18 Oct 2024 17:26:07 +0200 Subject: [PATCH 3/9] Hide tabs --- domains/grid/components/GridTabs.vue | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/domains/grid/components/GridTabs.vue b/domains/grid/components/GridTabs.vue index 4a3602f4..0a4c582e 100644 --- a/domains/grid/components/GridTabs.vue +++ b/domains/grid/components/GridTabs.vue @@ -14,13 +14,21 @@ const { showModal } = useModal() const { canEditGrid } = useGrid() const tabs = computed(() => { - return props.grid.map(grid => { - return { - grid, - } - }) + return ( + props.grid + // filter out empty grids + .filter(grid => grid.grid.length > 0) + .map(grid => { + return { + grid, + } + }) + ) }) +// we only show tabs when user has more then one +const hasTabs = computed(() => tabs.value.length > 1) + const handleAddGrid = () => { showModal({ template: 'AddEditGrid', @@ -34,7 +42,7 @@ const handleAddGrid = () => {