# Context Menu > Built-in cell context menu with copy/paste and custom actions. --- .. llms_copy::references/context_menu .. toc:: ### Overview The built-in cell context menu provides a native right-click menu for cells with support for copy/paste actions and custom menu items. The menu is theme-aware and can be configured with scroll behavior and height limits. ### Basic Usage [dgg] Right-click any cell to open the context menu. Use `contextMenuConfig` to define menu items and `contextMenuScrollBehavior` to control scroll interactions. .. exec::examples.reference.context_menu.basic :code: false ```python # File: examples/reference/context_menu/basic.py import dash_glide_grid as dgg import dash_mantine_components as dmc from dash import callback, Input, Output, State, no_update # Column definitions COLUMNS = [ {"title": "ID", "id": "id", "width": 60}, {"title": "Product", "id": "product", "width": 150}, {"title": "Category", "id": "category", "width": 120}, {"title": "Price", "id": "price", "width": 100}, {"title": "Stock", "id": "stock", "width": 80}, ] # Initial sample data INITIAL_DATA = [ { "id": 1, "product": "Laptop Pro", "category": "Electronics", "price": 1299.99, "stock": 45, }, { "id": 2, "product": "Wireless Mouse", "category": "Accessories", "price": 49.99, "stock": 200, }, { "id": 3, "product": "USB-C Hub", "category": "Accessories", "price": 79.99, "stock": 150, }, { "id": 4, "product": "Monitor 27", "category": "Electronics", "price": 449.99, "stock": 30, }, { "id": 5, "product": "Keyboard RGB", "category": "Accessories", "price": 129.99, "stock": 85, }, { "id": 6, "product": "Webcam HD", "category": "Electronics", "price": 89.99, "stock": 120, }, { "id": 7, "product": "Desk Lamp", "category": "Office", "price": 39.99, "stock": 75, }, { "id": 8, "product": "Chair Ergonomic", "category": "Furniture", "price": 299.99, "stock": 20, }, { "id": 9, "product": "Standing Desk", "category": "Furniture", "price": 599.99, "stock": 15, }, { "id": 10, "product": "Headphones Pro", "category": "Electronics", "price": 249.99, "stock": 60, }, { "id": 11, "product": "Mouse Pad XL", "category": "Accessories", "price": 29.99, "stock": 300, }, { "id": 12, "product": "Monitor Arm", "category": "Accessories", "price": 89.99, "stock": 45, }, ] # Context menu configuration - built-in actions + custom Python callback actions CONTEXT_MENU_CONFIG = { "maxHeight": "300px", "items": [ { "id": "copy", "label": "Copy Cell", "icon": "\u29c9", "action": "copyClickedCell", }, { "id": "copy-selection", "label": "Copy Selection", "icon": "\u29c9", "action": "copySelection", }, { "id": "paste-cell", "label": "Paste at Cell", "icon": "\u2398", "iconSize": "19px", "action": "pasteAtClickedCell", }, { "id": "paste-selection", "label": "Paste at Selection", "icon": "\u2398", "iconSize": "19px", "action": "pasteAtSelection", "dividerAfter": True, }, # Built-in clear actions { "id": "clear", "label": "Clear Cell", "icon": "\u2300", "action": "clearClickedCell", }, { "id": "clear-selection", "label": "Clear Selection", "icon": "\u2300", "action": "clearSelection", "dividerAfter": True, }, # Custom actions handled by Python callback { "id": "delete", "label": "Delete Row", "icon": "✕", "iconWeight": "800", "color": "#dc2626", "iconColor": "#dc2626", }, { "id": "details", "label": "View Details", "icon": "\u24d8", "iconSize": "11px", "iconWeight": "800", "color": "#4762BC", "iconColor": "#4762BC", }, ], } SCROLL_BEHAVIOR_OPTIONS = [ {"value": "default", "label": "default"}, {"value": "close-overlay-on-scroll", "label": "close-overlay-on-scroll"}, {"value": "lock-scroll", "label": "lock-scroll"}, ] MAX_HEIGHT_OPTIONS = [ {"value": "none", "label": "none (no limit)"}, {"value": "100px", "label": "100px"}, {"value": "150px", "label": "150px"}, {"value": "200px", "label": "200px"}, {"value": "250px", "label": "250px"}, {"value": "300px", "label": "300px"}, ] component = dmc.Stack( [ dmc.Paper( dmc.Stack( [ dmc.Text("Context Menu Options", fw=600, size="sm"), dmc.Group( [ dmc.Select( id="context-menu-scroll-behavior", label="contextMenuScrollBehavior", data=SCROLL_BEHAVIOR_OPTIONS, value="default", size="xs", w=200, allowDeselect=False, ), dmc.Select( id="context-menu-max-height", label="maxHeight", data=MAX_HEIGHT_OPTIONS, value="300px", size="xs", w=150, allowDeselect=False, ), ], gap="md", ), ], gap="md", ), p="md", radius="md", withBorder=True, ), dmc.Text( "Right-click on any cell to open the context menu. Try Delete Row or View Details for callback actions.", size="sm", c="dimmed", ), dgg.GlideGrid( id={"type": "glide-grid", "index": "context-menu-example"}, columns=COLUMNS, data=INITIAL_DATA, height=300, rowHeight=34, headerHeight=40, rowMarkers="number", rangeSelect="rect", contextMenuConfig=CONTEXT_MENU_CONFIG, contextMenuScrollBehavior="default", ), dmc.SimpleGrid( cols=2, spacing="md", children=[ dmc.Paper( dmc.Stack( [ dmc.Text("Last Action", fw=600, size="sm"), dmc.Code( id="context-menu-action-output", children="Right-click a cell to trigger an action...", block=True, ), ], gap="xs", ), p="md", radius="md", withBorder=True, ), dmc.Paper( dmc.Stack( [ dmc.Text( "cellsEdited (for server sync)", fw=600, size="sm" ), dmc.Code( id="context-menu-cells-edited", children="No edits yet", block=True, ), ], gap="xs", ), p="md", radius="md", withBorder=True, ), ], ), # Details modal dmc.Modal( id="context-menu-details-modal", title="Row Details", centered=True, children=[ dmc.Stack( id="context-menu-modal-content", gap="sm", ) ], ), ], gap="md", ) @callback( Output( {"type": "glide-grid", "index": "context-menu-example"}, "contextMenuScrollBehavior", ), Input("context-menu-scroll-behavior", "value"), ) def update_scroll_behavior(value): """Update scroll behavior.""" return value @callback( Output( {"type": "glide-grid", "index": "context-menu-example"}, "contextMenuConfig" ), Input("context-menu-max-height", "value"), State({"type": "glide-grid", "index": "context-menu-example"}, "contextMenuConfig"), ) def update_max_height(value, current_config): """Update maxHeight in context menu config.""" if not current_config: return no_update new_config = {**current_config} if value == "none": new_config.pop("maxHeight", None) else: new_config["maxHeight"] = value return new_config @callback( Output({"type": "glide-grid", "index": "context-menu-example"}, "data"), Output("context-menu-action-output", "children"), Output("context-menu-details-modal", "opened"), Output("context-menu-details-modal", "title"), Output("context-menu-modal-content", "children"), Input( {"type": "glide-grid", "index": "context-menu-example"}, "contextMenuItemClicked", ), State({"type": "glide-grid", "index": "context-menu-example"}, "data"), prevent_initial_call=True, ) def handle_context_menu(item, current_data): """Handle context menu actions - display feedback and handle delete/details.""" if not item: return no_update, no_update, no_update, no_update, no_update col = item.get("col", 0) row = item.get("row", 0) item_id = item.get("itemId", "") col_id = COLUMNS[col]["id"] if col < len(COLUMNS) else None row_data = current_data[row] if row < len(current_data) else {} cell_value = row_data.get(col_id, "") if col_id else "" # Default outputs new_data = no_update action_msg = no_update modal_opened = False modal_title = no_update modal_content = no_update if item_id == "copy": action_msg = f"Copied cell value: {cell_value}" elif item_id == "copy-selection": action_msg = "Copied selection to clipboard" elif item_id == "paste-cell": action_msg = f"Pasted at cell [{row}, {col}]" elif item_id == "paste-selection": action_msg = "Pasted at selection" elif item_id == "clear": action_msg = f"Cleared cell at [{row}, {col}]" elif item_id == "clear-selection": action_msg = "Cleared selection" elif item_id == "delete": # Delete the row from data if row < len(current_data): new_data = [r for i, r in enumerate(current_data) if i != row] deleted_product = row_data.get("product", f"Row {row + 1}") action_msg = f"Deleted: {deleted_product}" elif item_id == "details": # Show modal with row details product_name = row_data.get("product", "Unknown") action_msg = f"Viewing details for: {product_name}" modal_opened = True modal_title = f"Details: {product_name}" modal_content = [ dmc.Table( data={ "head": ["Field", "Value"], "body": [ [col["title"], str(row_data.get(col["id"], "N/A"))] for col in COLUMNS ], }, striped=True, highlightOnHover=True, withTableBorder=True, withColumnBorders=True, ), dmc.Text( f"Row index: {row}", size="sm", c="dimmed", mt="sm", ), ] else: action_msg = f"Action: {item_id}" return new_data, action_msg, modal_opened, modal_title, modal_content @callback( Output("context-menu-cells-edited", "children"), Input({"type": "glide-grid", "index": "context-menu-example"}, "cellsEdited"), prevent_initial_call=True, ) def show_cells_edited(edited): """Display cellsEdited prop - use this for server sync.""" if not edited: return "No edits" edits = edited.get("edits", []) count = edited.get("count", 0) lines = [f"Received {count} edit(s):"] for edit in edits[:5]: col = edit.get("col", "?") row = edit.get("row", "?") value = edit.get("value", "") val_str = str(value)[:20] + "..." if len(str(value)) > 20 else str(value) lines.append(f" [{row}, {col}] = '{val_str}'") if count > 5: lines.append(f" ... and {count - 5} more") return "\n".join(lines) ``` :defaultExpanded: false :withExpandedButton: true ### Advanced Usage [dgg] Custom actions can be performed using clientside functions in a Javascript asset file. .. exec::examples.reference.context_menu.advanced :code: false :defaultExpanded: false :withExpandedButton: true ### Props Reference #### contextMenuConfig | Property | Type | Description | |----------|------|-------------| | `items` | list | Array of menu item objects | | `maxHeight` | string | CSS max-height for the menu (e.g., `"300px"`). Enables scrolling when set. | Each menu item supports the following properties: | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | string | Yes | Unique identifier for the item | | `label` | string | Yes | Display text shown in the menu | | `icon` | string | No | Unicode symbol or emoji displayed before the label | | `iconSize` | string | No | CSS font-size for the icon (e.g., `"18px"`) | | `iconColor` | string | No | CSS color for the icon (e.g., `"#dc2626"`) | | `iconWeight` | string | No | CSS font-weight for the icon (e.g., `"bold"`, `"600"`) | | `color` | string | No | CSS color for the label text | | `fontWeight` | string | No | CSS font-weight for the label text | | `dividerAfter` | boolean | No | Show a horizontal divider after this item | | `disabled` | boolean | No | Gray out and disable the item | | `action` | string | No | Built-in action to perform or construct with `{"function": "func()" }` | The `action` property has a few built-in clipboard operations or can be built with a custom clientside script. | Action | Description | |--------|-------------| | `copyClickedCell` | Copy the right-clicked cell's value to clipboard | | `copySelection` | Copy all selected cells as tab-separated values | | `pasteAtClickedCell` | Paste clipboard content starting at the right-clicked cell | | `pasteAtSelection` | Paste clipboard content at the top-left of the current selection | | `{"function" : func()}` | Execute custom Javascript functions | Parameters that can be included in the custom clientside script include: | Parameter | Description | |-----------|-------------| | `col` | Column index of right-clicked cell | | `row` | Row index of right-clicked cell | | `columnId` | Column ID string | | `cellData` | Current cell value | | `selection` | Current selection object with `range: {x, y, width, height}` | | `columns` | Array of column definitions | | `data` | Current grid data | | `utils` | Utility object with `setCells()` and `getClipboard()` | #### contextMenuScrollBehavior The `contextMenuScrollBehavior` prop controls how the grid behaves when scrolling while the context menu is open. | Type | Default | Options | |------|---------|---------| | string | `"default"` | `"default"`: Menu stays at its original position while grid scrolls normally, `"close-overlay-on-scroll"`: Menu closes automatically when any scroll occurs outside the overlay, `"lock-scroll"`: Prevents all scrolling outside the overlay while the menu is open | #### contextMenuItemClicked (Output) Fires when a menu item is clicked. Returns: | Property | Type | Description | |----------|------|-------------| | `col` | number | Column index where right-click occurred | | `row` | number | Row index where right-click occurred | | `itemId` | string | The `id` of the clicked menu item | | `timestamp` | number | Event timestamp | --- *Source: /reference/context-menu* *Generated with dash-improve-my-llms*