Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attribute manipulation to the CME #867

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
15de329
Add textual menu for drag edge and functionality
RadStr Jan 17, 2025
ba5736c
Change Selection UI based on issue
RadStr Jan 17, 2025
d8059da
Fix setSelectedNodes to work with internal selection
RadStr Jan 17, 2025
2278f62
Fix not adding multi-edges in selection extension
RadStr Jan 17, 2025
26bd18f
Add option to extend selection only through edges
RadStr Jan 17, 2025
26b65d7
Save attributes to vis model + tests + visibility
RadStr Jan 19, 2025
f8a0087
Add dialog to set attribute order on node
RadStr Jan 19, 2025
fd42f38
Show names instead of ids for attributes
RadStr Jan 20, 2025
7ba414e
Fix not removing attribute from vis model on del
RadStr Jan 20, 2025
b7561c4
WIP - Fix removal of selection node drag
RadStr Jan 21, 2025
ee9fa36
Remove notDraggedNodes reference, using all selected instead
RadStr Jan 21, 2025
7bbb244
Fix unselecting whole group when only part is unselected
RadStr Jan 21, 2025
f7beb24
npm run lint and remove debug prints
RadStr Jan 21, 2025
cca2374
Remove debug print on node
RadStr Jan 21, 2025
81b5d6f
Fix sticky groups
RadStr Jan 21, 2025
df4a0be
Finalize Edit attributes on node dialog
RadStr Jan 21, 2025
a857a66
Fix selection extension not working correctly
RadStr Jan 22, 2025
bf7aeed
Merge branch 'main' into cme/UI-changes
RadStr Jan 22, 2025
2b5524f
Add TODOs
RadStr Jan 22, 2025
ac58627
Merge branch 'cme/UI-changes' into cme-feature/attribute-manipulation
RadStr Jan 22, 2025
d141295
Fix build errors
RadStr Jan 22, 2025
4e88452
Respect attribute order on reopen in dragdrop
RadStr Jan 22, 2025
24dc066
Try to improve edit attr dialog style
RadStr Jan 22, 2025
6964c8c
Merge branch 'main' into cme-feature/attribute-manipulation-package-l…
RadStr Jan 22, 2025
c83fb13
Add dependency for drag-drop in cme
RadStr Jan 22, 2025
324d9d5
Change icons and add key to react component
RadStr Jan 22, 2025
be8b5d4
Add TODOs, fix typos and remove create class on drag edge
RadStr Jan 22, 2025
9b88336
Remove the rounded corners in dnd dialog
RadStr Jan 23, 2025
513c37c
Add button that creates new attribute to the dnd dialog
RadStr Jan 29, 2025
cccc0e6
Add localization for dnd, simplify, move logic to controller
RadStr Jan 30, 2025
b3ed4db
Merge branch 'main' into cme-feature/attribute-manipulation-package-l…
RadStr Jan 30, 2025
805f43e
Code review fixes
RadStr Feb 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion applications/conceptual-model-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"iri": "^1.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tsx": "^4.19.2"
"tsx": "^4.19.2",
"@hello-pangea/dnd": "17.0.0"
},
"devDependencies": {
"@eslint/config-array": "^0.19.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { addSemanticClassProfileToVisualModelAction } from "./add-class-profile-
import { addSemanticGeneralizationToVisualModelAction } from "./add-generalization-to-visual-model";
import { addSemanticRelationshipToVisualModelAction } from "./add-relationship-to-visual-model";
import { addSemanticRelationshipProfileToVisualModelAction } from "./add-relationship-profile-to-visual-model";
import { EntityToDelete, convertToEntitiesToDeleteType, findTopLevelGroupFromVisualModel, getSelections, getViewportCenterForClassPlacement, setSelectionsInDiagram } from "./utilities";
import { EntityToDelete, checkIfIsAttributeOrAttributeProfile, convertToEntitiesToDeleteType, findTopLevelGroupFromVisualModel, getSelections, getViewportCenterForClassPlacement, setSelectionsInDiagram } from "./utilities";
import { removeFromVisualModelAction } from "./remove-from-visual-model";
import { removeFromSemanticModelsAction } from "./remove-from-semantic-model";
import { openCreateAttributeDialogAction } from "./open-create-attribute-dialog";
Expand All @@ -49,6 +49,13 @@ import { EntityModel } from "@dataspecer/core-v2";
import { openCreateAttributeForEntityDialogAction } from "./open-add-attribute-for-entity-dialog";
import { addGroupToVisualModelAction } from "./add-group-to-visual-model";
import { removeTopLevelGroupFromVisualModelAction } from "./remove-group-from-visual-model";
import { openCreateClassDialogAndCreateAssociationAction, openCreateClassDialogAndCreateGeneralizationAction } from "./open-create-class-dialog-with-edge";
import { removeAttributesFromVisualModelAction } from "./remove-attribute-from-visual-model";
import { addSemanticAttributeToVisualModelAction } from "./add-semantic-attribute-to-visual-model";
import { ShiftAttributeDirection, shiftAttributePositionAction } from "./shift-attribute";
import { openEditNodeAttributesDialogAction } from "./open-edit-node-attributes-dialog";
import { EditAttributeDialogState } from "../dialog/attribute/edit-attribute-dialog-controller";
import { EditAttributeProfileDialogState } from "../dialog/attribute-profile/edit-attribute-profile-dialog-controller";

const LOG = createLogger(import.meta.url);

Expand Down Expand Up @@ -90,7 +97,22 @@ interface DialogActions {
* Opens dialog, which purpose is to create new attribute in model identified by {@link model}.
* @param model is the identifier of the semantic model.
*/
openCreateAttributeDialog: (model: string) => void;
openCreateAttributeDialogForModel: (model: string) => void;

/**
* Opens dialog, which purpose is to create new attribute with domain class identified by {@link classIdentifier}.
* On successful creation {@link onConfirmCallback} is called.
* @param classIdentifier is the identifier of the class, which will be domain for the attribute.
* @param onConfirmCallback This callback is called after we sucessfully create attribute.
* Set to null, if there is no callback.
*/
openCreateAttributeDialogForClass: (
classIdentifier: string,
onConfirmCallback: ((state: EditAttributeDialogState | EditAttributeProfileDialogState, createdAttributeIdentifier: string) => void) | null
) => void;

// TODO RadStr: Document
openEditNodeAttributesDialog: (nodeIdentifier: string) => void;

/**
* @deprecated Use specialized method for given entity type.
Expand Down Expand Up @@ -164,6 +186,11 @@ interface VisualModelActions {
*/
addRelationProfileToVisualModel: (model: string, identifier: string) => void;

// TODO RadStr: Document
addAttributeToVisualModel: (attribute: string, domainClass: string | null) => void;
shiftAttributeUp: (attribute: string, domainNode: string | null) => void;
shiftAttributeDown: (attribute: string, domainNode: string | null) => void;

// TODO PRQuestion - different docs from this method and for the actual action
/**
* Removes the visual entities identified by given {@link identifier} from visual model.
Expand All @@ -172,6 +199,9 @@ interface VisualModelActions {
*/
removeFromVisualModel: (identifiers: string[]) => void;

// TODO RadStr: Document
removeAttributesFromVisualModel: (attributes: string[]) => void;

//

//
Expand Down Expand Up @@ -249,7 +279,9 @@ const noOperationActionsContext = {
openModifyDialog: noOperation,
openCreateClassDialog: noOperation,
openCreateAssociationDialog: noOperation,
openCreateAttributeDialog: noOperation,
openCreateAttributeDialogForModel: noOperation,
openCreateAttributeDialogForClass: noOperation,
openEditNodeAttributesDialog: noOperation,
openCreateProfileDialog: noOperation,
//
addSemanticEntitiesToVisualModel: noOperation,
Expand All @@ -258,9 +290,13 @@ const noOperationActionsContext = {
addGeneralizationToVisualModel: noOperation,
addRelationToVisualModel: noOperation,
addRelationProfileToVisualModel: noOperation,
addAttributeToVisualModel: noOperation,
shiftAttributeUp: noOperation,
shiftAttributeDown: noOperation,
deleteFromSemanticModels: noOperation,
//
removeFromVisualModel: noOperation,
removeAttributesFromVisualModel: noOperation,
centerViewportToVisualEntity: noOperation,
//
createNewVisualModelFromSelection: noOperation,
Expand Down Expand Up @@ -454,11 +490,14 @@ function createActionsContext(
});
}

const addAttributeForNode = (identifier: string) => {
const openCreateAttributeDialogForClass = (
classIdentifier: string,
onConfirmCallback: ((state: EditAttributeDialogState | EditAttributeProfileDialogState, createdAttributeIdentifier: string) => void) | null
) => {
withVisualModel(notifications, graph, (visualModel) => {
openCreateAttributeForEntityDialogAction(
options, dialogs, classes, graph, notifications,
visualModel, identifier);
visualModel, classIdentifier, onConfirmCallback);
});
};

Expand Down Expand Up @@ -486,7 +525,7 @@ function createActionsContext(
if (modelInstance === null || modelInstance instanceof InMemorySemanticModel) {
openCreateClassDialogAction(
options, dialogs, classes, graph, notifications, visualModel,
diagram, modelInstance, null);
diagram, modelInstance, null, null);
} else {
notifications.error("Can not add to given model.");
}
Expand All @@ -504,18 +543,24 @@ function createActionsContext(
}
};

const openCreateAttributeDialog = (model: string) => {
const openCreateAttributeDialogForModel = (model: string) => {
const visualModel = graph.aggregatorView.getActiveVisualModel();
const modelInstance = graph.models.get(model);
if (modelInstance === null || modelInstance instanceof InMemorySemanticModel) {
openCreateAttributeDialogAction(
options, dialogs, classes, graph, notifications, visualModel,
modelInstance);
options, dialogs, classes, graph, notifications,
visualModel, modelInstance);
} else {
notifications.error("Can not add to given model.");
}
};

const openEditNodeAttributesDialog = (nodeIdentifier: string) => {
withVisualModel(notifications, graph, (visualModel) => {
openEditNodeAttributesDialogAction(
dialogs, notifications, classes, options, visualModel, nodeIdentifier);
});
};
// Visual model actions.

const addSemanticEntitiesToVisualModel = (entities: EntityToAddToVisualModel[]): void => {
Expand Down Expand Up @@ -560,18 +605,62 @@ function createActionsContext(
});
};

