Fixed #6489 - TreeTable: ContextMenu implementation
parent
0bc1689995
commit
aa73025403
|
@ -30,6 +30,14 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
contextMenu: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
contextMenuSelection: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
rows: {
|
rows: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
|
|
|
@ -173,6 +173,21 @@ export interface TreeTableFilterEvent extends TreeTableSortEvent {
|
||||||
filteredValue: any;
|
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.
|
* Custom sort metadata.
|
||||||
*/
|
*/
|
||||||
|
@ -420,6 +435,15 @@ export interface TreeTableProps {
|
||||||
* @defaultValue false
|
* @defaultValue false
|
||||||
*/
|
*/
|
||||||
metaKeySelection?: boolean | undefined;
|
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.
|
* Number of rows to display per page.
|
||||||
*/
|
*/
|
||||||
|
@ -730,6 +754,11 @@ export interface TreeTableEmitsOptions {
|
||||||
* @param {TreeSelectionKeys} value - New selection keys.
|
* @param {TreeSelectionKeys} value - New selection keys.
|
||||||
*/
|
*/
|
||||||
'update:selectionKeys'(event: TreeTableSelectionKeys): void;
|
'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.
|
* Emitted when the first changes.
|
||||||
* @param {number} value - New value.
|
* @param {number} value - New value.
|
||||||
|
@ -795,6 +824,11 @@ export interface TreeTableEmitsOptions {
|
||||||
* @param {Event} event - Browser event.
|
* @param {Event} event - Browser event.
|
||||||
*/
|
*/
|
||||||
'column-resize-end'(event: Event): void;
|
'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>;
|
export declare type TreeTableEmits = EmitFn<TreeTableEmitsOptions>;
|
||||||
|
|
|
@ -97,10 +97,13 @@
|
||||||
:ariaPosInset="index + 1"
|
:ariaPosInset="index + 1"
|
||||||
:tabindex="setTabindex(node, index)"
|
:tabindex="setTabindex(node, index)"
|
||||||
:loadingMode="loadingMode"
|
:loadingMode="loadingMode"
|
||||||
|
:contextMenu="contextMenu"
|
||||||
|
:contextMenuSelection="contextMenuSelection"
|
||||||
:templates="$slots"
|
:templates="$slots"
|
||||||
@node-toggle="onNodeToggle"
|
@node-toggle="onNodeToggle"
|
||||||
@node-click="onNodeClick"
|
@node-click="onNodeClick"
|
||||||
@checkbox-change="onCheckboxChange"
|
@checkbox-change="onCheckboxChange"
|
||||||
|
@row-rightclick="onRowRightClick($event)"
|
||||||
:unstyled="unstyled"
|
:unstyled="unstyled"
|
||||||
:pt="pt"
|
:pt="pt"
|
||||||
></TTRow>
|
></TTRow>
|
||||||
|
@ -198,7 +201,9 @@ export default {
|
||||||
'update:multiSortMeta',
|
'update:multiSortMeta',
|
||||||
'sort',
|
'sort',
|
||||||
'filter',
|
'filter',
|
||||||
'column-resize-end'
|
'column-resize-end',
|
||||||
|
'update:contextMenuSelection',
|
||||||
|
'row-contextmenu'
|
||||||
],
|
],
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
|
@ -348,6 +353,15 @@ export default {
|
||||||
if (event.check) this.$emit('node-select', event.node);
|
if (event.check) this.$emit('node-select', event.node);
|
||||||
else this.$emit('node-unselect', 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() {
|
isSingleSelectionMode() {
|
||||||
return this.selectionMode === 'single';
|
return this.selectionMode === 'single';
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
@keydown="onKeyDown"
|
@keydown="onKeyDown"
|
||||||
@touchend="onTouchEnd"
|
@touchend="onTouchEnd"
|
||||||
|
@contextmenu="onRowRightClick"
|
||||||
v-bind="ptm('row', ptmOptions)"
|
v-bind="ptm('row', ptmOptions)"
|
||||||
:data-p-selected="selected"
|
: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">
|
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
|
||||||
<TTBodyCell
|
<TTBodyCell
|
||||||
|
@ -51,12 +53,15 @@
|
||||||
:expandedKeys="expandedKeys"
|
:expandedKeys="expandedKeys"
|
||||||
:selectionMode="selectionMode"
|
:selectionMode="selectionMode"
|
||||||
:selectionKeys="selectionKeys"
|
:selectionKeys="selectionKeys"
|
||||||
|
:contextMenu="contextMenu"
|
||||||
|
:contextMenuSelection="contextMenuSelection"
|
||||||
:indentation="indentation"
|
:indentation="indentation"
|
||||||
:ariaPosInset="node.children.indexOf(childNode) + 1"
|
:ariaPosInset="node.children.indexOf(childNode) + 1"
|
||||||
:ariaSetSize="node.children.length"
|
:ariaSetSize="node.children.length"
|
||||||
:templates="templates"
|
:templates="templates"
|
||||||
@node-toggle="$emit('node-toggle', $event)"
|
@node-toggle="$emit('node-toggle', $event)"
|
||||||
@node-click="$emit('node-click', $event)"
|
@node-click="$emit('node-click', $event)"
|
||||||
|
@row-rightclick="$emit('row-rightclick', $event)"
|
||||||
@checkbox-change="onCheckboxChange"
|
@checkbox-change="onCheckboxChange"
|
||||||
:unstyled="unstyled"
|
:unstyled="unstyled"
|
||||||
:pt="pt"
|
:pt="pt"
|
||||||
|
@ -66,7 +71,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { find, findSingle, focus, getAttribute, isClickable } from '@primeuix/utils/dom';
|
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 BaseComponent from '@primevue/core/basecomponent';
|
||||||
import { getVNodeProp } from '@primevue/core/utils';
|
import { getVNodeProp } from '@primevue/core/utils';
|
||||||
import BodyCell from './BodyCell.vue';
|
import BodyCell from './BodyCell.vue';
|
||||||
|
@ -75,7 +80,7 @@ export default {
|
||||||
name: 'TreeTableRow',
|
name: 'TreeTableRow',
|
||||||
hostName: 'TreeTable',
|
hostName: 'TreeTable',
|
||||||
extends: BaseComponent,
|
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: {
|
props: {
|
||||||
node: {
|
node: {
|
||||||
type: null,
|
type: null,
|
||||||
|
@ -132,6 +137,14 @@ export default {
|
||||||
templates: {
|
templates: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null
|
default: null
|
||||||
|
},
|
||||||
|
contextMenu: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
contextMenuSelection: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
nodeTouched: false,
|
nodeTouched: false,
|
||||||
|
@ -156,6 +169,12 @@ export default {
|
||||||
});
|
});
|
||||||
this.nodeTouched = false;
|
this.nodeTouched = false;
|
||||||
},
|
},
|
||||||
|
onRowRightClick(event) {
|
||||||
|
this.$emit('row-rightclick', {
|
||||||
|
originalEvent: event,
|
||||||
|
node: this.node
|
||||||
|
});
|
||||||
|
},
|
||||||
onTouchEnd() {
|
onTouchEnd() {
|
||||||
this.nodeTouched = true;
|
this.nodeTouched = true;
|
||||||
},
|
},
|
||||||
|
@ -426,6 +445,13 @@ export default {
|
||||||
selected() {
|
selected() {
|
||||||
return this.selectionMode && this.selectionKeys ? this.selectionKeys[this.nodeKey(this.node)] === true : false;
|
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() {
|
checked() {
|
||||||
return this.selectionKeys ? this.selectionKeys[this.nodeKey(this.node)] && this.selectionKeys[this.nodeKey(this.node)].checked : false;
|
return this.selectionKeys ? this.selectionKeys[this.nodeKey(this.node)] && this.selectionKeys[this.nodeKey(this.node)].checked : false;
|
||||||
},
|
},
|
||||||
|
|
|
@ -198,9 +198,9 @@ const theme = ({ dt }) => `
|
||||||
|
|
||||||
.p-treetable-tbody > tr:focus-visible,
|
.p-treetable-tbody > tr:focus-visible,
|
||||||
.p-treetable-tbody > tr.p-treetable-contextmenu-row-selected {
|
.p-treetable-tbody > tr.p-treetable-contextmenu-row-selected {
|
||||||
box-shadow: ${dt('treetable.body.cell.focus.ring.shadow')};
|
box-shadow: ${dt('treetable.row.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: ${dt('treetable.row.focus.ring.width')} ${dt('treetable.row.focus.ring.style')} ${dt('treetable.row.focus.ring.color')};
|
||||||
outline-offset: ${dt('treetable.body.cell.focus.ring.offset')};
|
outline-offset: ${dt('treetable.row.focus.ring.offset')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-treetable-tfoot > tr > td {
|
.p-treetable-tfoot > tr > td {
|
||||||
|
@ -470,9 +470,10 @@ const classes = {
|
||||||
sortIcon: 'p-treetable-sort-icon',
|
sortIcon: 'p-treetable-sort-icon',
|
||||||
pcSortBadge: 'p-treetable-sort-badge',
|
pcSortBadge: 'p-treetable-sort-badge',
|
||||||
tbody: 'p-treetable-tbody',
|
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 }) => [
|
bodyCell: ({ instance }) => [
|
||||||
|
|
Loading…
Reference in New Issue