From 6692dd44498b84b5feff288f217d81f1f76f7290 Mon Sep 17 00:00:00 2001 From: Andrea Vesterhus Date: Wed, 10 Jan 2024 09:50:38 +0100 Subject: [PATCH 1/4] feat: add additional config to DataGridPlugin --- .../data_grid/DataGridPluginConfig.json | 93 ++++++++++++++++++- .../dm-core-plugins/src/data-grid/types.ts | 46 +++++++++ 2 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 packages/dm-core-plugins/src/data-grid/types.ts diff --git a/packages/dm-core-plugins/blueprints/data_grid/DataGridPluginConfig.json b/packages/dm-core-plugins/blueprints/data_grid/DataGridPluginConfig.json index 70803d003..c157046f1 100644 --- a/packages/dm-core-plugins/blueprints/data_grid/DataGridPluginConfig.json +++ b/packages/dm-core-plugins/blueprints/data_grid/DataGridPluginConfig.json @@ -8,19 +8,94 @@ "attributeType": "string" }, { - "name": "fieldName", + "name": "title", "type": "CORE:BlueprintAttribute", - "attributeType": "string" + "attributeType": "string", + "optional": true, + "default": "" }, { - "name": "columns", + "name": "description", "type": "CORE:BlueprintAttribute", - "description": "Name of primitive child attributes to display in header of item", "attributeType": "string", "optional": true, - "default": [], + "default": "" + }, + { + "name": "fieldNames", + "type": "CORE:BlueprintAttribute", + "description": "If more than one field is selected, the arrays have to be 1-dimensional.", + "attributeType": "string", "dimensions": "*" }, + { + "name": "editable", + "type": "CORE:BlueprintAttribute", + "attributeType": "boolean", + "optional": true, + "default": true + }, + { + "name": "showColumns", + "type": "CORE:BlueprintAttribute", + "attributeType": "boolean", + "optional": true, + "default": true + }, + { + "name": "adjustableColumns", + "type": "CORE:BlueprintAttribute", + "attributeType": "boolean", + "optional": true, + "default": true + }, + { + "name": "columnLabels", + "type": "CORE:BlueprintAttribute", + "description": "Define column labels or use predefined columns. Predefined values are ABC, ZYX, 123 and you can select those by using the spread operator ['...ABC']. You can also merge the two like this: ['custom field', 'custom field2', '...ABC']", + "attributeType": "string", + "optional": true, + "default": ["...ABC"], + "dimensions": "*" + }, + { + "name": "showRows", + "type": "CORE:BlueprintAttribute", + "attributeType": "boolean", + "optional": true, + "default": true + }, + { + "name": "adjustableRows", + "type": "CORE:BlueprintAttribute", + "attributeType": "boolean", + "optional": true, + "default": true + }, + { + "name": "movableRows", + "type": "CORE:BlueprintAttribute", + "attributeType": "boolean", + "optional": true, + "default": true + }, + { + "name": "rowLabels", + "type": "CORE:BlueprintAttribute", + "description": "Define row labels or use predefined rows. Predefined values are ABC, ZYX, 123and you can select those by using the spread operator ['...ABC']. You can also merge the two like this: ['custom field', 'custom field2', '...ABC']", + "attributeType": "string", + "optional": true, + "default": ["...123"], + "dimensions": "*" + }, + { + "name": "printDirection", + "type": "CORE:BlueprintAttribute", + "description": "Which direction should rows be printed. Horizontal means printing rows top-down, vertical means left-right.", + "attributeType": "string", + "optional": true, + "default": "horizontal" + }, { "name": "rowsPerPage", "type": "CORE:BlueprintAttribute", @@ -28,6 +103,14 @@ "attributeType": "number", "optional": true, "default": 25 + }, + { + "name": "hidePaginationIfLessThan", + "type": "CORE:BlueprintAttribute", + "description": "Hide the pagination if rows are less than x values.", + "attributeType": "number", + "optional": true, + "default": 0 } ] } diff --git a/packages/dm-core-plugins/src/data-grid/types.ts b/packages/dm-core-plugins/src/data-grid/types.ts new file mode 100644 index 000000000..e4946aed8 --- /dev/null +++ b/packages/dm-core-plugins/src/data-grid/types.ts @@ -0,0 +1,46 @@ +export type DataGridConfig = { + title: string + description: string + fieldNames: string[] + editable: boolean + showColumns: boolean + adjustableColumns: boolean + columnLabels: string[] + showRows: boolean + adjustableRows: boolean + rowLabels: string[] + movableRows: boolean + printDirection: 'horizontal' | 'vertical' + rowsPerPage: number + hidePaginationIfLessThan: number +} + +export const defaultConfig: DataGridConfig = { + title: '', + description: '', + fieldNames: [], + editable: true, + showColumns: true, + adjustableColumns: true, + columnLabels: ['...ABC'], + showRows: true, + adjustableRows: true, + rowLabels: ['...123'], + movableRows: true, + printDirection: 'horizontal', + rowsPerPage: 25, + hidePaginationIfLessThan: 0, +} + +export type DataGridProps = { + attributeType: string + config?: DataGridConfig + data: any[] + description?: string + dimensions?: string + initialRowsPerPage?: number + setData: (data: any[]) => void + title?: string +} + +export type PredefinedLabels = '...ABC' | '...ZYX' | '...123' From 9c9d3b724ad99d76906f0bf4f1e395b68c8d6163 Mon Sep 17 00:00:00 2001 From: Andrea Vesterhus Date: Wed, 10 Jan 2024 09:52:57 +0100 Subject: [PATCH 2/4] refactor: deprecated multiType to default example --- .../blueprints/Default.blueprint.json} | 8 +- .../data_grid/default/default.entity.json | 19 +++ .../multi_type/multiType.entity.json | 11 -- .../data_grid/default/default.recipe.json | 158 ++++++++++++++++++ .../multi_type/multiType.recipe.json | 31 ---- 5 files changed, 184 insertions(+), 43 deletions(-) rename example/app/data/DemoDataSource/plugins/data_grid/{multi_type/blueprints/MultiType.blueprint.json => default/blueprints/Default.blueprint.json} (72%) create mode 100644 example/app/data/DemoDataSource/plugins/data_grid/default/default.entity.json delete mode 100644 example/app/data/DemoDataSource/plugins/data_grid/multi_type/multiType.entity.json create mode 100644 example/app/data/DemoDataSource/recipes/plugins/data_grid/default/default.recipe.json delete mode 100644 example/app/data/DemoDataSource/recipes/plugins/data_grid/multi_type/multiType.recipe.json diff --git a/example/app/data/DemoDataSource/plugins/data_grid/multi_type/blueprints/MultiType.blueprint.json b/example/app/data/DemoDataSource/plugins/data_grid/default/blueprints/Default.blueprint.json similarity index 72% rename from example/app/data/DemoDataSource/plugins/data_grid/multi_type/blueprints/MultiType.blueprint.json rename to example/app/data/DemoDataSource/plugins/data_grid/default/blueprints/Default.blueprint.json index b253793ff..2dd0f4727 100644 --- a/example/app/data/DemoDataSource/plugins/data_grid/multi_type/blueprints/MultiType.blueprint.json +++ b/example/app/data/DemoDataSource/plugins/data_grid/default/blueprints/Default.blueprint.json @@ -1,5 +1,5 @@ { - "name": "MultiType", + "name": "Default", "type": "CORE:Blueprint", "attributes": [ { @@ -17,6 +17,12 @@ "type": "CORE:BlueprintAttribute", "attributeType": "string", "dimensions": "*,*" + }, + { + "name": "dimensional", + "type": "CORE:BlueprintAttribute", + "attributeType": "string", + "dimensions": "4,5" } ] } diff --git a/example/app/data/DemoDataSource/plugins/data_grid/default/default.entity.json b/example/app/data/DemoDataSource/plugins/data_grid/default/default.entity.json new file mode 100644 index 000000000..ef13b077a --- /dev/null +++ b/example/app/data/DemoDataSource/plugins/data_grid/default/default.entity.json @@ -0,0 +1,19 @@ +{ + "_id": "Default", + "type": "./blueprints/Default", + "name": "Default", + "data": [ + ["Dodge", "Bentley Model T", "Extended Cab Pickup", "1D3MX48D48B28FPJU"], + ["Volvo", "Volvo Camry", "Cargo Van", "5XYZGDAG8BDE8J42H"], + ["Lamborghini", "Smart ATS", "Sedan", "3FTEW31R691XKD94Y"], + ["Land Rover", "Mazda Countach", "Crew Cab Pickup", "3GTP2WE3XBUUMTGXS"], + ["Ford", "Jeep Spyder", "Wagon", "1G1ZS51F37ETTZW4Y"] + ], + "dimensional": [ + ["Dodge", "Bentley Model T", "Extended Cab Pickup", "1D3MX48D48B28FPJU"], + ["Volvo", "Volvo Camry", "Cargo Van", "5XYZGDAG8BDE8J42H"], + ["Lamborghini", "Smart ATS", "Sedan", "3FTEW31R691XKD94Y"], + ["Land Rover", "Mazda Countach", "Crew Cab Pickup", "3GTP2WE3XBUUMTGXS"], + ["Ford", "Jeep Spyder", "Wagon", "1G1ZS51F37ETTZW4Y"] + ] +} diff --git a/example/app/data/DemoDataSource/plugins/data_grid/multi_type/multiType.entity.json b/example/app/data/DemoDataSource/plugins/data_grid/multi_type/multiType.entity.json deleted file mode 100644 index 7eed6013d..000000000 --- a/example/app/data/DemoDataSource/plugins/data_grid/multi_type/multiType.entity.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "_id": "MultiType", - "type": "./blueprints/MultiType", - "name": "MultiType", - "data": [ - ["MooringLine1", "type1", "anch0", "fair0", "4367.5", "3247.5", "true"], - ["MooringLine2", "type2", "anch1", "fair1", "4438.3", "3848.3", "true"], - ["MooringLine3", "type3", "anch2", "fair2", "4585.0", "3545.0", "true"], - ["MooringLine4", "type4", "anch3", "fair3", "4399.6", "3149.6", "false"] - ] -} diff --git a/example/app/data/DemoDataSource/recipes/plugins/data_grid/default/default.recipe.json b/example/app/data/DemoDataSource/recipes/plugins/data_grid/default/default.recipe.json new file mode 100644 index 000000000..b920e6bb8 --- /dev/null +++ b/example/app/data/DemoDataSource/recipes/plugins/data_grid/default/default.recipe.json @@ -0,0 +1,158 @@ +{ + "type": "CORE:RecipeLink", + "_blueprintPath_": "/plugins/data_grid/default/blueprints/Default", + "initialUiRecipe": { + "name": "ViewSelector", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/view_selector/tabs", + "config": { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorConfig", + "items": [ + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Default", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Default", + "type": "CORE:UiRecipe", + "description": "Single multidimensional primitive", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "fieldNames": ["data"] + } + }, + "scope": "self" + } + }, + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Set dimensions", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Set Dimensions", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "fieldNames": ["dimensional"], + "title": "Datagrid with set dimensions", + "description": "Dimensions are set to 4,5" + } + }, + "scope": "self" + } + }, + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Custom labels", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Custom labels", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "fieldNames": ["dimensional"], + "title": "Datagrid with custom labels", + "description": "Set dimensions: 4,5. Custom column and row labels.", + "columnLabels": ["Manufacturer", "Name", "Type", "VIN"], + "rowLabels": ["John", "Joe", "Jason", "Jack", "Jay"] + } + }, + "scope": "self" + } + }, + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Combined labels", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Combined labels", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "fieldNames": ["dimensional"], + "title": "Datagrid with combined labels", + "description": "Set dimensions: 4,5. Combined custom and pre-defined column and row labels.", + "columnLabels": ["Manufacturer", "Name", "Type", "VIN"], + "rowLabels": ["John", "...123"] + } + }, + "scope": "self" + } + }, + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Hidden labels", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Hidden labels", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "fieldNames": ["dimensional"], + "title": "Hidden labels", + "editable": false, + "description": "Hide labels for rows and columns. Set dimensions: 4,5.", + "showColumns": false, + "showRows": false + } + }, + "scope": "self" + } + }, + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "No-edit columns and rows", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "No-edit columns and rows", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "fieldNames": ["data"], + "title": "No-edit columns and rows", + "description": "No-edit columns and rows. No set dimensions.", + "adjustableColumns": false, + "adjustableRows": false + } + }, + "scope": "self" + } + }, + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Non-editable", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Non-editable", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "fieldNames": ["data"], + "title": "Non-editable datagrid", + "editable": false, + "description": "Non-editable. Unknown dimensions. Custom column and row labels.", + "columnLabels": ["Manufacturer", "Name", "Type", "VIN"], + "rowLabels": ["John", "Joe", "Jason", "Jack", "Jay"] + } + }, + "scope": "self" + } + } + ] + } + } +} diff --git a/example/app/data/DemoDataSource/recipes/plugins/data_grid/multi_type/multiType.recipe.json b/example/app/data/DemoDataSource/recipes/plugins/data_grid/multi_type/multiType.recipe.json deleted file mode 100644 index 872922a83..000000000 --- a/example/app/data/DemoDataSource/recipes/plugins/data_grid/multi_type/multiType.recipe.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "type": "CORE:RecipeLink", - "_blueprintPath_": "/plugins/data_grid/multi_type/blueprints/MultiType", - "initialUiRecipe": { - "name": "ViewSelector", - "type": "CORE:UiRecipe", - "plugin": "@development-framework/dm-core-plugins/view_selector/tabs", - "config": { - "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorConfig", - "items": [ - { - "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", - "viewConfig": { - "type": "CORE:InlineRecipeViewConfig", - "recipe": { - "name": "multi_type", - "type": "CORE:UiRecipe", - "description": "Multitype datagrid", - "plugin": "@development-framework/dm-core-plugins/data_grid", - "config": { - "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", - "fieldName": "data" - } - }, - "scope": "self" - } - } - ] - } - } -} From 5a09fd83393ac51f444d195ca8ad32bd54acc029 Mon Sep 17 00:00:00 2001 From: Andrea Vesterhus Date: Wed, 10 Jan 2024 09:53:43 +0100 Subject: [PATCH 3/4] feat: add example for multiple primitives --- .../MultiplePrimitives.blueprint.json | 40 ++++++++++++++ .../multiplePrimitives.entity.json | 27 +++++++++ .../multiplePrimitives.recipe.json | 55 +++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/blueprints/MultiplePrimitives.blueprint.json create mode 100644 example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/multiplePrimitives.entity.json create mode 100644 example/app/data/DemoDataSource/recipes/plugins/data_grid/multiple_primitives/multiplePrimitives.recipe.json diff --git a/example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/blueprints/MultiplePrimitives.blueprint.json b/example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/blueprints/MultiplePrimitives.blueprint.json new file mode 100644 index 000000000..3a1a80948 --- /dev/null +++ b/example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/blueprints/MultiplePrimitives.blueprint.json @@ -0,0 +1,40 @@ +{ + "name": "MultiplePrimitives", + "type": "CORE:Blueprint", + "attributes": [ + { + "name": "type", + "type": "dmss://system/SIMOS/BlueprintAttribute", + "attributeType": "string" + }, + { + "name": "name", + "type": "dmss://system/SIMOS/BlueprintAttribute", + "attributeType": "string" + }, + { + "name": "manufacturer", + "type": "CORE:BlueprintAttribute", + "attributeType": "string", + "dimensions": "*" + }, + { + "name": "car_name", + "type": "CORE:BlueprintAttribute", + "attributeType": "string", + "dimensions": "*" + }, + { + "name": "model", + "type": "CORE:BlueprintAttribute", + "attributeType": "string", + "dimensions": "*" + }, + { + "name": "vin", + "type": "CORE:BlueprintAttribute", + "attributeType": "string", + "dimensions": "*" + } + ] +} diff --git a/example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/multiplePrimitives.entity.json b/example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/multiplePrimitives.entity.json new file mode 100644 index 000000000..a7268c5ff --- /dev/null +++ b/example/app/data/DemoDataSource/plugins/data_grid/multiple_primitives/multiplePrimitives.entity.json @@ -0,0 +1,27 @@ +{ + "_id": "MultiplePrimitives", + "type": "./blueprints/MultiplePrimitives", + "name": "MultiplePrimitives", + "manufacturer": ["Dodge", "Volvo", "Lamborghini", "Land Rover", "Ford"], + "car_name": [ + "Bentley Model T", + "Volvo Camry", + "Smart ATS", + "Mazda Countach", + "Jeep Spyder" + ], + "model": [ + "Extended Cab Pickup", + "Cargo Van", + "Sedan", + "Crew Cab Pickup", + "Wagon" + ], + "vin": [ + "1D3MX48D48B28FPJU", + "5XYZGDAG8BDE8J42H", + "3FTEW31R691XKD94Y", + "3GTP2WE3XBUUMTGXS", + "1G1ZS51F37ETTZW4Y" + ] +} diff --git a/example/app/data/DemoDataSource/recipes/plugins/data_grid/multiple_primitives/multiplePrimitives.recipe.json b/example/app/data/DemoDataSource/recipes/plugins/data_grid/multiple_primitives/multiplePrimitives.recipe.json new file mode 100644 index 000000000..afab204d1 --- /dev/null +++ b/example/app/data/DemoDataSource/recipes/plugins/data_grid/multiple_primitives/multiplePrimitives.recipe.json @@ -0,0 +1,55 @@ +{ + "type": "CORE:RecipeLink", + "_blueprintPath_": "/plugins/data_grid/multiple_primitives/blueprints/MultiplePrimitives", + "initialUiRecipe": { + "name": "ViewSelector", + "type": "CORE:UiRecipe", + "plugin": "@development-framework/dm-core-plugins/view_selector/tabs", + "config": { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorConfig", + "items": [ + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Multiple (default)", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Multiple Primitives", + "type": "CORE:UiRecipe", + "description": "Multiple primitives combined in datagrid", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "rowLabels": ["Manufacturer", "Name", "Type", "VIN"], + "fieldNames": ["manufacturer", "car_name", "model", "vin"], + "title": "Multiple primitives datagrid" + } + }, + "scope": "self" + } + }, + { + "type": "PLUGINS:dm-core-plugins/view_selector/ViewSelectorItem", + "label": "Vertical printing", + "viewConfig": { + "type": "CORE:InlineRecipeViewConfig", + "recipe": { + "name": "Vertical printing", + "type": "CORE:UiRecipe", + "description": "Printdirection: vertical .Multiple primitives combined in datagrid", + "plugin": "@development-framework/dm-core-plugins/data_grid", + "config": { + "type": "PLUGINS:dm-core-plugins/data_grid/DataGridPluginConfig", + "rowLabels": ["Manufacturer", "Name", "Type", "VIN"], + "fieldNames": ["manufacturer", "car_name", "model", "vin"], + "title": "Vertically printed datagrid", + "printDirection": "vertical" + } + }, + "scope": "self" + } + } + ] + } + } +} From 14cd2ce1db832d3d8611737dbc4a70eaa2f98e4e Mon Sep 17 00:00:00 2001 From: Andrea Vesterhus Date: Wed, 10 Jan 2024 09:59:40 +0100 Subject: [PATCH 4/4] feat: make datagridplugin and component support additional config --- .../data-grid/ColumnHeader/ColumnHeader.tsx | 108 -------- .../src/data-grid/DataCell/DataCell.tsx | 15 +- .../src/data-grid/DataGrid.tsx | 248 +++++++++++------- .../src/data-grid/DataGridPlugin.tsx | 49 +++- .../src/data-grid/HeaderCell/HeaderCell.tsx | 89 +++++++ .../VerticalHeader/VerticalHeader.tsx | 83 ------ .../dm-core-plugins/src/data-grid/styles.ts | 122 ++++++--- .../dm-core-plugins/src/data-grid/utils.ts | 95 ++++++- 8 files changed, 464 insertions(+), 345 deletions(-) delete mode 100644 packages/dm-core-plugins/src/data-grid/ColumnHeader/ColumnHeader.tsx create mode 100644 packages/dm-core-plugins/src/data-grid/HeaderCell/HeaderCell.tsx delete mode 100644 packages/dm-core-plugins/src/data-grid/VerticalHeader/VerticalHeader.tsx diff --git a/packages/dm-core-plugins/src/data-grid/ColumnHeader/ColumnHeader.tsx b/packages/dm-core-plugins/src/data-grid/ColumnHeader/ColumnHeader.tsx deleted file mode 100644 index 7d1003ee8..000000000 --- a/packages/dm-core-plugins/src/data-grid/ColumnHeader/ColumnHeader.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React, { useState } from 'react' -import { Icon, Menu, Typography } from '@equinor/eds-core-react' -import * as Styled from '../styles' -import * as utils from '../utils' -import { add, delete_to_trash } from '@equinor/eds-icons' -import { TAttribute } from '@development-framework/dm-core' - -type ColumnHeaderProps = { - attributeType: string - column: any - columns: number[] - columnsAreSetDimension: boolean - data: any[] - deleteColumn: (index: number) => void - index: number - multi: boolean - selectedColumn: number | undefined - setColumns: React.Dispatch> - setData: (data: any[]) => void - setSelectedColumn: React.Dispatch> -} - -export function ColumnHeader(props: ColumnHeaderProps) { - const { - attributeType, - column, - columns, - data, - index, - multi, - selectedColumn, - setColumns, - setData, - setSelectedColumn, - } = props - const [isMenuOpen, setIsMenuOpen] = useState(false) - const [menuButtonAnchor, setMenuButtonAnchor] = - useState(null) - - function changeSelectedColumn(index: number) { - if (index === selectedColumn) { - setSelectedColumn(undefined) - return - } - setSelectedColumn(index) - } - - function handleColumnRightClick(event: any) { - event.preventDefault() - setSelectedColumn(index) - setIsMenuOpen(true) - } - - function addColumn(placement: 'before' | 'after') { - const newIndex = placement === 'before' ? index : index + 1 - const newColumns = utils.createArrayFromNumber(columns.length + 1) - const fillValue = utils.getFillValue(attributeType) - const updatedData = data.map((item) => { - item.splice(newIndex, 0, fillValue) - return item - }) - setData(updatedData) - setColumns(newColumns) - setSelectedColumn(newIndex) - } - - function onContextMenuClose() { - setIsMenuOpen(false) - setSelectedColumn(undefined) - } - - return ( - - (multi ? changeSelectedColumn(index) : null)} - onContextMenu={handleColumnRightClick} - ref={setMenuButtonAnchor} - > - {utils.columnLabels[column - 1]} - - - {props.columnsAreSetDimension || !multi ? ( - - Columns are pre-defined and cannot be deleted or added. - - ) : ( - <> - props.deleteColumn(index)}> - Delete column - - addColumn('before')}> - Add 1 column left - - addColumn('after')}> - Add 1 column right - - - )} - - - ) -} diff --git a/packages/dm-core-plugins/src/data-grid/DataCell/DataCell.tsx b/packages/dm-core-plugins/src/data-grid/DataCell/DataCell.tsx index f8bd4e663..dcf682e71 100644 --- a/packages/dm-core-plugins/src/data-grid/DataCell/DataCell.tsx +++ b/packages/dm-core-plugins/src/data-grid/DataCell/DataCell.tsx @@ -1,19 +1,22 @@ import React, { ChangeEvent } from 'react' import * as Styled from '../styles' import { Checkbox } from '@equinor/eds-core-react' +import { DataGridConfig } from '../types' type DataCellProps = { - selected: boolean - rowIndex: number + attributeType: string cellIndex?: number - value: string | number | boolean + config: DataGridConfig data: any[] + rowIndex: number + selected: boolean setData: (data: any[]) => void - attributeType: string + value: string | number | boolean } export function DataCell(props: DataCellProps) { - const { attributeType, data, setData, rowIndex, value, cellIndex } = props + const { attributeType, data, setData, rowIndex, value, cellIndex, config } = + props function parseValue(event: ChangeEvent) { const { value, checked } = event.target @@ -47,12 +50,14 @@ export function DataCell(props: DataCellProps) { updateValue(event, rowIndex, cellIndex)} + readOnly={!config.editable} /> ) : ( updateValue(event, rowIndex, cellIndex)} attributeType={attributeType} + readOnly={!config.editable} /> )} diff --git a/packages/dm-core-plugins/src/data-grid/DataGrid.tsx b/packages/dm-core-plugins/src/data-grid/DataGrid.tsx index 6131bdd8f..15f31d16b 100644 --- a/packages/dm-core-plugins/src/data-grid/DataGrid.tsx +++ b/packages/dm-core-plugins/src/data-grid/DataGrid.tsx @@ -1,25 +1,19 @@ import React, { useState, useMemo, useEffect } from 'react' -import { Stack, TAttribute } from '@development-framework/dm-core' -import { Button, EdsProvider, Icon } from '@equinor/eds-core-react' +import { Stack } from '@development-framework/dm-core' +import { EdsProvider, Icon, Typography } from '@equinor/eds-core-react' import { add, chevron_down, chevron_up, minimize } from '@equinor/eds-icons' import * as Styled from './styles' import * as utils from './utils' import { DataCell } from './DataCell/DataCell' import { DataGridPagination } from './DataGridPagination/DataGridPagination' -import { ColumnHeader } from './ColumnHeader/ColumnHeader' -import { VerticalHeader } from './VerticalHeader/VerticalHeader' - -type DataGridProps = { - attributeType: string - dimensions?: string - data: any[] - setData: (data: any[]) => void - initialRowsPerPage?: number -} +import { HeaderCell } from './HeaderCell/HeaderCell' +import { DataGridConfig, DataGridProps, defaultConfig } from './types' export function DataGrid(props: DataGridProps) { - const { data, attributeType, dimensions, setData } = props - const [columns, setColumns] = useState([]) + const { data, attributeType, dimensions, setData, config: userConfig } = props + const config: DataGridConfig = { ...defaultConfig, ...userConfig } + const [columnLabels, setColumnLabels] = useState([]) + const [rowLabels, setRowLabels] = useState([]) const [selectedRow, setSelectedRow] = useState(undefined) const [selectedColumn, setSelectedColumn] = useState( undefined @@ -27,35 +21,49 @@ export function DataGrid(props: DataGridProps) { const [paginationPage, setPaginationPage] = useState(0) const [rowsPerPage, setRowsPerPage] = useState(props.initialRowsPerPage || 25) - const dataGridId = useMemo(() => crypto.randomUUID(), []) - const multi: boolean = dimensions?.includes(',') || false + const dataGridId: string = useMemo(() => crypto.randomUUID(), []) const fillValue = utils.getFillValue(attributeType) const paginatedRows = data.slice( paginationPage * rowsPerPage, paginationPage * rowsPerPage + rowsPerPage ) - const [definedColumns, definedRows] = dimensions?.split(',') || ['*', '*'] - const rowsAreSetDimension = multi - ? definedRows !== '*' - : definedColumns !== '*' + const [ + rowsAreEditable, + columnsAreEditable, + addButtonFunctionality, + addButtonIsEnabled, + isMultiDimensional, + columnDimensions, + isSortEnabled, + ] = utils.getFunctionalityVariables(config, dimensions) useEffect(() => { - const columnsArray = multi - ? definedColumns === '*' - ? utils.createArrayFromNumber(data.length > 0 ? data[0].length : []) - : utils.createArrayFromNumber(parseInt(definedColumns, 10)) - : [1] + const columnLabels = isMultiDimensional + ? columnDimensions === '*' + ? utils.createLabels( + config.columnLabels, + data.length > 0 ? data[0].length : 0 + ) + : utils.createLabels( + config.columnLabels, + parseInt(columnDimensions, 10) + ) + : ['1'] + const rowLabels = utils.createLabels(config.rowLabels, data?.length) - setColumns(columnsArray) + setRowLabels(rowLabels) + setColumnLabels(columnLabels) }, []) function addRow(newIndex?: number) { const newRow = - columns.length > 1 - ? Array.from({ length: columns.length }).fill(fillValue) + columnLabels.length > 1 + ? Array.from({ length: columnLabels.length }).fill(fillValue) : fillValue const dataCopy = [...data] - dataCopy.splice(newIndex || columns.length, 0, newRow) + dataCopy.splice(newIndex || data.length, 0, newRow) + const newLabels = utils.createLabels(config.rowLabels, data.length + 1) + setRowLabels(newLabels) setData(dataCopy) } @@ -77,110 +85,152 @@ export function DataGrid(props: DataGridProps) { } } + function addColumn(newIndex: number) { + const newColumns = utils.createLabels( + config.columnLabels, + columnLabels.length + 1 + ) + const fillValue = utils.getFillValue(attributeType) + const updatedData = data.map((item) => { + item.splice(newIndex, 0, fillValue) + return item + }) + setData(updatedData) + setColumnLabels(newColumns) + setSelectedColumn(newIndex) + } + function deleteColumn(index: number) { - const newColumns = utils.createArrayFromNumber(columns.length - 1) + const newColumns = utils.createLabels( + config.columnLabels, + columnLabels.length - 1 + ) const updatedData = data.map((item) => { item.splice(index, 1) return item }) - setColumns(newColumns) + setColumnLabels(newColumns) setData(updatedData) setSelectedColumn(undefined) } return ( + + {config.title && {config.title}} + {config.description && {config.description}} + - - - - # - - {columns.map((column, index) => ( - - ))} - - {paginatedRows.map((item, rowIndex) => { - const calculatedIndex = paginationPage * rowsPerPage + rowIndex - return ( - - - {multi ? ( - item.map((cellValue: any, cellIndex: number) => ( - + {config.showColumns && ( + + + {config.showRows && #} + {columnLabels.map((column, index) => ( + + ))} + + + )} + + {paginatedRows.map((item, rowIndex) => { + const calculatedIndex = paginationPage * rowsPerPage + rowIndex + return ( + + {config.showRows && ( + + )} + {isMultiDimensional ? ( + item?.map((cellValue: any, cellIndex: number) => ( + + )) + ) : ( + - )) - ) : ( - - )} - - ) - })} + )} + + ) + })} + - {!rowsAreSetDimension && ( + {addButtonIsEnabled && ( addRow()} + onClick={() => + addButtonFunctionality === 'addRow' + ? addRow() + : addColumn(columnLabels.length) + } > )} {selectedRow !== undefined && ( <> - {definedRows === '*' && ( + {rowsAreEditable && ( )} - moveRow('up')}> - - - moveRow('down')}> - - + {isSortEnabled && ( + <> + moveRow('up')}> + + + moveRow('down')}> + + + + )} )} diff --git a/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx b/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx index 4a13b17d7..3f587319e 100644 --- a/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx +++ b/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx @@ -8,11 +8,13 @@ import { useDMSS, useDocument, } from '@development-framework/dm-core' -import { DataGrid } from './DataGrid' import { Button } from '@equinor/eds-core-react' +import { DataGridConfig, defaultConfig } from './types' +import { DataGrid } from './DataGrid' export function DataGridPlugin(props: IUIPlugin) { - const { idReference, config, type } = props + const { idReference, config: userConfig, type } = props + const config: DataGridConfig = { ...defaultConfig, ...userConfig } const dmssAPI = useDMSS() const [data, setData] = useState() const [loading, setLoading] = useState(false) @@ -22,14 +24,24 @@ export function DataGridPlugin(props: IUIPlugin) { idReference, 1 ) - const { fieldName, rowsPerPage } = config + const { fieldNames, rowsPerPage } = config + const multiplePrimitives = fieldNames?.length > 1 const attribute = blueprint?.attributes?.find( - (atts: TAttribute) => atts.name === fieldName + (atts: TAttribute) => atts.name === fieldNames[0] + ) + const attributes = fieldNames.map((field) => + blueprint?.attributes.find((att: TAttribute) => att.name === field) ) useEffect(() => { if (isLoading || !document) return - setData(document?.[fieldName] || []) + if (multiplePrimitives) { + const mergedData: string[] = [] + fieldNames.forEach((field) => mergedData.push(document[field])) + setData(mergedData) + return + } + setData(document?.[fieldNames[0]] || []) }, [document, isLoading]) function onChange(data: any[]) { @@ -40,7 +52,13 @@ export function DataGridPlugin(props: IUIPlugin) { async function saveDocument() { setLoading(true) try { - const payload = { ...document, [fieldName]: data } + let newData = { [fieldNames[0]]: data } + if (multiplePrimitives) { + newData = Object.fromEntries( + (data || []).map((value, index) => [fieldNames[index], value]) + ) + } + const payload = { ...document, ...newData } await dmssAPI.documentUpdate({ idAddress: idReference, data: JSON.stringify(payload), @@ -58,15 +76,22 @@ export function DataGridPlugin(props: IUIPlugin) { return !data ? null : ( - + {config.editable && ( + + )} ) } diff --git a/packages/dm-core-plugins/src/data-grid/HeaderCell/HeaderCell.tsx b/packages/dm-core-plugins/src/data-grid/HeaderCell/HeaderCell.tsx new file mode 100644 index 000000000..9a68f0632 --- /dev/null +++ b/packages/dm-core-plugins/src/data-grid/HeaderCell/HeaderCell.tsx @@ -0,0 +1,89 @@ +import React, { useState } from 'react' +import { Icon, Menu, Typography } from '@equinor/eds-core-react' +import * as Styled from '../styles' +import { add as addIcon, delete_to_trash } from '@equinor/eds-icons' + +type HeaderCellProps = { + add: (newIndex: number) => void + editable: boolean + delete: (index: number) => void + index: number + label: string + selected: number | undefined + setSelected: React.Dispatch> + type: 'column' | 'row' +} + +export function HeaderCell(props: HeaderCellProps) { + const { add, index, label, selected, setSelected, type } = props + const [isMenuOpen, setIsMenuOpen] = useState(false) + const [menuButtonAnchor, setMenuButtonAnchor] = + useState(null) + + function changeSelected(index: number) { + if (index === selected) { + setSelected(undefined) + return + } + setSelected(index) + } + + function handleColumnRightClick(event: any) { + event.preventDefault() + setSelected(index) + setIsMenuOpen(true) + } + + function onContextMenuClose() { + setIsMenuOpen(false) + setSelected(undefined) + } + + function setSelectedOnKeyDown(event: any) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault() + changeSelected(index) + } + } + + return ( + changeSelected(index)} + onContextMenu={handleColumnRightClick} + //@ts-ignore + ref={setMenuButtonAnchor} + selected={index === selected} + tabIndex={0} + onKeyDown={setSelectedOnKeyDown} + > + {label} + + {!props.editable ? ( + + {type === 'column' ? 'Columns' : 'Rows'} are pre-defined and cannot + be deleted or added. + + ) : ( + <> + props.delete(index)}> + Delete {type} + + add(index - 1)}> + Add 1 {type}{' '} + {type === 'column' ? 'left' : 'above'} + + add(index)}> + Add 1 {type}{' '} + {type === 'column' ? 'left' : 'below'} + + + )} + + + ) +} diff --git a/packages/dm-core-plugins/src/data-grid/VerticalHeader/VerticalHeader.tsx b/packages/dm-core-plugins/src/data-grid/VerticalHeader/VerticalHeader.tsx deleted file mode 100644 index 9402d7d59..000000000 --- a/packages/dm-core-plugins/src/data-grid/VerticalHeader/VerticalHeader.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { useState } from 'react' -import * as Styled from '../styles' -import { Icon, Menu, Typography } from '@equinor/eds-core-react' -import { add, delete_to_trash } from '@equinor/eds-icons' - -type VerticalHeaderProps = { - selectedRow: number | undefined - setSelectedRow: React.Dispatch> - index: number - addRow: (newIndex?: number) => void - deleteRow: () => void - rowsAreSetDimension: boolean -} - -export function VerticalHeader(props: VerticalHeaderProps) { - const { selectedRow, setSelectedRow, index, addRow } = props - const [isMenuOpen, setIsMenuOpen] = useState(false) - const [menuButtonAnchor, setMenuButtonAnchor] = - useState(null) - - function changeSelectedRow(index: number) { - if (index === selectedRow) { - setSelectedRow(undefined) - return - } - setSelectedRow(index) - } - - function handleColumnRightClick(event: any) { - event.preventDefault() - setSelectedRow(index) - setIsMenuOpen(true) - } - - function onContextMenuClose() { - setIsMenuOpen(false) - setSelectedRow(undefined) - } - - return ( - - changeSelectedRow(index)} - onContextMenu={handleColumnRightClick} - ref={setMenuButtonAnchor} - > - {index + 1} - - - {props.rowsAreSetDimension ? ( - - Number of rows are pre-defined and cannot be deleted or added. - - ) : ( - <> - - Delete row - - addRow(index)}> - Add 1 row above - - addRow(index + 1)}> - Add 1 row below - - - )} - - - ) -} diff --git a/packages/dm-core-plugins/src/data-grid/styles.ts b/packages/dm-core-plugins/src/data-grid/styles.ts index ba98a8693..e1b3e22b9 100644 --- a/packages/dm-core-plugins/src/data-grid/styles.ts +++ b/packages/dm-core-plugins/src/data-grid/styles.ts @@ -1,46 +1,90 @@ -import { Button } from '@equinor/eds-core-react' import { tokens } from '@equinor/eds-tokens' import styled, { css } from 'styled-components' -export const DataGrid = styled.div` +export const DataGrid = styled.table<{ flip?: boolean }>` width: 100%; - display: table; - justify-content: stretch ; - align-items: center; + max-width: 100%; + vertical-align: top; + overflow-x: auto; + white-space: nowrap; + border-collapse: collapse; + border-spacing: 0; border-bottom: 1px solid ${tokens.colors.ui.background__medium.rgba}; - > div { - &:nth-child(odd) { - background: ${tokens.colors.ui.background__light.rgba}; + tbody { + > tr { + &:nth-child(odd) { + td { + background-color: ${tokens.colors.ui.background__light.rgba}; + } + } } } + + ${({ flip }) => + flip && + css` + display: flex; + overflow: hidden; + thead { + display: flex; + flex-shrink: 0; + min-width: min-content; + th { + border-bottom: 0; + position: relative; + } + } + tbody { + display: flex; + position: relative; + overflow-x: auto; + overflow-y: hidden; + } + tr { + display: flex; + flex-direction: column; + min-width: min-content; + flex-shrink: 0; + } + th, td { + display: block; + } + `} ` -export const Row = styled.div<{ header?: boolean; selected?: boolean }>` - width: 100%; - display: table-row; - justify-content: stretch ; - align-items: stretch; - font-weight: ${({ header }) => (header ? 'bold' : 'normal')}; +export const Row = styled.tr` + position: relative; + th { + border: 1px solid ${tokens.colors.ui.background__medium.rgba}; + border-bottom: 0; + background-clip: padding-box; + padding: 0; + position: relative; + } +` + +export const Head = styled.thead` + font-weight: bold; + th { + border-bottom: 2px solid ${tokens.colors.ui.background__medium.rgba}; + text-align: center; + } ` -export const Cell = styled.div<{ - header?: boolean +type IStyledCell = { selected?: boolean attributeType?: string -}>` +} +export const Cell = styled.td` border: 1px solid ${tokens.colors.ui.background__medium.rgba}; - border-bottom: none; - display: table-cell; + border-bottom: 0; position: relative; - ${({ header }) => - header && - css` - border-bottom: 2px solid ${tokens.colors.ui.background__medium.rgba}; - `} + background-clip: padding-box; + padding: 0; ${({ selected }) => selected && css` - background: ${tokens.colors.interactive.primary__selected_highlight.rgba}; + background-color: ${tokens.colors.interactive.primary__selected_highlight.rgba}!important; `} ${({ attributeType }) => attributeType === 'boolean' && @@ -49,12 +93,27 @@ export const Cell = styled.div<{ `} ` +export const Header = styled.th<{ selected?: boolean }>` + background: ${({ selected }) => + selected ? 'rgba(0, 0, 0, 0.2)' : 'transparent'}; + cursor: pointer; + &:hover { + background: rgba(0, 0, 0, 0.2); + } + svg { + fill: #666; + } +` + export const RowButton = styled.button<{ selected?: boolean }>` - height: 100%; - width: 100%; - position: absolute; background: ${({ selected }) => selected ? 'rgba(0, 0, 0, 0.2)' : 'transparent'}; + width: 100%; + height: 100%; + padding: 0; + position: absolute; + top: 0; + left: 0; &:hover { background: rgba(0, 0, 0, 0.2); } @@ -99,13 +158,6 @@ export const ActionRowButton = styled.button` } ` -export const SaveButton = styled(Button)` - padding: 0.5rem; - height: 1.5rem; - font-size: 0.75rem; - line-height: 0; -` - export const Select = styled.select` background: transparent; font-size: 0.75rem; diff --git a/packages/dm-core-plugins/src/data-grid/utils.ts b/packages/dm-core-plugins/src/data-grid/utils.ts index 34d2a8ca3..39044cc91 100644 --- a/packages/dm-core-plugins/src/data-grid/utils.ts +++ b/packages/dm-core-plugins/src/data-grid/utils.ts @@ -1,6 +1,50 @@ -export const columnLabels = Array.from({ length: 26 }, (_, i) => - String.fromCharCode(i + 65) -) +import { DataGridConfig, PredefinedLabels } from './types' + +const predefinedLabels: PredefinedLabels[] = ['...ABC', '...ZYX', '...123'] + +function getPredefinedLabels(type: PredefinedLabels, length: number) { + if (type === '...ABC') { + const labels_ABC = Array.from({ length }, (_, i) => + String.fromCharCode(i + 65) + ) + return labels_ABC + } + if (type === '...ZYX') { + const labels_ZYX = Array.from({ length }, (_, i) => + String.fromCharCode(90 - i) + ) + return labels_ZYX + } + if (type === '...123') { + const labels_123 = Array.from({ length }, (_, i) => `${i + 1}`) + return labels_123 + } + return [] +} + +export function createLabels(labels: string[], length: number): string[] { + const predefinedColumns = labels.filter((label) => + predefinedLabels.includes(label as PredefinedLabels) + ) + if (predefinedColumns?.length > 0) { + const predefinedLabelsNeeded = + length - (labels?.length - predefinedColumns?.length) + let mappedLabels: string[] = [] + labels.forEach((label) => { + if (predefinedLabels.includes(label as PredefinedLabels)) { + const createdLabels = getPredefinedLabels( + label as PredefinedLabels, + predefinedLabelsNeeded + ) + mappedLabels = [...mappedLabels, ...createdLabels] + } else { + mappedLabels.push(label) + } + }) + return mappedLabels as string[] + } + return labels +} export const createArrayFromNumber = (number: number) => Array.from({ length: number }, (_, i) => i + 1) @@ -15,3 +59,48 @@ export function arrayMove(arr: any[], fromIndex: number, toIndex: number) { export const getFillValue = (type: string) => type === 'boolean' ? false : type === 'number' ? 0 : '' + +export function getFunctionalityVariables( + config: DataGridConfig, + dimensions: string | undefined +): [boolean, boolean, string, boolean, boolean, string, boolean] { + const { + editable, + adjustableColumns, + adjustableRows, + fieldNames, + printDirection, + } = config + + const [columnDimensions, rowDimensions] = dimensions?.split(',') || ['*', '*'] + const isMultiPrimitive = fieldNames.length > 1 + const isMultiDimensional: boolean = dimensions?.includes(',') || false + const addButtonFunctionality = + printDirection === 'horizontal' ? 'addRow' : 'addColumn' + const isSortEnabled = + !isMultiPrimitive && + config.adjustableRows && + columnDimensions === '*' && + config.editable && + config.movableRows + const rowsAreEditable = editable && adjustableRows && rowDimensions === '*' + const columnsAreEditable = + editable && + adjustableColumns && + columnDimensions === '*' && + isMultiDimensional + const addButtonIsEnabled = + (isMultiPrimitive && printDirection === 'vertical') || + (rowsAreEditable && printDirection === 'horizontal') || + (columnsAreEditable && printDirection === 'vertical') + + return [ + rowsAreEditable, + columnsAreEditable, + addButtonFunctionality, + addButtonIsEnabled, + isMultiDimensional, + columnDimensions, + isSortEnabled, + ] +}