Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions packages/core/src/i18n/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ export const TranslationsConfig = {
* See {@link Translations.getSpecialBundle} for handling identifiers with and without a dash.
*
* If internationalization is disabled, then the following variables should be overridden to reflect the current language of the system.
* These variables are cleared when i18n is disabled:
* - {@link CellRenderer.collapseExpandResource}
* These variables are cleared when i18n is disabled (the list may not be exhaustive):
* - {@link Editor.askZoomResource}
* - {@link Editor.currentFileResource}
* - {@link Editor.helpResource}
Expand All @@ -90,6 +89,7 @@ export const TranslationsConfig = {
* - {@link Editor.tasksResource}
* - {@link ElbowEdgeHandler.doubleClickOrientationResource}
* - {@link Graph.alreadyConnectedResource}.
* - {@link Graph.collapseExpandResource}
* - {@link Graph.containsValidationErrorsResource} and
* - {@link GraphSelectionModel.doneResource}
* - {@link GraphSelectionModel.updatingSelectionResource}
Expand Down
23 changes: 23 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1345,3 +1345,26 @@ export interface I18nProvider {
callback?: Function | null
): void;
}

export type GraphFoldingOptions = {
/**
* Specifies if folding (collapse and expand via an image icon in the graph should be enabled).
* @default true
*/
foldingEnabled: boolean;
/**
* Specifies the {@link ImageBox} to indicate a collapsed state.
* @default `Client.imageBasePath + '/collapsed.gif'`
*/
collapsedImage: ImageBox;
/**
* Specifies the {@link ImageBox} to indicate a expanded state.
* @default `Client.imageBasePath + '/expanded.gif'`
*/
expandedImage: ImageBox;
/**
* Specifies if the cell size should be changed to the preferred size when a cell is first collapsed.
* @default true
*/
collapseToPreferredSize: boolean;
};
9 changes: 9 additions & 0 deletions packages/core/src/view/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import EdgeSegmentHandler from './handler/EdgeSegmentHandler';
import ElbowEdgeHandler from './handler/ElbowEdgeHandler';
import type {
EdgeStyleFunction,
GraphFoldingOptions,
GraphPlugin,
GraphPluginConstructor,
MouseListenerSet,
Expand Down Expand Up @@ -395,6 +396,14 @@ class Graph extends EventSource {
? 'containsValidationErrors'
: '';

/** Folding options. */
options: GraphFoldingOptions = {
foldingEnabled: true,
collapsedImage: new Image(`${Client.imageBasePath}/collapsed.gif`, 9, 9),
expandedImage: new Image(`${Client.imageBasePath}/expanded.gif`, 9, 9),
collapseToPreferredSize: true,
};

// ===================================================================================================================
// Group: "Create Class Instance" factory functions.
// These can be overridden in subclasses of Graph to allow the Graph to instantiate user-defined implementations with
Expand Down
11 changes: 1 addition & 10 deletions packages/core/src/view/mixins/FoldingMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import Image from '../image/ImageBox';
import Client from '../../Client';
import Cell from '../cell/Cell';
import EventObject from '../event/EventObject';
import InternalEvent from '../event/InternalEvent';
Expand All @@ -37,10 +35,10 @@ type PartialGraph = Pick<
| 'getSelectionCells'
| 'stopEditing'
| 'batchUpdate'
| 'options'
>;
type PartialFolding = Pick<
Graph,
| 'options'
| 'collapseExpandResource'
| 'getCollapseExpandResource'
| 'isFoldingEnabled'
Expand All @@ -56,13 +54,6 @@ type PartialType = PartialGraph & PartialFolding;

// @ts-expect-error The properties of PartialGraph are defined elsewhere.
export const FoldingMixin: PartialType = {
options: {
foldingEnabled: true,
collapsedImage: new Image(`${Client.imageBasePath}/collapsed.gif`, 9, 9),
expandedImage: new Image(`${Client.imageBasePath}/expanded.gif`, 9, 9),
collapseToPreferredSize: true,
},

collapseExpandResource: isI18nEnabled() ? 'collapse-expand' : '',

getCollapseExpandResource() {
Expand Down
32 changes: 3 additions & 29 deletions packages/core/src/view/mixins/FoldingMixin.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,11 @@ limitations under the License.

import type Cell from '../cell/Cell';
import type CellState from '../cell/CellState';
import type Image from '../image/ImageBox';
import type ImageBox from '../image/ImageBox';
import type Geometry from '../geometry/Geometry';

export type GraphFoldingOptions = {
/**
* Specifies if folding (collapse and expand via an image icon in the graph should be enabled).
* @default true
*/
foldingEnabled: boolean;
/**
* Specifies the {@link Image} to indicate a collapsed state.
* @default `Client.imageBasePath + '/collapsed.gif'`
*/
collapsedImage: Image;
/**
* Specifies the {@link Image} to indicate a expanded state.
* @default `Client.imageBasePath + '/expanded.gif'`
*/
expandedImage: Image;
/**
* Specifies if the cell size should be changed to the preferred size when a cell is first collapsed.
* @default true
*/
collapseToPreferredSize: boolean;
};

declare module '../Graph' {
interface Graph {
/** Folding options. */
options: GraphFoldingOptions;

/**
* Specifies the resource key for the tooltip on the collapse/expand icon.
* If the resource for this key does not exist then the value is used as
Expand Down Expand Up @@ -75,11 +49,11 @@ declare module '../Graph' {
isCellFoldable: (cell: Cell, collapse: boolean) => boolean;

/**
* Returns the {@link Image} used to display the collapsed state of the specified cell state.
* Returns the {@link ImageBox} used to display the collapsed state of the specified cell state.
*
* This returns `null` for all edges.
*/
getFoldingImage: (state: CellState) => Image | null;
getFoldingImage: (state: CellState) => ImageBox | null;

/**
* Sets the collapsed state of the specified cells and all descendants if recurse is `true`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ limitations under the License.
*/

import {
type Cell,
type CellStateStyle,
getDefaultPlugins,
Graph,
InternalEvent,
SelectionHandler,
InternalMouseEvent,
RubberBandHandler,
SelectionHandler,
} from '@maxgraph/core';
import { globalTypes, globalValues } from './shared/args.js';
import { createGraphContainer } from './shared/configure.js';
import { createGraphContainer, createMainDiv } from './shared/configure.js';

export default {
title: 'Layouts/Constituent',
Expand All @@ -34,8 +38,15 @@ export default {
},
};

const Template = ({ label, ...args }) => {
const isNullish = (v: any): v is null | undefined => v === null || v === undefined;

const Template = ({ label, ...args }: Record<string, string>) => {
const div = createMainDiv(
`This example demonstrates using cells as parts of other cells.`
);

const container = createGraphContainer(args);
div.appendChild(container);

// Disables the built-in context menu
InternalEvent.disableContextMenu(container);
Expand All @@ -44,34 +55,42 @@ const Template = ({ label, ...args }) => {
/**
* Redirects start drag to parent.
*/
getInitialCellForEvent(me) {
override getInitialCellForEvent(me: InternalMouseEvent) {
let cell = super.getInitialCellForEvent(me);
if (this.graph.isPart(cell)) {
cell = cell.getParent();
if ((this.graph as MyCustomGraph).isPart(cell)) {
cell = cell?.getParent() ?? null;
}
return cell;
}
}

type CustomCellStateStyle = CellStateStyle & {
constituent: boolean;
};

class MyCustomGraph extends Graph {
constructor(container) {
super(container);
constructor(container: HTMLElement) {
super(container, undefined, [...getDefaultPlugins(), RubberBandHandler]);
this.options.foldingEnabled = false;
this.recursiveResize = true;
}

isPart(cell) {
// Helper method to mark parts with constituent=1 in the style
return this.getCurrentCellStyle(cell).constituent == '1';
// Helper method to mark parts with constituent in the Cell style
isPart(cell: Cell | null): boolean {
return (
!isNullish(cell) &&
(this.getCurrentCellStyle(cell) as CustomCellStateStyle).constituent
);
}

selectCellForEvent(cell, evt) {
// Redirects selection to parent
// Redirects selection to parent
override selectCellForEvent = (cell: Cell, evt: MouseEvent) => {
if (this.isPart(cell)) {
cell = cell.getParent();
const parent = cell.getParent();
!isNullish(parent) && (cell = parent);
}
super.selectCellForEvent(cell, evt);
}
};

createGraphHandler() {
return new MyCustomGraphHandler(this);
Expand All @@ -81,32 +100,25 @@ const Template = ({ label, ...args }) => {
// Creates the graph inside the given container
const graph = new MyCustomGraph(container);

// Enables rubberband selection
new RubberBandHandler(graph);

// Gets the default parent for inserting new cells. This
// is normally the first child of the root (ie. layer 0).
const parent = graph.getDefaultParent();

// Adds cells to the model in a single step
graph.batchUpdate(() => {
const v1 = graph.insertVertex({
parent,
parent: graph.getDefaultParent(),
position: [20, 20],
size: [120, 70],
});
const v2 = graph.insertVertex({
graph.insertVertex({
parent: v1,
value: 'Constituent',
position: [20, 20],
size: [80, 30],
style: {
constituent: 1,
},
constituent: true,
} as CustomCellStateStyle,
});
});

return container;
return div;
};

export const Default = Template.bind({});