From fb09defc8d25cfe80f17cdcd227c80cfb081972c Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Sun, 23 Jun 2024 12:31:03 +0200 Subject: [PATCH 1/4] Add identifier property to Entity attributes - Highlight identifier attributes by underlining their name - Allow to set identifier property in Entity form --- .../src/glsp-server/common/nodes.ts | 2 + .../glsp-server/system-diagram/model/nodes.ts | 10 +++- .../language-server/cross-model-serializer.ts | 4 ++ .../src/language-server/entity.langium | 7 ++- .../src/language-server/generated/ast.ts | 9 ++++ .../src/language-server/generated/grammar.ts | 46 ++++++++++++++++++- .../syntaxes/cross-model.tmLanguage.json | 2 +- .../serializer/cross-model-serializer.test.ts | 4 ++ packages/glsp-client/src/browser/model.ts | 5 +- packages/glsp-client/src/browser/views.tsx | 11 ++++- packages/glsp-client/style/diagram.css | 7 +++ .../protocol/src/model-service/protocol.ts | 1 + .../views/common/EntityAttributesDataGrid.tsx | 5 +- 13 files changed, 102 insertions(+), 11 deletions(-) diff --git a/extensions/crossmodel-lang/src/glsp-server/common/nodes.ts b/extensions/crossmodel-lang/src/glsp-server/common/nodes.ts index 131c4272..b66181c8 100644 --- a/extensions/crossmodel-lang/src/glsp-server/common/nodes.ts +++ b/extensions/crossmodel-lang/src/glsp-server/common/nodes.ts @@ -66,6 +66,8 @@ export class AttributeCompartmentBuilder extends GCompartmentBuilder { // Add the children of the node const attributes = getAttributes(node); - this.add(createAttributesCompartment(attributes, this.proxy.id, index)); + const attributesCompartment = new AttributesCompartmentBuilder().set(this.proxy.id); + for (const attribute of attributes) { + const attributeNode = AttributeCompartment.builder().set(attribute, index); + attributeNode.addArg('identifier', attribute.identifier); + attributesCompartment.add(attributeNode.build()); + } + this.add(attributesCompartment.build()); // The DiagramNode in the langium file holds the coordinates of node this.layout('vbox') diff --git a/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts b/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts index 14f737fd..7eb3abcb 100644 --- a/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts +++ b/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts @@ -21,6 +21,7 @@ import { import { isImplicitProperty } from './util/ast-util.js'; const PROPERTY_ORDER = [ + 'identifier', 'id', 'name', 'datatype', @@ -117,6 +118,9 @@ export class CrossModelSerializer implements Serializer { if (Array.isArray(objValue) && objValue.length === 0) { return; } + if (objKey === 'identifier' && objValue === false) { + return ''; + } const propKey = this.serializeKey(objKey); const propValue = this.serializeValue(objValue, indentationLevel, propKey); diff --git a/extensions/crossmodel-lang/src/language-server/entity.langium b/extensions/crossmodel-lang/src/language-server/entity.langium index ac6a5dc7..5c2c97a0 100644 --- a/extensions/crossmodel-lang/src/language-server/entity.langium +++ b/extensions/crossmodel-lang/src/language-server/entity.langium @@ -18,13 +18,16 @@ Entity: interface Attribute { id: string; name: string; - datatype: string; + datatype: string; description?: string; } -interface EntityAttribute extends Attribute {} +interface EntityAttribute extends Attribute { + identifier?: boolean; +} EntityAttribute returns EntityAttribute: + (identifier?='identifier' ':' ('TRUE' | 'true'))? 'id' ':' id=ID 'name' ':' name=STRING 'datatype' ':' datatype=STRING diff --git a/extensions/crossmodel-lang/src/language-server/generated/ast.ts b/extensions/crossmodel-lang/src/language-server/generated/ast.ts index 873787b3..6939f785 100644 --- a/extensions/crossmodel-lang/src/language-server/generated/ast.ts +++ b/extensions/crossmodel-lang/src/language-server/generated/ast.ts @@ -327,6 +327,7 @@ export function isTargetObject(item: unknown): item is TargetObject { export interface EntityAttribute extends Attribute { readonly $type: 'EntityAttribute' | 'EntityNodeAttribute'; + identifier: boolean } export const EntityAttribute = 'EntityAttribute'; @@ -521,6 +522,14 @@ export class CrossModelAstReflection extends AbstractAstReflection { ] }; } + case 'EntityAttribute': { + return { + name: 'EntityAttribute', + mandatory: [ + { name: 'identifier', type: 'boolean' } + ] + }; + } default: { return { name: type, diff --git a/extensions/crossmodel-lang/src/language-server/generated/grammar.ts b/extensions/crossmodel-lang/src/language-server/generated/grammar.ts index 55bdc7e8..104e0b19 100644 --- a/extensions/crossmodel-lang/src/language-server/generated/grammar.ts +++ b/extensions/crossmodel-lang/src/language-server/generated/grammar.ts @@ -245,6 +245,38 @@ export const CrossModelGrammar = (): Grammar => loadedCrossModelGrammar ?? (load "definition": { "$type": "Group", "elements": [ + { + "$type": "Group", + "elements": [ + { + "$type": "Assignment", + "feature": "identifier", + "operator": "?=", + "terminal": { + "$type": "Keyword", + "value": "identifier" + } + }, + { + "$type": "Keyword", + "value": ":" + }, + { + "$type": "Alternatives", + "elements": [ + { + "$type": "Keyword", + "value": "TRUE" + }, + { + "$type": "Keyword", + "value": "true" + } + ] + } + ], + "cardinality": "?" + }, { "$type": "Keyword", "value": "id" @@ -2220,13 +2252,23 @@ export const CrossModelGrammar = (): Grammar => loadedCrossModelGrammar ?? (load }, { "$type": "Interface", + "attributes": [ + { + "$type": "TypeAttribute", + "name": "identifier", + "isOptional": true, + "type": { + "$type": "SimpleType", + "primitiveType": "boolean" + } + } + ], "name": "EntityAttribute", "superTypes": [ { "$ref": "#/interfaces@0" } - ], - "attributes": [] + ] }, { "$type": "Interface", diff --git a/extensions/crossmodel-lang/syntaxes/cross-model.tmLanguage.json b/extensions/crossmodel-lang/syntaxes/cross-model.tmLanguage.json index 0de0da59..472fac31 100644 --- a/extensions/crossmodel-lang/syntaxes/cross-model.tmLanguage.json +++ b/extensions/crossmodel-lang/syntaxes/cross-model.tmLanguage.json @@ -10,7 +10,7 @@ }, { "name": "keyword.control.cross-model", - "match": "\\b(apply|attribute|attributes|child|conditions|cross-join|datatype|dependencies|description|diagram|edges|entity|expression|from|height|id|inner-join|join|left-join|mapping|mappings|name|nodes|parent|relationship|sourceNode|sources|systemDiagram|target|targetNode|type|width|x|y)\\b" + "match": "\\b(TRUE|apply|attribute|attributes|child|conditions|cross-join|datatype|dependencies|description|diagram|edges|entity|expression|from|height|id|identifier|inner-join|join|left-join|mapping|mappings|name|nodes|parent|relationship|sourceNode|sources|systemDiagram|target|targetNode|true|type|width|x|y)\\b" }, { "name": "string.quoted.double.cross-model", diff --git a/extensions/crossmodel-lang/test/language-server/serializer/cross-model-serializer.test.ts b/extensions/crossmodel-lang/test/language-server/serializer/cross-model-serializer.test.ts index 4ffa45f6..b082dcdc 100644 --- a/extensions/crossmodel-lang/test/language-server/serializer/cross-model-serializer.test.ts +++ b/extensions/crossmodel-lang/test/language-server/serializer/cross-model-serializer.test.ts @@ -41,6 +41,7 @@ describe('CrossModelLexer', () => { crossModelRoot.entity.attributes = [ { + identifier: false, $container: crossModelRoot.entity, $type: 'EntityAttribute', id: 'Attribute1', @@ -48,6 +49,7 @@ describe('CrossModelLexer', () => { datatype: 'Datatype Attribute 1' }, { + identifier: false, $container: crossModelRoot.entity, $type: 'EntityAttribute', id: 'Attribute2', @@ -66,6 +68,7 @@ describe('CrossModelLexer', () => { }; crossModelRootWithAttributesDifPlace.entity.attributes = [ { + identifier: false, $container: crossModelRoot.entity, $type: 'EntityAttribute', id: 'Attribute1', @@ -73,6 +76,7 @@ describe('CrossModelLexer', () => { datatype: 'Datatype Attribute 1' }, { + identifier: false, $container: crossModelRoot.entity, $type: 'EntityAttribute', id: 'Attribute2', diff --git a/packages/glsp-client/src/browser/model.ts b/packages/glsp-client/src/browser/model.ts index 0816113b..e2b222b1 100644 --- a/packages/glsp-client/src/browser/model.ts +++ b/packages/glsp-client/src/browser/model.ts @@ -3,11 +3,12 @@ ********************************************************************************/ import { ATTRIBUTE_COMPARTMENT_TYPE } from '@crossbreeze/protocol'; -import { GCompartment, GModelElement, Hoverable, Selectable, isSelectable } from '@eclipse-glsp/client'; +import { Args, ArgsAware, GCompartment, GModelElement, Hoverable, Selectable, isSelectable } from '@eclipse-glsp/client'; -export class AttributeCompartment extends GCompartment implements Selectable, Hoverable { +export class AttributeCompartment extends GCompartment implements Selectable, Hoverable, ArgsAware { hoverFeedback: boolean; selected: boolean; + args?: Args; static is(element?: GModelElement): element is AttributeCompartment { return !!element && isSelectable(element) && element.type === ATTRIBUTE_COMPARTMENT_TYPE; diff --git a/packages/glsp-client/src/browser/views.tsx b/packages/glsp-client/src/browser/views.tsx index e568a331..14bb030e 100644 --- a/packages/glsp-client/src/browser/views.tsx +++ b/packages/glsp-client/src/browser/views.tsx @@ -27,7 +27,7 @@ export class AttributeCompartmentView extends GCompartmentView { override render(compartment: Readonly, context: RenderingContext): VNode | undefined { const translate = `translate(${compartment.bounds.x}, ${compartment.bounds.y})`; const vnode: any = ( - + {context.renderChildren(compartment) as ReactNode} + {compartment.args?.identifier && ( + + )} ) as any; diff --git a/packages/glsp-client/style/diagram.css b/packages/glsp-client/style/diagram.css index b29e058e..76eb2edb 100644 --- a/packages/glsp-client/style/diagram.css +++ b/packages/glsp-client/style/diagram.css @@ -96,6 +96,13 @@ baseline-shift: 2px; } +.sprotty line.identifier { + stroke: black; + stroke-width: 0.75px; + stroke-dasharray: none; + stroke-linecap: round; +} + .sprotty[id^='system-diagram'] .tool-palette { top: 11px; right: 11px; diff --git a/packages/protocol/src/model-service/protocol.ts b/packages/protocol/src/model-service/protocol.ts index e0409fde..f2a996e4 100644 --- a/packages/protocol/src/model-service/protocol.ts +++ b/packages/protocol/src/model-service/protocol.ts @@ -54,6 +54,7 @@ export interface Attribute extends CrossModelElement, Identifiable { export const EntityAttributeType = 'EntityAttribute'; export interface EntityAttribute extends Attribute { readonly $type: typeof EntityAttributeType; + identifier?: boolean; } export const RelationshipType = 'Relationship'; diff --git a/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx b/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx index ca94c836..abcdd973 100644 --- a/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx +++ b/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx @@ -27,7 +27,8 @@ export function EntityAttributesDataGrid(): React.ReactElement { id: attribute.id, name: attribute.name, datatype: attribute.datatype, - description: attribute.description + description: attribute.description, + identifier: attribute.identifier } }); return attribute; @@ -89,6 +90,7 @@ export function EntityAttributesDataGrid(): React.ReactElement { const columns = React.useMemo( () => [ + { field: 'identifier', headerName: 'Id', maxWidth: 50, editable: true, type: 'boolean' }, { field: 'name', headerName: 'Name', @@ -100,6 +102,7 @@ export function EntityAttributesDataGrid(): React.ReactElement { field: 'datatype', headerName: 'Data type', editable: true, + flex: 100, type: 'singleSelect', valueOptions: ['Integer', 'Float', 'Char', 'Varchar', 'Bool'] }, From 49125904fd6057c4954222eb6818d8b1ff332b6a Mon Sep 17 00:00:00 2001 From: Harmen Wessels <97173058+harmen-xb@users.noreply.github.com> Date: Thu, 27 Jun 2024 18:47:49 +0000 Subject: [PATCH 2/4] Updated identifiers of source system entities. --- .../ExampleCRM/entities/Address.entity.cm | 3 +- .../ExampleCRM/entities/Customer.entity.cm | 43 ++++++++++--------- .../ExampleCRM/entities/Order.entity.cm | 3 +- .../entities/Country.entity.cm | 13 +++--- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm b/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm index e9bff3c3..adf08e65 100644 --- a/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm +++ b/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm @@ -3,7 +3,8 @@ entity: name: "Address" description: "The address of a customer." attributes: - - id: CustomerID + - identifier: true + id: CustomerID name: "CustomerID" datatype: "Integer" - id: Street diff --git a/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm b/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm index 78683740..1286efd0 100644 --- a/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm +++ b/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm @@ -2,24 +2,25 @@ entity: id: Customer name: "Customer" attributes: - - id: Id - name: "Id" - datatype: "Integer" - - id: FirstName - name: "FirstName" - datatype: "Text" - - id: LastName - name: "LastName" - datatype: "Text" - - id: City - name: "City" - datatype: "Text" - - id: Country - name: "Country" - datatype: "Text" - - id: Phone - name: "Phone" - datatype: "Text" - - id: BirthDate - name: "BirthDate" - datatype: "DateTime" \ No newline at end of file + - identifier: true + id: Id + name: "Id" + datatype: "Integer" + - id: FirstName + name: "FirstName" + datatype: "Text" + - id: LastName + name: "LastName" + datatype: "Text" + - id: City + name: "City" + datatype: "Text" + - id: Country + name: "Country" + datatype: "Text" + - id: Phone + name: "Phone" + datatype: "Text" + - id: BirthDate + name: "BirthDate" + datatype: "DateTime" \ No newline at end of file diff --git a/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm b/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm index 0d804945..aad4d257 100644 --- a/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm +++ b/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm @@ -3,7 +3,8 @@ entity: name: "Order" description: "Order placed by a customer in the Customer table." attributes: - - id: Id + - identifier: true + id: Id name: "Id" datatype: "Integer" - id: OrderDate diff --git a/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm b/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm index c760ff72..fbab99f3 100644 --- a/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm +++ b/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm @@ -2,9 +2,10 @@ entity: id: Country name: "Country" attributes: - - id: Code - name: "Code" - datatype: "Varchar" - - id: Name - name: "name" - datatype: "Varchar" \ No newline at end of file + - identifier: true + id: Code + name: "Code" + datatype: "Varchar" + - id: Name + name: "Name" + datatype: "Varchar" \ No newline at end of file From 4ea1fdd42b70a3ec868ed7a653d9b18473ccbbf0 Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Fri, 28 Jun 2024 09:57:47 +0200 Subject: [PATCH 3/4] PR Feedback: Order of properties, different icons --- .../ExampleCRM/entities/Address.entity.cm | 4 +- .../ExampleCRM/entities/Customer.entity.cm | 4 +- .../ExampleCRM/entities/Order.entity.cm | 4 +- .../entities/Country.entity.cm | 4 +- .../language-server/cross-model-serializer.ts | 4 +- .../src/language-server/entity.langium | 2 +- .../src/language-server/generated/grammar.ts | 64 +++++++++---------- .../views/common/EntityAttributesDataGrid.tsx | 21 +++++- .../react-model-ui/src/views/common/Icons.tsx | 16 +++++ 9 files changed, 77 insertions(+), 46 deletions(-) create mode 100644 packages/react-model-ui/src/views/common/Icons.tsx diff --git a/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm b/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm index adf08e65..6e3d6ddb 100644 --- a/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm +++ b/examples/mapping-example/Sources/ExampleCRM/entities/Address.entity.cm @@ -3,10 +3,10 @@ entity: name: "Address" description: "The address of a customer." attributes: - - identifier: true - id: CustomerID + - id: CustomerID name: "CustomerID" datatype: "Integer" + identifier: true - id: Street name: "Street" datatype: "Text" diff --git a/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm b/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm index 1286efd0..d3ef8026 100644 --- a/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm +++ b/examples/mapping-example/Sources/ExampleCRM/entities/Customer.entity.cm @@ -2,10 +2,10 @@ entity: id: Customer name: "Customer" attributes: - - identifier: true - id: Id + - id: Id name: "Id" datatype: "Integer" + identifier: true - id: FirstName name: "FirstName" datatype: "Text" diff --git a/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm b/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm index aad4d257..62184e8a 100644 --- a/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm +++ b/examples/mapping-example/Sources/ExampleCRM/entities/Order.entity.cm @@ -3,10 +3,10 @@ entity: name: "Order" description: "Order placed by a customer in the Customer table." attributes: - - identifier: true - id: Id + - id: Id name: "Id" datatype: "Integer" + identifier: true - id: OrderDate name: "OrderDate" datatype: "Integer" diff --git a/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm b/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm index fbab99f3..183df446 100644 --- a/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm +++ b/examples/mapping-example/Sources/ExampleMasterdata/entities/Country.entity.cm @@ -2,10 +2,10 @@ entity: id: Country name: "Country" attributes: - - identifier: true - id: Code + - id: Code name: "Code" datatype: "Varchar" + identifier: true - id: Name name: "Name" datatype: "Varchar" \ No newline at end of file diff --git a/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts b/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts index 7eb3abcb..1aaf7036 100644 --- a/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts +++ b/extensions/crossmodel-lang/src/language-server/cross-model-serializer.ts @@ -21,10 +21,10 @@ import { import { isImplicitProperty } from './util/ast-util.js'; const PROPERTY_ORDER = [ - 'identifier', 'id', 'name', 'datatype', + 'identifier', 'description', 'entity', 'parent', @@ -119,7 +119,7 @@ export class CrossModelSerializer implements Serializer { return; } if (objKey === 'identifier' && objValue === false) { - return ''; + return; } const propKey = this.serializeKey(objKey); diff --git a/extensions/crossmodel-lang/src/language-server/entity.langium b/extensions/crossmodel-lang/src/language-server/entity.langium index 5c2c97a0..f02f7d87 100644 --- a/extensions/crossmodel-lang/src/language-server/entity.langium +++ b/extensions/crossmodel-lang/src/language-server/entity.langium @@ -27,8 +27,8 @@ interface EntityAttribute extends Attribute { } EntityAttribute returns EntityAttribute: - (identifier?='identifier' ':' ('TRUE' | 'true'))? 'id' ':' id=ID 'name' ':' name=STRING 'datatype' ':' datatype=STRING + (identifier?='identifier' ':' ('TRUE' | 'true'))? ('description' ':' description=STRING)?; diff --git a/extensions/crossmodel-lang/src/language-server/generated/grammar.ts b/extensions/crossmodel-lang/src/language-server/generated/grammar.ts index 104e0b19..4fae890b 100644 --- a/extensions/crossmodel-lang/src/language-server/generated/grammar.ts +++ b/extensions/crossmodel-lang/src/language-server/generated/grammar.ts @@ -245,38 +245,6 @@ export const CrossModelGrammar = (): Grammar => loadedCrossModelGrammar ?? (load "definition": { "$type": "Group", "elements": [ - { - "$type": "Group", - "elements": [ - { - "$type": "Assignment", - "feature": "identifier", - "operator": "?=", - "terminal": { - "$type": "Keyword", - "value": "identifier" - } - }, - { - "$type": "Keyword", - "value": ":" - }, - { - "$type": "Alternatives", - "elements": [ - { - "$type": "Keyword", - "value": "TRUE" - }, - { - "$type": "Keyword", - "value": "true" - } - ] - } - ], - "cardinality": "?" - }, { "$type": "Keyword", "value": "id" @@ -337,6 +305,38 @@ export const CrossModelGrammar = (): Grammar => loadedCrossModelGrammar ?? (load "arguments": [] } }, + { + "$type": "Group", + "elements": [ + { + "$type": "Assignment", + "feature": "identifier", + "operator": "?=", + "terminal": { + "$type": "Keyword", + "value": "identifier" + } + }, + { + "$type": "Keyword", + "value": ":" + }, + { + "$type": "Alternatives", + "elements": [ + { + "$type": "Keyword", + "value": "TRUE" + }, + { + "$type": "Keyword", + "value": "true" + } + ] + } + ], + "cardinality": "?" + }, { "$type": "Group", "elements": [ diff --git a/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx b/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx index abcdd973..cb7d8bf7 100644 --- a/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx +++ b/packages/react-model-ui/src/views/common/EntityAttributesDataGrid.tsx @@ -2,11 +2,14 @@ * Copyright (c) 2023 CrossBreeze. ********************************************************************************/ import { EntityAttribute, EntityAttributeType } from '@crossbreeze/protocol'; +import CheckBoxOutlineBlankOutlined from '@mui/icons-material/CheckBoxOutlineBlankOutlined'; +import CheckBoxOutlined from '@mui/icons-material/CheckBoxOutlined'; import { GridColDef } from '@mui/x-data-grid'; import * as React from 'react'; import { useEntity, useModelDispatch } from '../../ModelContext'; import { ErrorView } from '../ErrorView'; import GridComponent, { GridComponentRow, ValidationFunction } from './GridComponent'; +import { KeyIcon } from './Icons'; export type EntityAttributeRow = GridComponentRow; @@ -88,9 +91,8 @@ export function EntityAttributesDataGrid(): React.ReactElement { [] ); - const columns = React.useMemo( + const columns = React.useMemo[]>( () => [ - { field: 'identifier', headerName: 'Id', maxWidth: 50, editable: true, type: 'boolean' }, { field: 'name', headerName: 'Name', @@ -106,6 +108,19 @@ export function EntityAttributesDataGrid(): React.ReactElement { type: 'singleSelect', valueOptions: ['Integer', 'Float', 'Char', 'Varchar', 'Bool'] }, + { + field: 'identifier', + renderHeader: () => , + renderCell: ({ row }) => + row.identifier ? ( + + ) : ( + + ), + maxWidth: 50, + editable: true, + type: 'boolean' + }, { field: 'description', headerName: 'Description', editable: true, flex: 200 } ], [] @@ -127,7 +142,7 @@ export function EntityAttributesDataGrid(): React.ReactElement { return ; } return ( - autoHeight gridColumns={columns} gridData={entity.attributes} diff --git a/packages/react-model-ui/src/views/common/Icons.tsx b/packages/react-model-ui/src/views/common/Icons.tsx new file mode 100644 index 00000000..1abbe829 --- /dev/null +++ b/packages/react-model-ui/src/views/common/Icons.tsx @@ -0,0 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2024 CrossBreeze. + ********************************************************************************/ +/* eslint-disable max-len */ + +import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; +import React = require('react'); + +export const KeyIcon: React.FC = props => ( + // Exported from https://fonts.google.com/icons?selected=Material+Symbols+Outlined:key_vertical:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=key&icon.size=null&icon.color=%235f6368 + // It seems that the browser scales nicer if we do not provide a size in the SVG export + // ViewBox property comes from the export + + + +); From 24c8d0ee185fbf0c3a355b4956812d45dabd7bab Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Fri, 28 Jun 2024 14:58:50 +0200 Subject: [PATCH 4/4] Add icon to diagram instead of underline --- .../src/glsp-server/system-diagram/model/nodes.ts | 3 ++- packages/glsp-client/src/browser/views.tsx | 14 ++++++-------- packages/glsp-client/style/diagram.css | 5 +++++ packages/react-model-ui/src/views/common/Icons.tsx | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/extensions/crossmodel-lang/src/glsp-server/system-diagram/model/nodes.ts b/extensions/crossmodel-lang/src/glsp-server/system-diagram/model/nodes.ts index cfca3826..0a593984 100644 --- a/extensions/crossmodel-lang/src/glsp-server/system-diagram/model/nodes.ts +++ b/extensions/crossmodel-lang/src/glsp-server/system-diagram/model/nodes.ts @@ -37,7 +37,8 @@ export class GEntityNodeBuilder extends GNodeBuilder { const attributesCompartment = new AttributesCompartmentBuilder().set(this.proxy.id); for (const attribute of attributes) { const attributeNode = AttributeCompartment.builder().set(attribute, index); - attributeNode.addArg('identifier', attribute.identifier); + // increase padding left and right so we have space for the identifier icon + attributeNode.addArg('identifier', attribute.identifier).addLayoutOption('paddingLeft', 8).addLayoutOption('paddingRight', 8); attributesCompartment.add(attributeNode.build()); } this.add(attributesCompartment.build()); diff --git a/packages/glsp-client/src/browser/views.tsx b/packages/glsp-client/src/browser/views.tsx index 14bb030e..11cab6b6 100644 --- a/packages/glsp-client/src/browser/views.tsx +++ b/packages/glsp-client/src/browser/views.tsx @@ -2,6 +2,7 @@ * Copyright (c) 2024 CrossBreeze. ********************************************************************************/ /* eslint-disable react/no-unknown-property */ +/* eslint-disable max-len */ import { GCompartmentView, RenderingContext, RoundedCornerNodeView, RoundedCornerWrapper, svg } from '@eclipse-glsp/client'; import { ReactNode } from '@theia/core/shared/react'; @@ -37,16 +38,13 @@ export class AttributeCompartmentView extends GCompartmentView { width={Math.max(compartment.size.width, 0)} height={Math.max(compartment.size.height, 0)} > - {context.renderChildren(compartment) as ReactNode} {compartment.args?.identifier && ( - + )} + {context.renderChildren(compartment) as ReactNode} ) as any; diff --git a/packages/glsp-client/style/diagram.css b/packages/glsp-client/style/diagram.css index 76eb2edb..d1d88ec2 100644 --- a/packages/glsp-client/style/diagram.css +++ b/packages/glsp-client/style/diagram.css @@ -138,3 +138,8 @@ .command-palette { animation: none; } + +.attribute.identifier .icon-path { + transform: scale(0.0105) translate(-400px, 1375px); + fill: #000; +} diff --git a/packages/react-model-ui/src/views/common/Icons.tsx b/packages/react-model-ui/src/views/common/Icons.tsx index 1abbe829..fd810265 100644 --- a/packages/react-model-ui/src/views/common/Icons.tsx +++ b/packages/react-model-ui/src/views/common/Icons.tsx @@ -10,7 +10,7 @@ export const KeyIcon: React.FC = props => ( // Exported from https://fonts.google.com/icons?selected=Material+Symbols+Outlined:key_vertical:FILL@0;wght@400;GRAD@0;opsz@48&icon.query=key&icon.size=null&icon.color=%235f6368 // It seems that the browser scales nicer if we do not provide a size in the SVG export // ViewBox property comes from the export - + );