Fixed #6489 - TreeTable: ContextMenu implementation

pull/6493/head
tugcekucukoglu 2024-09-27 16:30:55 +03:00
parent 0bc1689995
commit aa73025403
5 changed files with 91 additions and 8 deletions

View File

@ -30,6 +30,14 @@ export default {
type: Boolean,
default: false
},
contextMenu: {
type: Boolean,
default: false
},
contextMenuSelection: {
type: Object,
default: null
},
rows: {
type: Number,
default: 0

View File

@ -173,6 +173,21 @@ export interface TreeTableFilterEvent extends TreeTableSortEvent {
filteredValue: any;
}
/**
* Custom row context menu event.
* @see {@link TreeTableEmitsOptions['row-contextmenu']}
*/
export interface TreeTableRowContextMenuEvent {
/**
* Browser event.
*/
originalEvent: Event;
/**
* Selected row data.
*/
node: TreeNode;
}
/**
* Custom sort metadata.
*/
@ -420,6 +435,15 @@ export interface TreeTableProps {
* @defaultValue false
*/
metaKeySelection?: boolean | undefined;
/**
* Enables context menu integration.
* @defaultValue false
*/
contextMenu?: boolean | undefined;
/**
* Selected row instance with the ContextMenu.
*/
contextMenuSelection?: any | any[] | undefined;
/**
* Number of rows to display per page.
*/
@ -730,6 +754,11 @@ export interface TreeTableEmitsOptions {
* @param {TreeSelectionKeys} value - New selection keys.
*/
'update:selectionKeys'(event: TreeTableSelectionKeys): void;
/**
* Emitted when the contextMenuSelection changes.
* @param {*} value - New value.
*/
'update:contextMenuSelection'(value: any[] | any | undefined): void;
/**
* Emitted when the first changes.
* @param {number} value - New value.
@ -795,6 +824,11 @@ export interface TreeTableEmitsOptions {
* @param {Event} event - Browser event.
*/
'column-resize-end'(event: Event): void;
/**
* Callback to invoke when a row is selected with a ContextMenu.
* @param {TreeTableRowContextMenuEvent} event - Custom row context menu event.
*/
'row-contextmenu'(event: TreeTableRowContextMenuEvent): void;
}
export declare type TreeTableEmits = EmitFn<TreeTableEmitsOptions>;

View File

@ -97,10 +97,13 @@
:ariaPosInset="index + 1"
:tabindex="setTabindex(node, index)"
:loadingMode="loadingMode"
:contextMenu="contextMenu"
:contextMenuSelection="contextMenuSelection"
:templates="$slots"
@node-toggle="onNodeToggle"
@node-click="onNodeClick"
@checkbox-change="onCheckboxChange"
@row-rightclick="onRowRightClick($event)"
:unstyled="unstyled"
:pt="pt"
></TTRow>
@ -198,7 +201,9 @@ export default {
'update:multiSortMeta',
'sort',
'filter',
'column-resize-end'
'column-resize-end',
'update:contextMenuSelection',
'row-contextmenu'
],
provide() {
return {
@ -348,6 +353,15 @@ export default {
if (event.check) this.$emit('node-select', event.node);
else this.$emit('node-unselect', event.node);
},
onRowRightClick(event) {
if (this.contextMenu) {
clearSelection();
event.originalEvent.target.focus();
}
this.$emit('update:contextMenuSelection', event.node);
this.$emit('row-contextmenu', event);
},
isSingleSelectionMode() {
return this.selectionMode === 'single';
},

View File

@ -14,8 +14,10 @@
@click="onClick"
@keydown="onKeyDown"
@touchend="onTouchEnd"
@contextmenu="onRowRightClick"
v-bind="ptm('row', ptmOptions)"
:data-p-selected="selected"
:data-p-selected-contextmenu="contextMenuSelection && isSelectedWithContextMenu"
>
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
<TTBodyCell
@ -51,12 +53,15 @@
:expandedKeys="expandedKeys"
:selectionMode="selectionMode"
:selectionKeys="selectionKeys"
:contextMenu="contextMenu"
:contextMenuSelection="contextMenuSelection"
:indentation="indentation"
:ariaPosInset="node.children.indexOf(childNode) + 1"
:ariaSetSize="node.children.length"
:templates="templates"
@node-toggle="$emit('node-toggle', $event)"
@node-click="$emit('node-click', $event)"
@row-rightclick="$emit('row-rightclick', $event)"
@checkbox-change="onCheckboxChange"
:unstyled="unstyled"
:pt="pt"
@ -66,7 +71,7 @@
<script>
import { find, findSingle, focus, getAttribute, isClickable } from '@primeuix/utils/dom';
import { resolveFieldData } from '@primeuix/utils/object';
import { equals, resolveFieldData } from '@primeuix/utils/object';
import BaseComponent from '@primevue/core/basecomponent';
import { getVNodeProp } from '@primevue/core/utils';
import BodyCell from './BodyCell.vue';
@ -75,7 +80,7 @@ export default {
name: 'TreeTableRow',
hostName: 'TreeTable',
extends: BaseComponent,
emits: ['node-click', 'node-toggle', 'checkbox-change', 'nodeClick', 'nodeToggle', 'checkboxChange'],
emits: ['node-click', 'node-toggle', 'checkbox-change', 'nodeClick', 'nodeToggle', 'checkboxChange', 'row-rightclick', 'rowRightclick'],
props: {
node: {
type: null,
@ -132,6 +137,14 @@ export default {
templates: {
type: Object,
default: null
},
contextMenu: {
type: Boolean,
default: false
},
contextMenuSelection: {
type: Object,
default: null
}
},
nodeTouched: false,
@ -156,6 +169,12 @@ export default {
});
this.nodeTouched = false;
},
onRowRightClick(event) {
this.$emit('row-rightclick', {
originalEvent: event,
node: this.node
});
},
onTouchEnd() {
this.nodeTouched = true;
},
@ -426,6 +445,13 @@ export default {
selected() {
return this.selectionMode && this.selectionKeys ? this.selectionKeys[this.nodeKey(this.node)] === true : false;
},
isSelectedWithContextMenu() {
if (this.node && this.contextMenuSelection) {
return equals(this.node, this.contextMenuSelection, this.dataKey);
}
return false;
},
checked() {
return this.selectionKeys ? this.selectionKeys[this.nodeKey(this.node)] && this.selectionKeys[this.nodeKey(this.node)].checked : false;
},

View File

@ -198,9 +198,9 @@ const theme = ({ dt }) => `
.p-treetable-tbody > tr:focus-visible,
.p-treetable-tbody > tr.p-treetable-contextmenu-row-selected {
box-shadow: ${dt('treetable.body.cell.focus.ring.shadow')};
outline: ${dt('treetable.body.cell.focus.ring.width')} ${dt('treetable.body.cell.focus.ring.style')} ${dt('treetable.body.cell.focus.ring.color')};
outline-offset: ${dt('treetable.body.cell.focus.ring.offset')};
box-shadow: ${dt('treetable.row.focus.ring.shadow')};
outline: ${dt('treetable.row.focus.ring.width')} ${dt('treetable.row.focus.ring.style')} ${dt('treetable.row.focus.ring.color')};
outline-offset: ${dt('treetable.row.focus.ring.offset')};
}
.p-treetable-tfoot > tr > td {
@ -470,9 +470,10 @@ const classes = {
sortIcon: 'p-treetable-sort-icon',
pcSortBadge: 'p-treetable-sort-badge',
tbody: 'p-treetable-tbody',
row: ({ instance }) => [
row: ({ props, instance }) => [
{
'p-treetable-row-selected': instance.selected
'p-treetable-row-selected': instance.selected,
'p-treetable-contextmenu-row-selected': props.contextMenuSelection && instance.isSelectedWithContextMenu
}
],
bodyCell: ({ instance }) => [