From fce12cb7f190299df28ea062cb0db6a7918e6fef Mon Sep 17 00:00:00 2001 From: Michael Charfadi Date: Mon, 2 Sep 2024 10:32:22 +0200 Subject: [PATCH] [3943] Add support for the widget refeference in the vs-code extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: https://github.com/eclipse-sirius/sirius-web/issues/3943 Signed-off-by: Michaƫl Charfadi --- CHANGELOG.adoc | 1 + vscode-extension/package.json | 1 + vscode-extension/src/view/app/index.tsx | 39 +-- .../app/registry/DefaultExtensionRegistry.tsx | 57 ++++ .../ReferenceWidgetDocumentTransform.ts | 244 ++++++++++++++++++ 5 files changed, 325 insertions(+), 17 deletions(-) create mode 100644 vscode-extension/src/view/app/registry/DefaultExtensionRegistry.tsx create mode 100644 vscode-extension/src/view/app/registry/ReferenceWidgetDocumentTransform.ts diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 4f0360fe6f..374bbff312 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1151,6 +1151,7 @@ An error message is now displayed if the _Shape_ is not set. - https://github.com/eclipse-sirius/sirius-web/issues/2426[#2426] [sirius-web] Fix an issue where the Representations view was always empty. - https://github.com/eclipse-sirius/sirius-web/issues/2429[#2429] [sirius-web] Ensure that view models can be successfully uploaded by loading the default color palettes - https://github.com/eclipse-sirius/sirius-web/issues/2433[#2433] [form] Fix an issue where the readonly property of FlexboxContainerPropertySection was not correctly dispatched to children. +- https://github.com/eclipse-sirius/sirius-web/issues/3943[#3943] [vs-code] Fix an issue with widget reference === New Features diff --git a/vscode-extension/package.json b/vscode-extension/package.json index 765c7dc231..07bebb1f49 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -135,6 +135,7 @@ "@eclipse-sirius/sirius-components-selection": "*", "@eclipse-sirius/sirius-components-trees": "*", "@eclipse-sirius/sirius-components-validation": "*", + "@eclipse-sirius/sirius-web-application": "*", "@mui/icons-material": "5.15.19", "@mui/material": "5.15.19", "@ObeoNetwork/gantt-task-react": "0.5.0", diff --git a/vscode-extension/src/view/app/index.tsx b/vscode-extension/src/view/app/index.tsx index fe4bbec0d2..dfc191cc02 100644 --- a/vscode-extension/src/view/app/index.tsx +++ b/vscode-extension/src/view/app/index.tsx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022, 2023 Obeo. + * Copyright (c) 2022, 2024 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -14,11 +14,13 @@ import { ApolloClient, ApolloProvider, DefaultOptions, HttpLink, InMemoryCache, split } from '@apollo/client'; import { WebSocketLink } from '@apollo/client/link/ws'; import { getMainDefinition } from '@apollo/client/utilities'; -import { ServerContext } from '@eclipse-sirius/sirius-components-core'; +import { ExtensionProvider, ServerContext } from '@eclipse-sirius/sirius-components-core'; import React from 'react'; import ReactDOM from 'react-dom'; import { App } from './App'; import './index.css'; +import { defaultExtensionRegistry } from './registry/DefaultExtensionRegistry'; +import { referenceWidgetDocumentTransform } from './registry/ReferenceWidgetDocumentTransform'; import { ToastProvider } from './toast/ToastProvider'; declare global { @@ -79,23 +81,26 @@ const ApolloGraphQLClient = new ApolloClient({ link: splitLink, cache: new InMemoryCache({ addTypename: true }), defaultOptions, + documentTransform: referenceWidgetDocumentTransform, }); ReactDOM.render( - - - - - - - , + + + + + + + + + , document.getElementById('root') ); diff --git a/vscode-extension/src/view/app/registry/DefaultExtensionRegistry.tsx b/vscode-extension/src/view/app/registry/DefaultExtensionRegistry.tsx new file mode 100644 index 0000000000..10ae8d7cae --- /dev/null +++ b/vscode-extension/src/view/app/registry/DefaultExtensionRegistry.tsx @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2024 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { ExtensionRegistry } from '@eclipse-sirius/sirius-components-core'; +import { + GQLWidget, + PropertySectionComponent, + widgetContributionExtensionPoint, +} from '@eclipse-sirius/sirius-components-forms'; +import { + GQLReferenceWidget, + ReferenceIcon, + ReferencePreview, + ReferencePropertySection, +} from '@eclipse-sirius/sirius-components-widget-reference'; +import React from 'react'; + +const defaultExtensionRegistry = new ExtensionRegistry(); +/******************************************************************************* + * + * Custom widget + * + * Used to register new custom widget in form + * + *******************************************************************************/ + +const isReferenceWidget = (widget: GQLWidget): widget is GQLReferenceWidget => widget.__typename === 'ReferenceWidget'; + +defaultExtensionRegistry.putData(widgetContributionExtensionPoint, { + identifier: `siriusWeb_${widgetContributionExtensionPoint.identifier}_referenceWidget`, + data: [ + { + name: 'ReferenceWidget', + icon: , + previewComponent: ReferencePreview, + component: (widget: GQLWidget): PropertySectionComponent | null => { + let propertySectionComponent: PropertySectionComponent | null = null; + + if (isReferenceWidget(widget)) { + propertySectionComponent = ReferencePropertySection; + } + return propertySectionComponent; + }, + }, + ], +}); + +export { defaultExtensionRegistry }; diff --git a/vscode-extension/src/view/app/registry/ReferenceWidgetDocumentTransform.ts b/vscode-extension/src/view/app/registry/ReferenceWidgetDocumentTransform.ts new file mode 100644 index 0000000000..d0cab0a13a --- /dev/null +++ b/vscode-extension/src/view/app/registry/ReferenceWidgetDocumentTransform.ts @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2024 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { DocumentTransform } from '@apollo/client'; +import { DocumentNode, FieldNode, FragmentSpreadNode, InlineFragmentNode, Kind, SelectionNode, visit } from 'graphql'; + +const shouldTransform = (document: DocumentNode) => { + return ( + document.definitions[0] && + document.definitions[0].kind === Kind.OPERATION_DEFINITION && + (document.definitions[0].name?.value === 'detailsEvent' || document.definitions[0].name?.value === 'formEvent') + ); +}; + +const isWidgetFragment = (field: FieldNode) => { + if (field.selectionSet && (field.name.value === 'widgets' || field.name.value === 'children')) { + const fragmentSpreads = field.selectionSet.selections + .filter((selection: SelectionNode): selection is FragmentSpreadNode => selection.kind === Kind.FRAGMENT_SPREAD) + .map((fragmentSpread: FragmentSpreadNode) => fragmentSpread.name.value); + if (fragmentSpreads.includes('widgetFields')) { + return true; + } + } + return false; +}; + +const labelField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'label', + }, +}; + +const iconURLField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'iconURL', + }, +}; + +const ownerIdField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'ownerId', + }, +}; + +const descriptionIdField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'descriptionId', + }, +}; + +const ownerKindField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'ownerKind', + }, +}; + +const referenceKindField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'referenceKind', + }, +}; + +const containmentField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'containment', + }, +}; + +const manyValuedField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'manyValued', + }, +}; + +const referenceField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'reference', + }, + selectionSet: { + kind: Kind.SELECTION_SET, + selections: [ownerKindField, referenceKindField, containmentField, manyValuedField], + }, +}; + +const idField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'id', + }, +}; + +const kindField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'kind', + }, +}; + +const colorField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'color', + }, +}; + +const fontSizeField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'fontSize', + }, +}; + +const italicField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'italic', + }, +}; + +const boldField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'bold', + }, +}; + +const underlineField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'underline', + }, +}; + +const strikeThroughField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'strikeThrough', + }, +}; + +const referenceValuesField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'referenceValues', + }, + selectionSet: { + kind: Kind.SELECTION_SET, + selections: [idField, labelField, kindField, iconURLField], + }, +}; + +const styleField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'style', + }, + selectionSet: { + kind: Kind.SELECTION_SET, + selections: [colorField, fontSizeField, italicField, boldField, underlineField, strikeThroughField], + }, +}; + +export const referenceWidgetDocumentTransform = new DocumentTransform((document) => { + if (shouldTransform(document)) { + return visit(document, { + Field(field) { + if (!isWidgetFragment(field)) { + return undefined; + } + const selections = field.selectionSet?.selections ?? []; + + const referenceWidgetInlineFragment: InlineFragmentNode = { + kind: Kind.INLINE_FRAGMENT, + selectionSet: { + kind: Kind.SELECTION_SET, + selections: [ + labelField, + iconURLField, + ownerIdField, + descriptionIdField, + referenceField, + referenceValuesField, + styleField, + ], + }, + typeCondition: { + kind: Kind.NAMED_TYPE, + name: { + kind: Kind.NAME, + value: 'ReferenceWidget', + }, + }, + }; + + return { + ...field, + selectionSet: { + ...field.selectionSet, + selections: [...selections, referenceWidgetInlineFragment], + }, + }; + }, + }); + } + return document; +});