const addAttributeToVisualModel = (attribute: string, domainClass: string | null): void => {
if(domainClass === null) {
notifications.error("Adding attribute to domain class which is null");
RadStr marked this conversation as resolved.
Show resolved Hide resolved
return;
}
withVisualModel(notifications, graph, (visualModel) => {
addSemanticAttributeToVisualModelAction(notifications, visualModel, domainClass, attribute, null);
});
};

const shiftAttributeUp = (attribute: string, domainNode: string | null): void => {
if(domainNode === null) {
notifications.error("Shifting attribute in domain node which is null");
return;
}
withVisualModel(notifications, graph, (visualModel) => {
shiftAttributePositionAction(notifications, visualModel, domainNode, attribute, ShiftAttributeDirection.UP, 1);
});
};

const shiftAttributeDown = (attribute: string, domainNode: string | null): void => {
if(domainNode === null) {
notifications.error("Shifting attribute in domain node which is null");
return;
}
withVisualModel(notifications, graph, (visualModel) => {
shiftAttributePositionAction(notifications, visualModel, domainNode, attribute, ShiftAttributeDirection.DOWN, 1);
});
};

const removeFromVisualModel = (identifiers: string[]): void => {
withVisualModel(notifications, graph, (visualModel) => {
removeFromVisualModelAction(notifications, visualModel, identifiers);
});
};

