diff --git a/canvas_modules/common-canvas/src/common-canvas/cc-context-toolbar.jsx b/canvas_modules/common-canvas/src/common-canvas/cc-context-toolbar.jsx index 5150610c8e..0f53b854f9 100644 --- a/canvas_modules/common-canvas/src/common-canvas/cc-context-toolbar.jsx +++ b/canvas_modules/common-canvas/src/common-canvas/cc-context-toolbar.jsx @@ -25,6 +25,7 @@ import ColorPicker from "../color-picker"; const CM_TOOLBAR_GAP = 2; const CM_ICON_SIZE = 32; const CM_ICON_PAD = 2; +const DIVIDER_SIZE = 1; const ICON_SIZE_PLUS_GAP = CM_ICON_SIZE + CM_TOOLBAR_GAP; const PADDING = 2; @@ -104,17 +105,22 @@ class CommonCanvasContextToolbar extends React.Component { // Returns the width of the context toolbar. getContextToolbarWidth(toolbarItems, overflowMenuItems) { + const dividers = toolbarItems.filter((i) => i.divider); + const dividersCount = dividers.length; + const dividersWidth = dividersCount * DIVIDER_SIZE; + // If there is at least one overflow item, we will need an overflow // icon which will increase the toolbar items by one. const overflowItemCount = overflowMenuItems.length > 0 ? 1 : 0; - const toolbarItemsCount = toolbarItems.length + overflowItemCount; + const buttonsCount = toolbarItems.length + overflowItemCount - dividersCount; + const buttonsWidth = (buttonsCount * (CM_ICON_SIZE + CM_ICON_PAD)); // If we have some overflow menu items, we reduce the width by five pixels // which forces the overflow menu and the overflow icon to be shown. We // use 5 pixels because this is how many are needed to make the toolbar // work correcty with differnet browser magnificaitons. const reduction = overflowMenuItems.length > 0 ? 5 : 0; - return (toolbarItemsCount * (CM_ICON_SIZE + CM_ICON_PAD)) - reduction; + return buttonsWidth + dividersWidth - reduction; } // Removes leading and trailing dividers from the items array and any @@ -210,7 +216,7 @@ class CommonCanvasContextToolbar extends React.Component { let contextToolbar = null; if (this.props.showContextMenu) { - const toolbarItems = this.props.contextMenuDef.filter((cmItem) => cmItem.toolbarItem && !cmItem.divider); + const toolbarItems = this.props.contextMenuDef.filter((cmItem) => cmItem.toolbarItem); let overflowMenuItems = this.props.contextMenuDef.filter((cmItem) => !cmItem.toolbarItem); overflowMenuItems = this.removeUnnecessaryDividers(overflowMenuItems); const toolbarConfig = this.getToolbarConfig({ toolbarItems, overflowMenuItems }); @@ -224,6 +230,7 @@ class CommonCanvasContextToolbar extends React.Component { : pos.x - toolbarWidth; let y = pos.y - ICON_SIZE_PLUS_GAP; + // Make sure the context toolbar is fully inside the viewport. ({ x, y } = this.adjustPosToFit(x, y, toolbarWidth, ICON_SIZE_PLUS_GAP)); contextToolbar = ( diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 770b6bf4d3..3d332e2baf 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -97,6 +97,7 @@ nav: - External Pipeline Flows: 03.08-external-pipeline-flows.md - Read Only or Locked Flows: 03.09-read-only-or-locked-flows.md - Commmand Stack: 03.10-command-stack.md + - Internal Actions: 03.11-internal-actions.md - Deprecated: - Context Menu Wrapper: 03.30.01-context-menu-wrapper.md - Flow Validation: 03.30.02-flow-validation.md diff --git a/docs/pages/03.03.01-context-menu-handler.md b/docs/pages/03.03.01-context-menu-handler.md index 070b79ddcc..8b1a679003 100644 --- a/docs/pages/03.03.01-context-menu-handler.md +++ b/docs/pages/03.03.01-context-menu-handler.md @@ -1,6 +1,6 @@ # Context Menu Handler -This callback is optional. You don't need to implement anything for it. If implemented it must return an array of actions that describe what options are displayed on the context menu or context toolbar. +This callback is optional. You don't need to implement anything for it. If implemented it must return an array of actions that describe what options are displayed on the [context menu](01.03-context-menu.md) or [context toolbar](01.04-context-toolbar.md). ## contextMenuHandler ```js @@ -8,22 +8,22 @@ This callback is optional. You don't need to implement anything for it. If imple ``` This callback is used for both 'context menus' and, if the [`enableContextToolbar`](03.02.01-canvas-config.md#enablecontexttoolbar) canvas config option is set to `true`, for 'context toolbars'. -If this callback is not provided common canvas will handle context menu/toolbars, and their actions, internally. You only need to implement this callback if you want to add or remove options to/from the context menu/toolbar. +If this callback is not provided common canvas will handle context menu/toolbars, and their actions, internally. You only need to implement this callback if you want to add or remove options to/from the context menu/toolbar or provide your own menus in place of the default ones. -### For Context Menu +## For Context Menu -For context menus this will be dependent on what object or set of objects the context menu was requested for by the user. +For context menus, this will be dependent on what object or set of objects the context menu was requested for by the user. -This callback will be called if the user performs a context menu gesture (such as mouse 'right click') on a: +This callback will be called if the user performs a context menu gesture (such as mouse 'right click' or clicking an ellipsis icon) on a: -* node -* link -* comment -* port -* on the canvas background or -* if a number of objects are selected, the combination of objects. +* Node +* Link +* Comment +* Port +* On the canvas background or +* Combination of objects - if a number of objects are selected -This callback must return an array that defines the menu to be displayed. If the callback is *not* provided, a default context menu (the defaultMenu passed into the handler) will be displayed by the common canvas. +#### 'source' parameter The source object passed in looks like this: ```js @@ -34,116 +34,117 @@ The source object passed in looks like this: mousePos: {x: "10", y:"20"} } ``` -**type** - Indicates type of object for which the context menu was selected. Can be "node", "port", "link", "canvas" or "comment" +**type** - Indicates the type of object for which the context menu was selected. Can be "node", "input_port", "output_port", "link", "canvas" or "comment" -**targetObject** - The object for which the context menu was requested. Only provided when type is "node" or "comment" +**targetObject** - The object for which the context menu was requested. Not provided when type is "canvas". -**selectedObjectIds** - An array containing the IDs of all currently selected nodes and/or comments +**selectedObjectIds** - An array containing the IDs of all currently selected nodes and/or comments and/or links. **mousePos** - An object containing the coords of the mouse when the context menu was requested -The callback would need to return an array, that describes the context menu to be displayed, that looks something like this: -```js - [ - {action: "deleteSelectedObjects", label: "Delete"}, - {divider: true}, - {action: "myAction", label: "My Action"} - {action: "myUnavailableAction", label: "A test disabled item", enable: false} - ] -``` -There is one element in the array for each entry in the context menu. An entry can be either a context menu item, which consists of a label and an action, or a divider, whose field would need to be set to true. - -Actions can be either internal (implemented inside the common canvas, like "deleteSelectedObjects" above) or external (like "myAction"). - -Existing internal actions are: - -* selectAll -* cut -* copy -* paste -* undo -* redo -* createSupernode -* expandSupernode -* collapseSupernode -* deleteSelectedObjects -* createComment -* deleteLink -* disconnectNode -* highlightBranch -* highlightDownstream -* highlightUpstream -* unhighlight - -External actions are custom actions you want common canvas to display for your application. To get common canvas to display your action you would need to return an array from the callback that includes a menu item for the action. - -When the user clicks the option in the context menu matching your action common canvas will call the [Edit Action Handler](03.03.03-edit-action-handler.md) callback so you'll need to implement some code in that callback to execute the intended action. If you want to simply add your action to the default context menu provided by common canvas you can take the defaultMenu parameter provided to the callback and add your menu item to it. Alternatively, you can provide a complete new context menu of your own. +#### 'defaultMenu' parameter -Here is a sample implementation of contextMenuHandler, which takes a source object (described above) and the defaultMenu as parameters, and adds a custom action to the default menu when the user 'right clicks' the canvas background. +This is an array describing the default menu that common-canvas would usually display. If necessary, you can modify this array with your own elements or remove elements and then return the modified array. + +### Return + +The callback must return an array, that describes the context menu to be displayed. If the callback returns a null, then no menu will be displayed. + +There is one element in the array for each entry in the context menu. An entry can be either a context menu item, which consists of a label and an action, or a divider, whose field would need to be set to true. An action can be disabled by setting the 'enable' field to false. + +Here's an example of a contextMenuHandler: ```js contextMenuHandler(source, defaultMenu) { - let customMenu = defaultMenu; - if (source.type === "canvas") { - customMenu = customMenu.concat({ action: "myAction", label: "My Action" }); - } - return customMenu; + if (source.type === "node") { + return [ + { action: "deleteSelectedObjects", label: "Delete" }, + { divider: true}, + { action: "myAction", label: "My Action" }, + { action: "paste", label: "Paste from clipboard", enable: false } + ]; + } + return defaultMenu; } ``` -In addition to adding the context menu item, you would also need to implement the [Edit Action Handler](03.03.03-edit-action-handler.md) callback to execute the action, like this: + +The above array will produce a context menu like this: + + + +When the user clicks an action in the menu the action is executed either internally or externally. + +### Internal acitons + +Internal actions are implemented inside the common canvas, like "deleteSelectedObjects" in the example above. Common-canvas supports a large number of [internal actions](03.11-internal-actions.md). + +### External actions + +External actions are custom actions you want common canvas to display for your application like "myAction", in the example above. +Tip: To avoid any future name clashes with internal actions that might be added it is recommended you should make sure you action names are unique. For example, by adding a prefix to your application specfic actions. + +### Handling actions + +When the user clicks an option in the context menu (or context toolbar) it causes the [Before Edit Action Handler](03.03.02-before-edit-action-handler.md) and then the [Edit Action Handler](03.03.03-edit-action-handler.md) callbacks to be called. + + +### Customizing the default context menu + + If you want to simply add your action to the default context menu provided by common canvas you can take the defaultMenu parameter provided to the callback, and add your menu item to it. Alternatively, you can provide a complete new context menu of your own. + +Here is a sample implementation of contextMenuHandler, which takes a source object (described above) and the defaultMenu as parameters, and adds a custom action to the default menu when the user 'right clicks' the canvas background. + ```js - editActionHandler(data) { - if (data.editType === "myAction") { - // Execute my action code here. - } + contextMenuHandler(source, defaultMenu) { + let customMenu = defaultMenu; + if (source.type === "canvas") { + customMenu = customMenu.concat({ action: "myAction", label: "My Action" }); + } + return customMenu; } ``` -Tip: To avoid any future name clashes with internal actions you should make sure you action names are unique. For example, you could add a prefix to your action names eg. `$MyApp_action` where `$MayApp_` is the prefix for all your actions. -### For Context Toolbar +## For Context Toolbar To display a context toolbar the same type of array is returned as described above for context menu. However, there are some extra fields for the action elements in the array. These are * isToolbarItem: This is a boolean. The default is false. If set to true the action will be added to the toolbar and if set to false the action will be displayed in the overflow menu. -* icon: This is the icon to display for the action. If `isToolbarItem` is set to true you must provide an icon otherwise the action will show as an empty space in the toolbar. If an icon is specified and `isToolbarItem` is set to false, teh icon will be displayed next to the action in the overflow menu. - - -The contents of the context toolbar is dependent on which object the mouse cursor is currently hovering over (which may be different to any of the currently selected objects). You should make sure the actions you return in the array are applicable to the object the mouse cursor is hovering over or, if it is hovering over a selected object and other objects are alos selected, to the set of selected objects. - -## Action names with built in icons - -If you use any of the following action names common-canvas will automatically display an appropriate Carbon icon for that action either if it appears as a button in the toolbar or if it appears in the overflow menu. - -| Action | Carbon Icon | -|--------------------------|---------------------| -| stop | StopFilledAlt | -| run | Play | -| undo | Undo | -| redo | Redo | -| cut | Cut | -| copy | Copy | -| paste | Paste | -| clipboard | Result | -| createComment | AddComment | -| createAutoComment | AddComment | -| setCommentEditingMode | Edit | -| setNodeLabelEditingMode | Edit | -| commentsShow | Chat | -| commentsHide | ChatOff | -| colorBackground | ColorPalette | -| deleteLink | TrashCan | -| deleteSelectedObjects | TrashCan | -| zoomIn | ZoomIn | -| zoomOut | ZoomOut | -| zoomToFit | CenterToFit | -| arrangeHorizontally | ArrangeHorizont | -| arrangeVertically | ArrangeVertical | -| toggleNotificationPanel | NotificationCounter | -| paletteOpen | OpenPanelFilledLeft | -| paletteClose | OpenPanelFilledLeft | -| paletteToggle | OpenPanelFilledLeft | -| expandSuperNodeInPlace | Maximize | -| collapseSuperNodeInPlace | Minimize | -| displaySubPipeline | Launch | +* icon: This is the icon to display for the action. If `isToolbarItem` is set to true you must provide an icon otherwise the action will show as an empty space in the toolbar. If an icon is specified and `isToolbarItem` is set to false, the icon will be displayed next to the action in the overflow menu. For many internal actions, common canvas will automatically display an appropriate carbon icon. See the [Internal Actions page](03.11-internal-actions.md/#action-names-with-built-in-icons) for a list of actions that have associated icons. + +Dividers can also be added to the context toolbar by specifying 'toolbarItem: true' + +```js + import { Add } from "@carbon/react/icons"; + + ... + + contextMenuHandler(source, defaultMenu) { + if (source.type === "node") { + return [ + { action: "deleteSelectedObjects", label: "Delete", toolbarItem: true }, + { divider: true, toolbarItem: true }, + { action: "myAction1", label: "My Action1", toolbarItem: true, icon: () }, + { action: "disconnectNode", label: "Disconnect", enable: false }, + { action: "cut", label: "Cut" }, + { action: "copy", label: "Copy" } + ]; + } + return defaultMenu; + } +``` +This will produce a context toolbar like this: + + + +And when the overflow icon is clicked, like this: + + + + +#### Warning +The contents of the context toolbar is dependent on which object the mouse cursor is currently **hovering** over (which may be different to any of the **currently selected** objects). You should make sure the actions you return in the array are applicable to the object the mouse cursor is hovering over or, if it is hovering over a selected object and other objects are alos selected, to the set of selected objects. + +To help decide whether the mouse cursor is hovering over a selected object or not, the application can call the canvas controller's helper function: 'isContextToolbarForNonSelectedObj(source)'. This will return true if the mouse cursor is over a non-selected object. + diff --git a/docs/pages/03.03.03-edit-action-handler.md b/docs/pages/03.03.03-edit-action-handler.md index 1ad788f0d6..5def7c0af5 100644 --- a/docs/pages/03.03.03-edit-action-handler.md +++ b/docs/pages/03.03.03-edit-action-handler.md @@ -1,21 +1,18 @@ # Edit Action Handler -This callback is optional. You don't *need* to implement anything for it and it doesn't return anything. It is called whenever the user does the following types of action on the canvas: +This callback is optional. You don't *need* to implement anything for it and it doesn't return anything. It is called whenever the user does the following gestures on the canvas: * Clicks a tool/icon in the toolbar. * Clicks an option in the context menu or context toolbar * Presses a [key combination](03.05-keyboard-support.md) on the keyboard to cause the canvas to change. * Performs some direct manipulation on the canvas such as: - * Creates a node (createNode) - * Moves one or a set of nodes/comments (moveObjects) - * Edits a comment (editComment) - * Links two nodes together (linkNodes) - * Links a comment to a node (linkComment) - * Resizes a supernode (resizeObjects) - * Resizes a comment (editComment) - * Expands a supernode in place (expandSuperNodeInPlace) - * Navigates into a sub-flow in a supernode (displaySubPipeline) - * Navigates out of a sub-flow in a supernode (displayPreviousPipeline) + * Create a node + * Moves one or a set of nodes/comments + * Edits a comment + * Links two nodes together + * etc + +These is will either perform one of the many [internal actions](03.11-internal-actions.md) supported by common canvas or, if you have customized the context menu/toolbar or the maon canvas toolbar, your own external action. ## editActionHandler ```js @@ -48,3 +45,18 @@ This callback is called *after* the common-canvas internal object model has been 2. **command parameter** - This is a Javascript class which is the command object that was added to the command stack and executed to run the action 'requested' by the user. If the user performed an `undo` action this will be the command that has been undone. If the user performed a `redo` action this will be the command that was redone. The command object may contain fields which are connected with the execution of the command. +### Handling external action + +If you specified your application's own 'external' action you can do whatever is necessary in this callback. + +The `editType` field of the first parameter, passed in to the callback, will be set to the action name. + +Here's a simple expmple: + +```js + editActionHandler(data, command) { + if (data.editType === "myAction") { + // Execute my action code here. + } + } +``` diff --git a/docs/pages/03.11-internal-actions.md b/docs/pages/03.11-internal-actions.md new file mode 100644 index 0000000000..7c6e5ffe73 --- /dev/null +++ b/docs/pages/03.11-internal-actions.md @@ -0,0 +1,136 @@ +# Internal common-canvas actions + +Internal actions can be generated by the user either: + +* Clicking a toolbar button +* Clicking an option in a context menu or context toobar +* Pressing a keyboard short cut key(s) +* Doing some direct manipulation like dragging one or more objects + +Occassionally, actions can be generated internally by common canvas but this is quite rare. +Some actions can be generated from different sources. For example, the "deleteSelectedObjects" action can be generated by the user: + +* Clicking the trash can icon in the toolbar or +* Pressing the Delete key +* Clicking the Delete option in a context menu. + +In each case, the action causes the [editActionHandler](03.03.03-edit-action-handler.md) callback to be called with the `editType` field of the first parameter passed in to be set to one of the actions listed below. + +Theese are the intenal actions: + +* "selectAll" + +* "zoomIn" +* "zoomOut" +* "zoomToFit" +* "setZoom" + +* "commentsToggle" +* "commentsHide" +* "commentsShow" + +* "setCommentEditingMode" +* "setNodeLabelEditingMode" + +* "paletteToggle" +* "paletteOpen" +* "paletteClose" + +* "toggleNotificationPanel" +* "openNotificationPanel" +* "closeNotificationPanel" + +* "createNode" +* "createNodeOnLink" +* "createNodeAttachLinks" +* "createAutoNode" + +* "createComment" +* "createAutoComment" + +* "insertNodeIntoLink" +* "attachNodeToLinks" + +* "moveObjects" +* "resizeObjects" +* "setObjectsStyle" +* "setLinksStyle" + +* "updateLink" +* "setNodeLabel" +* "editComment" +* "editDecorationLabel" +* "linkNodes" +* "linkNodesAndReplace" +* "linkComment" +* "createDetachedLink" +* "colorSelectedObjects" +* "deleteSelectedObjects" +* "displaySubPipeline" +* "displayPreviousPipeline" + +* "arrangeHorizontally" +* "arrangeVertically" + +* "createSuperNode" +* "createSuperNodeExternal" +* "deconstructSuperNode" +* "expandSuperNodeInPlace" +* "collapseSuperNodeInPlace" +* "convertSuperNodeExternalToLocal" +* "convertSuperNodeLocalToExternal" +* "deleteLink" +* "disconnectNode" +* "saveToPalette" +* "loadPipelineFlow" + + +* "cut" +* "copy" +* "paste" + +* "undo" +* "redo" + +* "highlightBranch" +* "highlightDownstream" +* "highlightUpstream" +* "unhighlight" + +## Action names with built in icons + +If you use any of the following action names common-canvas will automatically display an appropriate Carbon icon for that action either if it appears as a button in the toolbar or if it appears in the overflow menu. + +| Action | Carbon Icon | +|--------------------------|---------------------| +| stop | StopFilledAlt | +| run | Play | +| undo | Undo | +| redo | Redo | +| cut | Cut | +| copy | Copy | +| paste | Paste | +| clipboard | Result | +| createComment | AddComment | +| createAutoComment | AddComment | +| setCommentEditingMode | Edit | +| setNodeLabelEditingMode | Edit | +| commentsShow | Chat | +| commentsHide | ChatOff | +| colorBackground | ColorPalette | +| deleteLink | TrashCan | +| deleteSelectedObjects | TrashCan | +| zoomIn | ZoomIn | +| zoomOut | ZoomOut | +| zoomToFit | CenterToFit | +| arrangeHorizontally | ArrangeHorizont | +| arrangeVertically | ArrangeVertical | +| toggleNotificationPanel | NotificationCounter | +| paletteOpen | OpenPanelFilledLeft | +| paletteClose | OpenPanelFilledLeft | +| paletteToggle | OpenPanelFilledLeft | +| expandSuperNodeInPlace | Maximize | +| collapseSuperNodeInPlace | Minimize | +| displaySubPipeline | Launch | + + diff --git a/docs/pages/assets/context-menu.png b/docs/pages/assets/context-menu.png new file mode 100644 index 0000000000..901e2270bb Binary files /dev/null and b/docs/pages/assets/context-menu.png differ diff --git a/docs/pages/assets/context-toolbar-open-overflow.png b/docs/pages/assets/context-toolbar-open-overflow.png new file mode 100644 index 0000000000..3b2df542cc Binary files /dev/null and b/docs/pages/assets/context-toolbar-open-overflow.png differ diff --git a/docs/pages/assets/context-toolbar.png b/docs/pages/assets/context-toolbar.png new file mode 100644 index 0000000000..8688b18833 Binary files /dev/null and b/docs/pages/assets/context-toolbar.png differ diff --git a/docs/pages/assets/ellipsis-icon.png b/docs/pages/assets/ellipsis-icon.png new file mode 100644 index 0000000000..bcf8616f84 Binary files /dev/null and b/docs/pages/assets/ellipsis-icon.png differ