const removeAttributesFromVisualModel = (attributes: string[]): void => {
withVisualModel(notifications, graph, (visualModel) => {
removeAttributesFromVisualModelAction(notifications, classes, visualModel, attributes);
});
};

// ...

const deleteFromSemanticModels = (entitiesToDelete: EntityToDelete[]) => {
// We start be removing from the visual model.
withVisualModel(notifications, graph, (visualModel) => {
removeFromVisualModelAction(notifications, visualModel, entitiesToDelete.map(entitiesToDelete => entitiesToDelete.identifier));
const entityToDeleteWithAttributeData = entitiesToDelete.map(entityToDelete =>
({...entityToDelete,
isAttributeOrAttributeProfile: checkIfIsAttributeOrAttributeProfile(entityToDelete.identifier, graph.models, entityToDelete.sourceModel)
})
);
const attributesToBeDeleted = entityToDeleteWithAttributeData.filter(entity => entity.isAttributeOrAttributeProfile);
const notAttributesToBeDeleted = entityToDeleteWithAttributeData.filter(entity => !entity.isAttributeOrAttributeProfile);
removeFromVisualModelAction(notifications, visualModel, notAttributesToBeDeleted.map(entitiesToDelete => entitiesToDelete.identifier));
removeAttributesFromVisualModelAction(notifications, classes, visualModel, attributesToBeDeleted.map(entitiesToDelete => entitiesToDelete.identifier));
});
removeFromSemanticModelsAction(notifications, graph, entitiesToDelete);
};
Expand Down Expand Up @@ -706,7 +795,7 @@ function createActionsContext(

onChangeWaypointPositions: changeWaypointPositions,

onAddAttributeForNode: (node) => addAttributeForNode(node.externalIdentifier),
onAddAttributeForNode: (node) => openCreateAttributeDialogForClass(node.externalIdentifier, null),

onCreateConnectionToNode: (source, target) => {
openCreateConnectionDialog(source.externalIdentifier, target.externalIdentifier);
Expand Down Expand Up @@ -759,11 +848,30 @@ function createActionsContext(
},
onShowFilterSelection: () => {
const selectionToFilter = getSelections(diagram, false, true);
openFilterSelectionDialog({ nodeSelection: selectionToFilter.nodeSelection, edgeSelection: selectionToFilter.edgeSelection, areVisualModelIdentifiers: true });
openFilterSelectionDialog({
nodeSelection: selectionToFilter.nodeSelection,
edgeSelection: selectionToFilter.edgeSelection,
areVisualModelIdentifiers: true
});
},
onCanvasOpenCreateClassDialog: (nodeIdentifier, positionToPlaceClassOn) => {
withVisualModel(notifications, graph, (visualModel) => {
openCreateClassDialogWithModelDerivedFromClassAction(notifications, graph, dialogs, classes, options, diagram, visualModel, nodeIdentifier, positionToPlaceClassOn);
openCreateClassDialogWithModelDerivedFromClassAction(notifications, graph, dialogs, classes, options,
diagram, visualModel, nodeIdentifier, positionToPlaceClassOn, null);
});
},
onCanvasOpenCreateClassDialogWithAssociation: (nodeIdentifier, positionToPlaceClassOn, isCreatedClassTarget) => {
withVisualModel(notifications, graph, (visualModel) => {
openCreateClassDialogAndCreateAssociationAction(notifications, dialogs, classes, options, graph,
diagram, visualModel, nodeIdentifier, isCreatedClassTarget, positionToPlaceClassOn);
});
},
onCanvasOpenCreateClassDialogWithGeneralization: (nodeIdentifier, positionToPlaceClassOn, isCreatedClassParent) => {
withVisualModel(notifications, graph, (visualModel) => {
openCreateClassDialogAndCreateGeneralizationAction(
notifications, dialogs, classes, useClasses, options, graph, diagram,
visualModel, nodeIdentifier, isCreatedClassParent, positionToPlaceClassOn,
);
});
},
onCreateNewViewFromSelection: () => {
Expand Down Expand Up @@ -797,7 +905,9 @@ function createActionsContext(
openModifyDialog,
openCreateClassDialog,
openCreateAssociationDialog,
openCreateAttributeDialog,
openCreateAttributeDialogForModel,
openCreateAttributeDialogForClass,
openEditNodeAttributesDialog,
openCreateProfileDialog,
//
addSemanticEntitiesToVisualModel,
Expand All @@ -806,7 +916,11 @@ function createActionsContext(
addGeneralizationToVisualModel,
addRelationToVisualModel,
addRelationProfileToVisualModel,
addAttributeToVisualModel,
shiftAttributeUp,
shiftAttributeDown,
removeFromVisualModel,
removeAttributesFromVisualModel,
//
deleteFromSemanticModels,
centerViewportToVisualEntity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ export const addClassNeighborhoodToVisualModelAction = (
// We have to filter the source class, whose neighborhood we are adding, from the extension
classesOrClassProfilesToAdd.push(...neighborhood.selectionExtension.nodeSelection.filter(node => node !== identifier).map(node => ({identifier: node, position: null})));
addSemanticEntitiesToVisualModelAction(notifications, classes, graph, visualModel, diagram, classesOrClassProfilesToAdd);
})
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ClassesContextType } from "../context/classes-context";
import { findPositionForNewNodesUsingLayouting } from "./layout-visual-model";
import { findSourceModelOfEntity } from "../service/model-service";
import { createLogger } from "../application";
import { getVisualNodeContentBasedOnExistingEntities } from "./add-semantic-attribute-to-visual-model";

const LOG = createLogger(import.meta.url);

Expand Down Expand Up @@ -43,20 +44,23 @@ export async function addSemanticClassProfileToVisualModelAction(
entityIdentifier, model.getId(),
isSemanticModelClassUsage, (entity) => {
addSemanticClassProfileToVisualModelCommand(
visualModel, entity, model.getId(),
position);
classes, visualModel, entity,
model.getId(), position);
addRelatedEntitiesAction(
notifications, graph, visualModel, Object.values(entities),
graph.models, entity);
});
}

function addSemanticClassProfileToVisualModelCommand(
classes: ClassesContextType,
visualModel: WritableVisualModel,
entity: SemanticModelClassUsage,
model: string,
position: { x: number, y: number },
) {
const content = getVisualNodeContentBasedOnExistingEntities(
classes, entity);
visualModel.addVisualNode({
model: model,
representedEntity: entity.id,
Expand All @@ -65,7 +69,7 @@ function addSemanticClassProfileToVisualModelCommand(
y: position.y,
anchored: null,
},
content: [],
content,
visualModels: [],
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { addRelatedEntitiesAction } from "./add-related-entities-to-visual-model
import { findPositionForNewNodesUsingLayouting } from "./layout-visual-model";
import { ClassesContextType } from "../context/classes-context";
import { addVisualNode } from "../dataspecer/visual-model/command/add-visual-node";
import { getVisualNodeContentBasedOnExistingEntities } from "./add-semantic-attribute-to-visual-model";

export async function addSemanticClassToVisualModelAction(
notifications: UseNotificationServiceWriterType,
Expand All @@ -28,7 +29,10 @@ export async function addSemanticClassToVisualModelAction(
withAggregatedEntity(notifications, entities,
entityIdentifier, modelIdentifier,
isSemanticModelClass, (entity) => {
addVisualNode(visualModel, entity, modelIdentifier, position);
// TODO PRQuestion: How to handle this? Put it into the addVisualNode?
const content = getVisualNodeContentBasedOnExistingEntities(
classes, entity);
addVisualNode(visualModel, entity, modelIdentifier, position, content);
addRelatedEntitiesAction(
notifications, graph, visualModel, Object.values(entities),
graph.models, entity);
Expand Down
Loading