mirror of
https://github.com/primefaces/primevue.git
synced 2025-05-09 00:42:36 +00:00
Merge branch 'master' into feature/column-filter-apply-filter-prop
# Conflicts: # components/lib/datatable/ColumnFilter.vue
This commit is contained in:
commit
b6c2073526
3056 changed files with 305661 additions and 157526 deletions
746
components/lib/datatable/BaseDataTable.vue
Normal file
746
components/lib/datatable/BaseDataTable.vue
Normal file
|
@ -0,0 +1,746 @@
|
|||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import { useStyle } from 'primevue/usestyle';
|
||||
|
||||
const styles = `
|
||||
.p-datatable {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.p-datatable-table {
|
||||
border-spacing: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.p-datatable .p-sortable-column {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.p-datatable .p-sortable-column .p-column-title,
|
||||
.p-datatable .p-sortable-column .p-sortable-column-icon,
|
||||
.p-datatable .p-sortable-column .p-sortable-column-badge {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.p-datatable .p-sortable-column .p-sortable-column-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.p-datatable-hoverable-rows .p-selectable-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Scrollable */
|
||||
.p-datatable-scrollable > .p-datatable-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.p-datatable-scrollable-table > .p-datatable-thead {
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.p-datatable-scrollable-table > .p-datatable-frozen-tbody {
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.p-datatable-scrollable-table > .p-datatable-tfoot {
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.p-datatable-scrollable .p-frozen-column {
|
||||
position: sticky;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.p-datatable-scrollable th.p-frozen-column {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.p-datatable-flex-scrollable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.p-datatable-flex-scrollable > .p-datatable-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.p-datatable-scrollable-table > .p-datatable-tbody > .p-rowgroup-header {
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Resizable */
|
||||
.p-datatable-resizable-table > .p-datatable-thead > tr > th,
|
||||
.p-datatable-resizable-table > .p-datatable-tfoot > tr > td,
|
||||
.p-datatable-resizable-table > .p-datatable-tbody > tr > td {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.p-datatable-resizable-table > .p-datatable-thead > tr > th.p-resizable-column:not(.p-frozen-column) {
|
||||
background-clip: padding-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.p-datatable-resizable-table-fit > .p-datatable-thead > tr > th.p-resizable-column:last-child .p-column-resizer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.p-datatable .p-column-resizer {
|
||||
display: block;
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
width: 0.5rem;
|
||||
height: 100%;
|
||||
padding: 0px;
|
||||
cursor: col-resize;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.p-datatable .p-column-header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.p-datatable .p-column-resizer-helper {
|
||||
width: 1px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.p-datatable .p-row-editor-init,
|
||||
.p-datatable .p-row-editor-save,
|
||||
.p-datatable .p-row-editor-cancel {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Expand */
|
||||
.p-datatable .p-row-toggler {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Reorder */
|
||||
.p-datatable-reorder-indicator-up,
|
||||
.p-datatable-reorder-indicator-down {
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.p-reorderable-column,
|
||||
.p-datatable-reorderablerow-handle {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/* Loader */
|
||||
.p-datatable .p-datatable-loading-overlay {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Filter */
|
||||
.p-column-filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.p-column-filter-menu {
|
||||
display: inline-flex;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.p-column-filter-row .p-column-filter-element {
|
||||
flex: 1 1 auto;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.p-column-filter-menu-button,
|
||||
.p-column-filter-clear-button {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.p-column-filter-row-items {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.p-column-filter-row-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.p-column-filter-add-button,
|
||||
.p-column-filter-remove-button {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.p-column-filter-add-button .p-button-label,
|
||||
.p-column-filter-remove-button .p-button-label {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.p-column-filter-buttonbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.p-column-filter-buttonbar .p-button:not(.p-button-icon-only) {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
.p-datatable .p-datatable-tbody > tr > td > .p-column-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* VirtualScroller */
|
||||
.p-datatable-virtualscroller-spacer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.p-datatable .p-virtualscroller .p-virtualscroller-loading {
|
||||
transform: none !important;
|
||||
min-height: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const inlineStyles = {
|
||||
wrapper: { overflow: 'auto' },
|
||||
thead: { position: 'sticky' },
|
||||
tfoot: { position: 'sticky' }
|
||||
};
|
||||
|
||||
const classes = {
|
||||
root: ({ instance, props }) => [
|
||||
'p-datatable p-component',
|
||||
{
|
||||
'p-datatable-hoverable-rows': props.rowHover || props.selectionMode,
|
||||
'p-datatable-resizable': props.resizableColumns,
|
||||
'p-datatable-resizable-fit': props.resizableColumns && props.columnResizeMode === 'fit',
|
||||
'p-datatable-scrollable': props.scrollable,
|
||||
'p-datatable-flex-scrollable': props.scrollable && props.scrollHeight === 'flex',
|
||||
'p-datatable-responsive-stack': props.responsiveLayout === 'stack',
|
||||
'p-datatable-responsive-scroll': props.responsiveLayout === 'scroll',
|
||||
'p-datatable-striped': props.stripedRows,
|
||||
'p-datatable-gridlines': props.showGridlines,
|
||||
'p-datatable-grouped-header': instance.headerColumnGroup != null,
|
||||
'p-datatable-grouped-footer': instance.footerColumnGroup != null,
|
||||
'p-datatable-sm': props.size === 'small',
|
||||
'p-datatable-lg': props.size === 'large'
|
||||
}
|
||||
],
|
||||
loadingOverlay: 'p-datatable-loading-overlay p-component-overlay',
|
||||
loadingIcon: 'p-datatable-loading-icon',
|
||||
header: 'p-datatable-header',
|
||||
paginator: ({ instance }) => (instance.paginatorTop ? 'p-paginator-top' : instance.paginatorBottom ? 'p-paginator-bottom' : ''),
|
||||
wrapper: 'p-datatable-wrapper',
|
||||
table: ({ props }) => [
|
||||
'p-datatable-table',
|
||||
{
|
||||
'p-datatable-scrollable-table': props.scrollable,
|
||||
'p-datatable-resizable-table': props.resizableColumns,
|
||||
'p-datatable-resizable-table-fit': props.resizableColumns && props.columnResizeMode === 'fit'
|
||||
}
|
||||
],
|
||||
//tablehead
|
||||
thead: 'p-datatable-thead',
|
||||
// headercell
|
||||
headerCell: ({ instance, props, column }) =>
|
||||
column && !instance.columnProp(column, 'hidden') && (props.rowGroupMode !== 'subheader' || props.groupRowsBy !== instance.columnProp(column, 'field'))
|
||||
? [
|
||||
'p-filter-column',
|
||||
{
|
||||
'p-frozen-column': instance.columnProp(column, 'frozen')
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
'p-sortable-column': instance.columnProp('sortable'),
|
||||
'p-resizable-column': instance.resizableColumns,
|
||||
'p-highlight': instance.isColumnSorted(),
|
||||
'p-filter-column': props.filterColumn,
|
||||
'p-frozen-column': instance.columnProp('frozen'),
|
||||
'p-reorderable-column': props.reorderableColumns
|
||||
}
|
||||
],
|
||||
columnResizer: 'p-column-resizer',
|
||||
headerContent: 'p-column-header-content',
|
||||
headerTitle: 'p-column-title',
|
||||
sortIcon: 'p-sortable-column-icon',
|
||||
sortBadge: 'p-sortable-column-badge',
|
||||
//headercheckbox
|
||||
headerCheckboxWrapper: ({ instance }) => [
|
||||
'p-checkbox p-component',
|
||||
{
|
||||
'p-checkbox-focused': instance.focused,
|
||||
'p-disabled': instance.disabled
|
||||
}
|
||||
],
|
||||
headerCheckbox: ({ instance }) => [
|
||||
'p-checkbox-box p-component',
|
||||
{
|
||||
'p-highlight': instance.checked,
|
||||
'p-disabled': instance.disabled,
|
||||
'p-focus': instance.focused
|
||||
}
|
||||
],
|
||||
headerCheckboxIcon: 'p-checkbox-icon',
|
||||
// columnfilter
|
||||
columnFilter: ({ props }) => [
|
||||
'p-column-filter p-fluid',
|
||||
{
|
||||
'p-column-filter-row': props.display === 'row',
|
||||
'p-column-filter-menu': props.display === 'menu'
|
||||
}
|
||||
],
|
||||
filterInput: 'p-fluid p-column-filter-element',
|
||||
filterMenuButton: ({ instance }) => [
|
||||
'p-column-filter-menu-button p-link',
|
||||
{
|
||||
'p-column-filter-menu-button-open': instance.overlayVisible,
|
||||
'p-column-filter-menu-button-active': instance.hasFilter()
|
||||
}
|
||||
],
|
||||
headerFilterClearButton: ({ instance }) => [
|
||||
'p-column-filter-clear-button p-link',
|
||||
{
|
||||
'p-hidden-space': !instance.hasRowFilter()
|
||||
}
|
||||
],
|
||||
filterOverlay: ({ instance, props }) => [
|
||||
{
|
||||
'p-column-filter-overlay p-component p-fluid': true,
|
||||
'p-column-filter-overlay-menu': props.display === 'menu',
|
||||
'p-input-filled': instance.$primevue.config.inputStyle === 'filled',
|
||||
'p-ripple-disabled': instance.$primevue.config.ripple === false
|
||||
}
|
||||
],
|
||||
filterRowItems: 'p-column-filter-row-items',
|
||||
filterRowItem: ({ instance, matchMode }) => [
|
||||
'p-column-filter-row-item',
|
||||
{
|
||||
'p-highlight': matchMode && instance.isRowMatchModeSelected(matchMode.value)
|
||||
}
|
||||
],
|
||||
filterSeparator: 'p-column-filter-separator',
|
||||
filterOperator: 'p-column-filter-operator',
|
||||
filterOperatorDropdown: 'p-column-filter-operator-dropdown',
|
||||
filterConstraints: 'p-column-filter-constraints',
|
||||
filterConstraint: 'p-column-filter-constraint',
|
||||
filterMatchModeDropdown: 'p-column-filter-matchmode-dropdown',
|
||||
filterRemoveButton: 'p-column-filter-remove-button p-button-text p-button-danger p-button-sm',
|
||||
filterAddRule: 'p-column-filter-add-rule',
|
||||
filterAddRuleButton: 'p-column-filter-add-button p-button-text p-button-sm',
|
||||
filterButtonbar: 'p-column-filter-buttonbar',
|
||||
filterClearButton: 'p-button-outlined p-button-sm',
|
||||
filterApplyButton: 'p-button-sm',
|
||||
//tablebody
|
||||
tbody: ({ props }) => (props.frozenRow ? 'p-datatable-tbody p-datatable-frozen-tbody' : 'p-datatable-tbody'),
|
||||
rowgroupHeader: 'p-rowgroup-header',
|
||||
rowGroupToggler: 'p-row-toggler p-link',
|
||||
rowGroupTogglerIcon: 'p-row-toggler-icon',
|
||||
row: ({ instance, props, rowData }) => {
|
||||
let rowStyleClass = [];
|
||||
|
||||
if (props.selectionMode) {
|
||||
rowStyleClass.push('p-selectable-row');
|
||||
}
|
||||
|
||||
if (props.selection) {
|
||||
rowStyleClass.push({
|
||||
'p-highlight': instance.isSelected(rowData)
|
||||
});
|
||||
}
|
||||
|
||||
if (props.contextMenuSelection) {
|
||||
rowStyleClass.push({
|
||||
'p-highlight-contextmenu': instance.isSelectedWithContextMenu(rowData)
|
||||
});
|
||||
}
|
||||
|
||||
return rowStyleClass;
|
||||
},
|
||||
rowExpansion: 'p-datatable-row-expansion',
|
||||
rowgroupFooter: 'p-rowgroup-footer',
|
||||
emptyMessage: 'p-datatable-emptymessage',
|
||||
//bodycell
|
||||
bodyCell: ({ instance }) => [
|
||||
{
|
||||
'p-selection-column': instance.columnProp('selectionMode') != null,
|
||||
'p-editable-column': instance.isEditable(),
|
||||
'p-cell-editing': instance.d_editing,
|
||||
'p-frozen-column': instance.columnProp('frozen')
|
||||
}
|
||||
],
|
||||
columnTitle: 'p-column-title',
|
||||
rowReorderIcon: 'p-datatable-reorderablerow-handle',
|
||||
rowToggler: 'p-row-toggler p-link',
|
||||
rowTogglerIcon: 'p-row-toggler-icon',
|
||||
rowEditorInitButton: 'p-row-editor-init p-link',
|
||||
rowEditorInitIcon: 'p-row-editor-init-icon',
|
||||
rowEditorSaveButton: 'p-row-editor-save p-link',
|
||||
rowEditorSaveIcon: 'p-row-editor-save-icon',
|
||||
rowEditorCancelButton: 'p-row-editor-cancel p-link',
|
||||
rowEditorCancelIcon: 'p-row-editor-cancel-icon',
|
||||
//rowcheckbox
|
||||
checkboxWrapper: ({ instance }) => [
|
||||
'p-checkbox p-component',
|
||||
{
|
||||
'p-checkbox-focused': instance.focused
|
||||
}
|
||||
],
|
||||
checkbox: ({ instance }) => [
|
||||
'p-checkbox-box p-component',
|
||||
{
|
||||
'p-highlight': instance.checked,
|
||||
'p-disabled': instance.$attrs.disabled,
|
||||
'p-focus': instance.focused
|
||||
}
|
||||
],
|
||||
checkboxIcon: 'p-checkbox-icon',
|
||||
//rowradiobutton
|
||||
radiobuttonWrapper: ({ instance }) => [
|
||||
'p-radiobutton p-component',
|
||||
{
|
||||
'p-radiobutton-focused': instance.focused
|
||||
}
|
||||
],
|
||||
radiobutton: ({ instance }) => [
|
||||
'p-radiobutton-box p-component',
|
||||
{
|
||||
'p-highlight': instance.checked,
|
||||
'p-disabled': instance.$attrs.disabled,
|
||||
'p-focus': instance.focused
|
||||
}
|
||||
],
|
||||
radiobuttonIcon: 'p-radiobutton-icon',
|
||||
//tablefooter
|
||||
tfoot: 'p-datatable-tfoot',
|
||||
//footercell
|
||||
footerCell: ({ instance }) => [
|
||||
{
|
||||
'p-frozen-column': instance.columnProp('frozen')
|
||||
}
|
||||
],
|
||||
//datatable
|
||||
virtualScrollerSpacer: 'p-datatable-virtualscroller-spacer',
|
||||
footer: 'p-datatable-footer',
|
||||
resizeHelper: 'p-column-resizer-helper',
|
||||
reorderIndicatorUp: 'p-datatable-reorder-indicator-up',
|
||||
reorderIndicatorDown: 'p-datatable-reorder-indicator-down'
|
||||
};
|
||||
|
||||
const { load: loadStyle } = useStyle(styles, { name: 'datatable', manual: true });
|
||||
|
||||
export default {
|
||||
name: 'BaseDataTable',
|
||||
extends: BaseComponent,
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
dataKey: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
first: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
totalRecords: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
paginator: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
paginatorPosition: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
},
|
||||
alwaysShowPaginator: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
paginatorTemplate: {
|
||||
type: [Object, String],
|
||||
default: 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown'
|
||||
},
|
||||
pageLinkSize: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
rowsPerPageOptions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
currentPageReportTemplate: {
|
||||
type: String,
|
||||
default: '({currentPage} of {totalPages})'
|
||||
},
|
||||
lazy: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loadingIcon: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
sortField: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
sortOrder: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
defaultSortOrder: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
multiSortMeta: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
sortMode: {
|
||||
type: String,
|
||||
default: 'single'
|
||||
},
|
||||
removableSort: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
filters: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
filterDisplay: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
globalFilterFields: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
filterLocale: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
selection: {
|
||||
type: [Array, Object],
|
||||
default: null
|
||||
},
|
||||
selectionMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
compareSelectionBy: {
|
||||
type: String,
|
||||
default: 'deepEquals'
|
||||
},
|
||||
metaKeySelection: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
contextMenu: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
contextMenuSelection: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
selectAll: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
},
|
||||
rowHover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
csvSeparator: {
|
||||
type: String,
|
||||
default: ','
|
||||
},
|
||||
exportFilename: {
|
||||
type: String,
|
||||
default: 'download'
|
||||
},
|
||||
exportFunction: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
resizableColumns: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
columnResizeMode: {
|
||||
type: String,
|
||||
default: 'fit'
|
||||
},
|
||||
reorderableColumns: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expandedRows: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
expandedRowIcon: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
collapsedRowIcon: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
rowGroupMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
groupRowsBy: {
|
||||
type: [Array, String, Function],
|
||||
default: null
|
||||
},
|
||||
expandableRowGroups: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expandedRowGroups: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
stateStorage: {
|
||||
type: String,
|
||||
default: 'session'
|
||||
},
|
||||
stateKey: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
editMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
editingRows: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
rowClass: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
rowStyle: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
scrollable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
virtualScrollerOptions: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
scrollHeight: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
frozenValue: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
responsiveLayout: {
|
||||
type: String,
|
||||
default: 'scroll'
|
||||
},
|
||||
breakpoint: {
|
||||
type: String,
|
||||
default: '960px'
|
||||
},
|
||||
showGridlines: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
stripedRows: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
tableStyle: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
tableClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
tableProps: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
filterInputProps: {
|
||||
type: null,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
css: {
|
||||
classes,
|
||||
inlineStyles,
|
||||
loadStyle
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
$parentInstance: this
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
538
components/lib/datatable/BodyCell.vue
Executable file
538
components/lib/datatable/BodyCell.vue
Executable file
|
@ -0,0 +1,538 @@
|
|||
<template>
|
||||
<td v-if="loading" :style="containerStyle" :class="containerClass" role="cell" v-bind="{ ...getColumnPT('root'), ...getColumnPT('bodyCell') }">
|
||||
<component :is="column.children.loading" :data="rowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" :loadingOptions="loadingOptions" />
|
||||
</td>
|
||||
<td
|
||||
v-else
|
||||
:style="containerStyle"
|
||||
:class="containerClass"
|
||||
:colspan="columnProp('colspan')"
|
||||
:rowspan="columnProp('rowspan')"
|
||||
@click="onClick"
|
||||
@keydown="onKeyDown"
|
||||
role="cell"
|
||||
v-bind="{ ...getColumnPT('root'), ...getColumnPT('bodyCell') }"
|
||||
:data-p-selection-column="columnProp('selectionMode') != null"
|
||||
:data-p-editable-column="isEditable()"
|
||||
:data-p-cell-editing="d_editing"
|
||||
:data-p-frozen-column="columnProp('frozen')"
|
||||
>
|
||||
<span v-if="responsiveLayout === 'stack'" :class="cx('columnTitle')" v-bind="getColumnPT('columnTitle')">{{ columnProp('header') }}</span>
|
||||
<component v-if="column.children && column.children.body && !d_editing" :is="column.children.body" :data="rowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" :editorInitCallback="editorInitCallback" />
|
||||
<component
|
||||
v-else-if="column.children && column.children.editor && d_editing"
|
||||
:is="column.children.editor"
|
||||
:data="editingRowData"
|
||||
:column="column"
|
||||
:field="field"
|
||||
:index="rowIndex"
|
||||
:frozenRow="frozenRow"
|
||||
:editorSaveCallback="editorSaveCallback"
|
||||
:editorCancelCallback="editorCancelCallback"
|
||||
/>
|
||||
<component v-else-if="column.children && column.children.body && !column.children.editor && d_editing" :is="column.children.body" :data="editingRowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" />
|
||||
<template v-else-if="columnProp('selectionMode')">
|
||||
<DTRadioButton v-if="columnProp('selectionMode') === 'single'" :value="rowData" :name="name" :checked="selected" @change="toggleRowWithRadio($event, rowIndex)" :column="column" :index="index" :unstyled="unstyled" :pt="pt" />
|
||||
<DTCheckbox
|
||||
v-else-if="columnProp('selectionMode') === 'multiple'"
|
||||
:value="rowData"
|
||||
:checked="selected"
|
||||
:rowCheckboxIconTemplate="column.children && column.children.rowcheckboxicon"
|
||||
:aria-selected="selected ? true : undefined"
|
||||
@change="toggleRowWithCheckbox($event, rowIndex)"
|
||||
:column="column"
|
||||
:index="index"
|
||||
:unstyled="unstyled"
|
||||
:pt="pt"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="columnProp('rowReorder')">
|
||||
<component v-if="column.children && column.children.rowreordericon" :is="column.children.rowreordericon" :class="cx('rowReorderIcon')" />
|
||||
<i v-else-if="columnProp('rowReorderIcon')" :class="[cx('rowReorderIcon'), columnProp('rowReorderIcon')]" v-bind="getColumnPT('rowReorderIcon')" />
|
||||
<BarsIcon v-else :class="cx('rowReorderIcon')" v-bind="getColumnPT('rowReorderIcon')" />
|
||||
</template>
|
||||
<template v-else-if="columnProp('expander')">
|
||||
<button
|
||||
v-ripple
|
||||
:class="cx('rowToggler')"
|
||||
type="button"
|
||||
:aria-expanded="isRowExpanded"
|
||||
:aria-controls="ariaControls"
|
||||
:aria-label="expandButtonAriaLabel"
|
||||
@click="toggleRow"
|
||||
v-bind="getColumnPT('rowToggler')"
|
||||
data-pc-group-section="rowactionbutton"
|
||||
>
|
||||
<component v-if="column.children && column.children.rowtogglericon" :is="column.children.rowtogglericon" :rowExpanded="isRowExpanded" />
|
||||
<template v-else>
|
||||
<span v-if="isRowExpanded && expandedRowIcon" :class="[cx('rowTogglerIcon'), expandedRowIcon]" />
|
||||
<ChevronDownIcon v-else-if="isRowExpanded && !expandedRowIcon" :class="cx('rowTogglerIcon')" v-bind="getColumnPT('rowTogglerIcon')" />
|
||||
<span v-else-if="!isRowExpanded && collapsedRowIcon" :class="[cx('rowTogglerIcon'), collapsedRowIcon]" />
|
||||
<ChevronRightIcon v-else-if="!isRowExpanded && !collapsedRowIcon" :class="cx('rowTogglerIcon')" v-bind="getColumnPT('rowTogglerIcon')" />
|
||||
</template>
|
||||
</button>
|
||||
</template>
|
||||
<template v-else-if="editMode === 'row' && columnProp('rowEditor')">
|
||||
<button v-if="!d_editing" v-ripple :class="cx('rowEditorInitButton')" type="button" :aria-label="initButtonAriaLabel" @click="onRowEditInit" v-bind="getColumnPT('rowEditorInitButton')" data-pc-group-section="rowactionbutton">
|
||||
<component :is="(column.children && column.children.roweditoriniticon) || 'PencilIcon'" :class="cx('rowEditorInitIcon')" v-bind="getColumnPT('rowEditorInitIcon')" />
|
||||
</button>
|
||||
<button v-if="d_editing" v-ripple :class="cx('rowEditorSaveButton')" type="button" :aria-label="saveButtonAriaLabel" @click="onRowEditSave" v-bind="getColumnPT('rowEditorSaveButton')" data-pc-group-section="rowactionbutton">
|
||||
<component :is="(column.children && column.children.roweditorsaveicon) || 'CheckIcon'" :class="cx('rowEditorSaveIcon')" v-bind="getColumnPT('rowEditorSaveIcon')" />
|
||||
</button>
|
||||
<button v-if="d_editing" v-ripple :class="cx('rowEditorCancelButton')" type="button" :aria-label="cancelButtonAriaLabel" @click="onRowEditCancel" v-bind="getColumnPT('rowEditorCancelButton')" data-pc-group-section="rowactionbutton">
|
||||
<component :is="(column.children && column.children.roweditorcancelicon) || 'TimesIcon'" :class="cx('rowEditorCancelIcon')" v-bind="getColumnPT('rowEditorCancelIcon')" />
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>{{ resolveFieldData() }}</template>
|
||||
</td>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import BarsIcon from 'primevue/icons/bars';
|
||||
import CheckIcon from 'primevue/icons/check';
|
||||
import ChevronDownIcon from 'primevue/icons/chevrondown';
|
||||
import ChevronRightIcon from 'primevue/icons/chevronright';
|
||||
import PencilIcon from 'primevue/icons/pencil';
|
||||
import TimesIcon from 'primevue/icons/times';
|
||||
import OverlayEventBus from 'primevue/overlayeventbus';
|
||||
import Ripple from 'primevue/ripple';
|
||||
import { DomHandler, ObjectUtils } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
import RowCheckbox from './RowCheckbox.vue';
|
||||
import RowRadioButton from './RowRadioButton.vue';
|
||||
|
||||
export default {
|
||||
name: 'BodyCell',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
emits: ['cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'row-edit-init', 'row-edit-save', 'row-edit-cancel', 'row-toggle', 'radio-change', 'checkbox-change', 'editing-meta-change'],
|
||||
props: {
|
||||
rowData: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
column: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
frozenRow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rowIndex: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
isRowExpanded: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selected: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
editing: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
editingMeta: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
editMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
responsiveLayout: {
|
||||
type: String,
|
||||
default: 'stack'
|
||||
},
|
||||
virtualScrollerContentProps: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
ariaControls: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
expandedRowIcon: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
collapsedRowIcon: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
documentEditListener: null,
|
||||
selfClick: false,
|
||||
overlayEventListener: null,
|
||||
data() {
|
||||
return {
|
||||
d_editing: this.editing,
|
||||
styleObject: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
editing(newValue) {
|
||||
this.d_editing = newValue;
|
||||
},
|
||||
'$data.d_editing': function (newValue) {
|
||||
this.$emit('editing-meta-change', { data: this.rowData, field: this.field || `field_${this.index}`, index: this.rowIndex, editing: newValue });
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.columnProp('frozen')) {
|
||||
this.updateStickyPosition();
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
if (this.columnProp('frozen')) {
|
||||
this.updateStickyPosition();
|
||||
}
|
||||
|
||||
if (this.d_editing && (this.editMode === 'cell' || (this.editMode === 'row' && this.columnProp('rowEditor')))) {
|
||||
setTimeout(() => {
|
||||
const focusableEl = DomHandler.getFirstFocusableElement(this.$el);
|
||||
|
||||
focusableEl && focusableEl.focus();
|
||||
}, 1);
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.overlayEventListener) {
|
||||
OverlayEventBus.off('overlay-click', this.overlayEventListener);
|
||||
this.overlayEventListener = null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
columnProp(prop) {
|
||||
return ObjectUtils.getVNodeProp(this.column, prop);
|
||||
},
|
||||
getColumnPT(key) {
|
||||
const columnMetaData = {
|
||||
props: this.column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index: this.index,
|
||||
size: this.$parentInstance?.$parentInstance?.size,
|
||||
showGridlines: this.$parentInstance?.$parentInstance?.showGridlines
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(), key, columnMetaData));
|
||||
},
|
||||
getColumnProp() {
|
||||
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined;
|
||||
},
|
||||
resolveFieldData() {
|
||||
return ObjectUtils.resolveFieldData(this.rowData, this.field);
|
||||
},
|
||||
toggleRow(event) {
|
||||
this.$emit('row-toggle', {
|
||||
originalEvent: event,
|
||||
data: this.rowData
|
||||
});
|
||||
},
|
||||
toggleRowWithRadio(event, index) {
|
||||
this.$emit('radio-change', { originalEvent: event.originalEvent, index: index, data: event.data });
|
||||
},
|
||||
toggleRowWithCheckbox(event, index) {
|
||||
this.$emit('checkbox-change', { originalEvent: event.originalEvent, index: index, data: event.data });
|
||||
},
|
||||
isEditable() {
|
||||
return this.column.children && this.column.children.editor != null;
|
||||
},
|
||||
bindDocumentEditListener() {
|
||||
if (!this.documentEditListener) {
|
||||
this.documentEditListener = (event) => {
|
||||
if (!this.selfClick) {
|
||||
this.completeEdit(event, 'outside');
|
||||
}
|
||||
|
||||
this.selfClick = false;
|
||||
};
|
||||
|
||||
document.addEventListener('click', this.documentEditListener);
|
||||
}
|
||||
},
|
||||
unbindDocumentEditListener() {
|
||||
if (this.documentEditListener) {
|
||||
document.removeEventListener('click', this.documentEditListener);
|
||||
this.documentEditListener = null;
|
||||
this.selfClick = false;
|
||||
}
|
||||
},
|
||||
switchCellToViewMode() {
|
||||
this.d_editing = false;
|
||||
this.unbindDocumentEditListener();
|
||||
OverlayEventBus.off('overlay-click', this.overlayEventListener);
|
||||
this.overlayEventListener = null;
|
||||
},
|
||||
onClick(event) {
|
||||
if (this.editMode === 'cell' && this.isEditable()) {
|
||||
this.selfClick = true;
|
||||
|
||||
if (!this.d_editing) {
|
||||
this.d_editing = true;
|
||||
this.bindDocumentEditListener();
|
||||
this.$emit('cell-edit-init', { originalEvent: event, data: this.rowData, field: this.field, index: this.rowIndex });
|
||||
|
||||
this.overlayEventListener = (e) => {
|
||||
if (this.$el && this.$el.contains(e.target)) {
|
||||
this.selfClick = true;
|
||||
}
|
||||
};
|
||||
|
||||
OverlayEventBus.on('overlay-click', this.overlayEventListener);
|
||||
}
|
||||
}
|
||||
},
|
||||
completeEdit(event, type) {
|
||||
const completeEvent = {
|
||||
originalEvent: event,
|
||||
data: this.rowData,
|
||||
newData: this.editingRowData,
|
||||
value: this.rowData[this.field],
|
||||
newValue: this.editingRowData[this.field],
|
||||
field: this.field,
|
||||
index: this.rowIndex,
|
||||
type: type,
|
||||
defaultPrevented: false,
|
||||
preventDefault: function () {
|
||||
this.defaultPrevented = true;
|
||||
}
|
||||
};
|
||||
|
||||
this.$emit('cell-edit-complete', completeEvent);
|
||||
|
||||
if (!completeEvent.defaultPrevented) {
|
||||
this.switchCellToViewMode();
|
||||
}
|
||||
},
|
||||
onKeyDown(event) {
|
||||
if (this.editMode === 'cell') {
|
||||
switch (event.code) {
|
||||
case 'Enter':
|
||||
case 'NumpadEnter':
|
||||
this.completeEdit(event, 'enter');
|
||||
break;
|
||||
|
||||
case 'Escape':
|
||||
this.switchCellToViewMode();
|
||||
this.$emit('cell-edit-cancel', { originalEvent: event, data: this.rowData, field: this.field, index: this.rowIndex });
|
||||
break;
|
||||
|
||||
case 'Tab':
|
||||
this.completeEdit(event, 'tab');
|
||||
|
||||
if (event.shiftKey) this.moveToPreviousCell(event);
|
||||
else this.moveToNextCell(event);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
moveToPreviousCell(event) {
|
||||
let currentCell = this.findCell(event.target);
|
||||
let targetCell = this.findPreviousEditableColumn(currentCell);
|
||||
|
||||
if (targetCell) {
|
||||
DomHandler.invokeElementMethod(targetCell, 'click');
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
moveToNextCell(event) {
|
||||
let currentCell = this.findCell(event.target);
|
||||
let targetCell = this.findNextEditableColumn(currentCell);
|
||||
|
||||
if (targetCell) {
|
||||
DomHandler.invokeElementMethod(targetCell, 'click');
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
findCell(element) {
|
||||
if (element) {
|
||||
let cell = element;
|
||||
|
||||
while (cell && !DomHandler.getAttribute(cell, 'data-p-cell-editing')) {
|
||||
cell = cell.parentElement;
|
||||
}
|
||||
|
||||
return cell;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
findPreviousEditableColumn(cell) {
|
||||
let prevCell = cell.previousElementSibling;
|
||||
|
||||
if (!prevCell) {
|
||||
let previousRow = cell.parentElement.previousElementSibling;
|
||||
|
||||
if (previousRow) {
|
||||
prevCell = previousRow.lastElementChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (prevCell) {
|
||||
if (DomHandler.getAttribute(prevCell, 'data-p-editable-column')) return prevCell;
|
||||
else return this.findPreviousEditableColumn(prevCell);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
findNextEditableColumn(cell) {
|
||||
let nextCell = cell.nextElementSibling;
|
||||
|
||||
if (!nextCell) {
|
||||
let nextRow = cell.parentElement.nextElementSibling;
|
||||
|
||||
if (nextRow) {
|
||||
nextCell = nextRow.firstElementChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextCell) {
|
||||
if (DomHandler.getAttribute(nextCell, 'data-p-editable-column')) return nextCell;
|
||||
else return this.findNextEditableColumn(nextCell);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
isEditingCellValid() {
|
||||
return DomHandler.find(this.$el, '.p-invalid').length === 0;
|
||||
},
|
||||
onRowEditInit(event) {
|
||||
this.$emit('row-edit-init', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex });
|
||||
},
|
||||
onRowEditSave(event) {
|
||||
this.$emit('row-edit-save', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex });
|
||||
},
|
||||
onRowEditCancel(event) {
|
||||
this.$emit('row-edit-cancel', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex });
|
||||
},
|
||||
editorInitCallback(event) {
|
||||
this.$emit('row-edit-init', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex });
|
||||
},
|
||||
editorSaveCallback(event) {
|
||||
if (this.editMode === 'row') {
|
||||
this.$emit('row-edit-save', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex });
|
||||
} else {
|
||||
this.completeEdit(event, 'enter');
|
||||
}
|
||||
},
|
||||
editorCancelCallback(event) {
|
||||
if (this.editMode === 'row') {
|
||||
this.$emit('row-edit-cancel', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex });
|
||||
} else {
|
||||
this.switchCellToViewMode();
|
||||
this.$emit('cell-edit-cancel', { originalEvent: event, data: this.rowData, field: this.field, index: this.rowIndex });
|
||||
}
|
||||
},
|
||||
updateStickyPosition() {
|
||||
if (this.columnProp('frozen')) {
|
||||
let align = this.columnProp('alignFrozen');
|
||||
|
||||
if (align === 'right') {
|
||||
let right = 0;
|
||||
let next = this.$el.nextElementSibling;
|
||||
|
||||
if (next) {
|
||||
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right || 0);
|
||||
}
|
||||
|
||||
this.styleObject.right = right + 'px';
|
||||
} else {
|
||||
let left = 0;
|
||||
let prev = this.$el.previousElementSibling;
|
||||
|
||||
if (prev) {
|
||||
left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left || 0);
|
||||
}
|
||||
|
||||
this.styleObject.left = left + 'px';
|
||||
}
|
||||
}
|
||||
},
|
||||
getVirtualScrollerProp(option) {
|
||||
return this.virtualScrollerContentProps ? this.virtualScrollerContentProps[option] : null;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editingRowData() {
|
||||
return this.editingMeta[this.rowIndex] ? this.editingMeta[this.rowIndex].data : this.rowData;
|
||||
},
|
||||
field() {
|
||||
return this.columnProp('field');
|
||||
},
|
||||
containerClass() {
|
||||
return [this.columnProp('bodyClass'), this.columnProp('class'), this.cx('bodyCell')];
|
||||
},
|
||||
containerStyle() {
|
||||
let bodyStyle = this.columnProp('bodyStyle');
|
||||
let columnStyle = this.columnProp('style');
|
||||
|
||||
return this.columnProp('frozen') ? [columnStyle, bodyStyle, this.styleObject] : [columnStyle, bodyStyle];
|
||||
},
|
||||
loading() {
|
||||
return this.getVirtualScrollerProp('loading');
|
||||
},
|
||||
loadingOptions() {
|
||||
const getLoaderOptions = this.getVirtualScrollerProp('getLoaderOptions');
|
||||
|
||||
return (
|
||||
getLoaderOptions &&
|
||||
getLoaderOptions(this.rowIndex, {
|
||||
cellIndex: this.index,
|
||||
cellFirst: this.index === 0,
|
||||
cellLast: this.index === this.getVirtualScrollerProp('columns').length - 1,
|
||||
cellEven: this.index % 2 === 0,
|
||||
cellOdd: this.index % 2 !== 0,
|
||||
column: this.column,
|
||||
field: this.field
|
||||
})
|
||||
);
|
||||
},
|
||||
expandButtonAriaLabel() {
|
||||
return this.$primevue.config.locale.aria ? (this.isRowExpanded ? this.$primevue.config.locale.aria.expandRow : this.$primevue.config.locale.aria.collapseRow) : undefined;
|
||||
},
|
||||
initButtonAriaLabel() {
|
||||
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.editRow : undefined;
|
||||
},
|
||||
saveButtonAriaLabel() {
|
||||
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.saveEdit : undefined;
|
||||
},
|
||||
cancelButtonAriaLabel() {
|
||||
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.cancelEdit : undefined;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DTRadioButton: RowRadioButton,
|
||||
DTCheckbox: RowCheckbox,
|
||||
ChevronDownIcon: ChevronDownIcon,
|
||||
ChevronRightIcon: ChevronRightIcon,
|
||||
BarsIcon: BarsIcon,
|
||||
PencilIcon: PencilIcon,
|
||||
CheckIcon: CheckIcon,
|
||||
TimesIcon: TimesIcon
|
||||
},
|
||||
directives: {
|
||||
ripple: Ripple
|
||||
}
|
||||
};
|
||||
</script>
|
720
components/lib/datatable/ColumnFilter.vue
Normal file
720
components/lib/datatable/ColumnFilter.vue
Normal file
|
@ -0,0 +1,720 @@
|
|||
<template>
|
||||
<div :class="cx('columnFilter')" v-bind="getColumnPT('columnFilter')">
|
||||
<div v-if="display === 'row'" :class="cx('filterInput')" v-bind="{ ...filterInputProps, ...getColumnPT('filterInput') }">
|
||||
<component :is="filterElement" :field="field" :filterModel="filters[field]" :filterCallback="filterCallback" />
|
||||
</div>
|
||||
<button
|
||||
v-if="showMenuButton"
|
||||
ref="icon"
|
||||
type="button"
|
||||
:aria-label="filterMenuButtonAriaLabel"
|
||||
aria-haspopup="true"
|
||||
:aria-expanded="overlayVisible"
|
||||
:aria-controls="overlayId"
|
||||
:class="cx('filterMenuButton')"
|
||||
@click="toggleMenu($event)"
|
||||
@keydown="onToggleButtonKeyDown($event)"
|
||||
v-bind="getColumnPT('filterMenuButton', ptmFilterMenuParams)"
|
||||
>
|
||||
<component :is="filterIconTemplate || 'FilterIcon'" v-bind="getColumnPT('filterMenuIcon')" />
|
||||
</button>
|
||||
<button v-if="showClearButton && display === 'row'" :class="cx('headerFilterClearButton')" type="button" @click="clearFilter()" v-bind="getColumnPT('headerFilterClearButton', ptmHeaderFilterClearParams)">
|
||||
<component :is="filterClearIconTemplate || 'FilterSlashIcon'" v-bind="getColumnPT('filterClearIcon')" />
|
||||
</button>
|
||||
<Portal>
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave" v-bind="getColumnPT('transition')">
|
||||
<div
|
||||
v-if="overlayVisible"
|
||||
:ref="overlayRef"
|
||||
:id="overlayId"
|
||||
v-focustrap="{ autoFocus: true }"
|
||||
:aria-modal="overlayVisible"
|
||||
role="dialog"
|
||||
:class="[cx('filterOverlay'), filterMenuClass]"
|
||||
@keydown.escape="hide"
|
||||
@click="onContentClick"
|
||||
@mousedown="onContentMouseDown"
|
||||
v-bind="getColumnPT('filterOverlay')"
|
||||
>
|
||||
<component :is="filterHeaderTemplate" :field="field" :filterModel="filters[field]" :filterCallback="filterCallback" />
|
||||
<template v-if="display === 'row'">
|
||||
<ul :class="cx('filterRowItems')" v-bind="getColumnPT('filterRowItems')">
|
||||
<li
|
||||
v-for="(matchMode, i) of matchModes"
|
||||
:key="matchMode.label"
|
||||
:class="cx('filterRowItem', { matchMode })"
|
||||
@click="onRowMatchModeChange(matchMode.value)"
|
||||
@keydown="onRowMatchModeKeyDown($event)"
|
||||
@keydown.enter.prevent="onRowMatchModeChange(matchMode.value)"
|
||||
:tabindex="i === 0 ? '0' : null"
|
||||
v-bind="getColumnPT('filterRowItem', ptmFilterRowItemOptions(matchMode))"
|
||||
>
|
||||
{{ matchMode.label }}
|
||||
</li>
|
||||
<li :class="cx('filterSeparator')" v-bind="getColumnPT('filterSeparator')"></li>
|
||||
<li :class="cx('filterRowItem')" @click="clearFilter()" @keydown="onRowMatchModeKeyDown($event)" @keydown.enter="onRowClearItemClick()" v-bind="getColumnPT('filterRowItem')">
|
||||
{{ noFilterLabel }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-if="isShowOperator" :class="cx('filterOperator')" v-bind="getColumnPT('filterOperator')">
|
||||
<CFDropdown
|
||||
:options="operatorOptions"
|
||||
:modelValue="operator"
|
||||
:aria-label="filterOperatorAriaLabel"
|
||||
:class="cx('filterOperatorDropdown')"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
@update:modelValue="onOperatorChange($event)"
|
||||
:unstyled="unstyled"
|
||||
:pt="getColumnPT('filterOperatorDropdown')"
|
||||
data-pc-section="filteroperatordropdown"
|
||||
></CFDropdown>
|
||||
</div>
|
||||
<div :class="cx('filterConstraints')" v-bind="getColumnPT('filterConstraints')">
|
||||
<div v-for="(fieldConstraint, i) of fieldConstraints" :key="i" :class="cx('filterConstraint')" v-bind="getColumnPT('filterConstraint')">
|
||||
<CFDropdown
|
||||
v-if="isShowMatchModes"
|
||||
:options="matchModes"
|
||||
:modelValue="fieldConstraint.matchMode"
|
||||
:class="cx('filterMatchModeDropdown')"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
:aria-label="filterConstraintAriaLabel"
|
||||
@update:modelValue="onMenuMatchModeChange($event, i)"
|
||||
:unstyled="unstyled"
|
||||
:pt="getColumnPT('filterMatchModeDropdown')"
|
||||
data-pc-section="filtermatchmodedropdown"
|
||||
></CFDropdown>
|
||||
<component v-if="display === 'menu'" :is="filterElement" :field="field" :filterModel="fieldConstraint" :filterCallback="filterCallback" :applyFilter="applyFilter" />
|
||||
<div v-bind="getColumnPT('filterRemove')">
|
||||
<CFButton
|
||||
v-if="showRemoveIcon"
|
||||
type="button"
|
||||
:class="cx('filterRemoveButton')"
|
||||
@click="removeConstraint(i)"
|
||||
:label="removeRuleButtonLabel"
|
||||
:unstyled="unstyled"
|
||||
text
|
||||
severity="danger"
|
||||
size="small"
|
||||
:pt="getColumnPT('filterRemoveButton')"
|
||||
data-pc-section="filterremovebutton"
|
||||
>
|
||||
<template #icon="iconProps">
|
||||
<component :is="filterRemoveIconTemplate || 'TrashIcon'" :class="iconProps.class" v-bind="getColumnPT('filterRemoveButton')['icon']" />
|
||||
</template>
|
||||
</CFButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isShowAddConstraint" :class="cx('filterAddRule')" v-bind="getColumnPT('filterAddRule')">
|
||||
<CFButton
|
||||
type="button"
|
||||
:label="addRuleButtonLabel"
|
||||
iconPos="left"
|
||||
:class="cx('filterAddRuleButton')"
|
||||
@click="addConstraint()"
|
||||
:unstyled="unstyled"
|
||||
text
|
||||
severity="info"
|
||||
size="small"
|
||||
:pt="getColumnPT('filterAddRuleButton')"
|
||||
data-pc-section="filteraddrulebutton"
|
||||
>
|
||||
<template #icon="iconProps">
|
||||
<component :is="filterAddIconTemplate || 'PlusIcon'" :class="iconProps.class" v-bind="getColumnPT('filterAddRuleButton')['icon']" />
|
||||
</template>
|
||||
</CFButton>
|
||||
</div>
|
||||
<div :class="cx('filterButtonbar')" v-bind="getColumnPT('filterButtonbar')">
|
||||
<CFButton
|
||||
v-if="!filterClearTemplate && showClearButton"
|
||||
type="button"
|
||||
:class="cx('filterClearButton')"
|
||||
:label="clearButtonLabel"
|
||||
@click="clearFilter"
|
||||
:unstyled="unstyled"
|
||||
size="small"
|
||||
outlined
|
||||
:pt="getColumnPT('filterClearButton')"
|
||||
data-pc-section="filterclearbutton"
|
||||
></CFButton>
|
||||
<component v-else :is="filterClearTemplate" :field="field" :filterModel="filters[field]" :filterCallback="clearFilter" />
|
||||
<template v-if="showApplyButton">
|
||||
<CFButton
|
||||
v-if="!filterApplyTemplate"
|
||||
type="button"
|
||||
:class="cx('filterApplyButton')"
|
||||
:label="applyButtonLabel"
|
||||
@click="applyFilter()"
|
||||
:unstyled="unstyled"
|
||||
size="small"
|
||||
:pt="getColumnPT('filterApplyButton')"
|
||||
data-pc-section="filterapplybutton"
|
||||
></CFButton>
|
||||
<component v-else :is="filterApplyTemplate" :field="field" :filterModel="filters[field]" :filterCallback="applyFilter" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<component :is="filterFooterTemplate" :field="field" :filterModel="filters[field]" :filterCallback="filterCallback" />
|
||||
</div>
|
||||
</transition>
|
||||
</Portal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FilterOperator } from 'primevue/api';
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import Button from 'primevue/button';
|
||||
import Dropdown from 'primevue/dropdown';
|
||||
import FocusTrap from 'primevue/focustrap';
|
||||
import FilterIcon from 'primevue/icons/filter';
|
||||
import FilterSlashIcon from 'primevue/icons/filterslash';
|
||||
import PlusIcon from 'primevue/icons/plus';
|
||||
import TrashIcon from 'primevue/icons/trash';
|
||||
import OverlayEventBus from 'primevue/overlayeventbus';
|
||||
import Portal from 'primevue/portal';
|
||||
import { ConnectedOverlayScrollHandler, DomHandler, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'ColumnFilter',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
emits: ['filter-change', 'filter-apply', 'operator-change', 'matchmode-change', 'constraint-add', 'constraint-remove', 'filter-clear', 'apply-click'],
|
||||
props: {
|
||||
field: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
display: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
showMenu: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
matchMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
showOperator: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showClearButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showApplyButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showMatchModes: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showAddButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
matchModeOptions: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
maxConstraints: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
filterElement: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterHeaderTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterFooterTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterClearTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterApplyTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterAddIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterRemoveIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filterClearIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
filters: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
filtersStore: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
filterMenuClass: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
filterMenuStyle: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
filterInputProps: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
column: null
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
overlayVisible: false,
|
||||
defaultMatchMode: null,
|
||||
defaultOperator: null
|
||||
};
|
||||
},
|
||||
overlay: null,
|
||||
selfClick: false,
|
||||
overlayEventListener: null,
|
||||
beforeUnmount() {
|
||||
if (this.overlayEventListener) {
|
||||
OverlayEventBus.off('overlay-click', this.overlayEventListener);
|
||||
this.overlayEventListener = null;
|
||||
}
|
||||
|
||||
if (this.overlay) {
|
||||
ZIndexUtils.clear(this.overlay);
|
||||
this.onOverlayHide();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.filters && this.filters[this.field]) {
|
||||
let fieldFilters = this.filters[this.field];
|
||||
|
||||
if (fieldFilters.operator) {
|
||||
this.defaultMatchMode = fieldFilters.constraints[0].matchMode;
|
||||
this.defaultOperator = fieldFilters.operator;
|
||||
} else {
|
||||
this.defaultMatchMode = this.filters[this.field].matchMode;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getColumnPT(key, params) {
|
||||
const columnMetaData = {
|
||||
props: this.column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
...params
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(), key, columnMetaData));
|
||||
},
|
||||
getColumnProp() {
|
||||
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined;
|
||||
},
|
||||
ptmFilterRowItemOptions(matchMode) {
|
||||
return {
|
||||
context: {
|
||||
highlighted: matchMode && this.isRowMatchModeSelected(matchMode.value)
|
||||
}
|
||||
};
|
||||
},
|
||||
clearFilter() {
|
||||
let _filters = { ...this.filters };
|
||||
|
||||
if (_filters[this.field].operator) {
|
||||
_filters[this.field].constraints.splice(1);
|
||||
_filters[this.field].operator = this.defaultOperator;
|
||||
_filters[this.field].constraints[0] = { value: null, matchMode: this.defaultMatchMode };
|
||||
} else {
|
||||
_filters[this.field].value = null;
|
||||
_filters[this.field].matchMode = this.defaultMatchMode;
|
||||
}
|
||||
|
||||
this.$emit('filter-clear');
|
||||
this.$emit('filter-change', _filters);
|
||||
this.$emit('filter-apply');
|
||||
this.hide();
|
||||
},
|
||||
applyFilter() {
|
||||
this.$emit('apply-click', { field: this.field, constraints: this.filters[this.field] });
|
||||
this.$emit('filter-apply');
|
||||
this.hide();
|
||||
},
|
||||
hasFilter() {
|
||||
if (this.filtersStore) {
|
||||
let fieldFilter = this.filtersStore[this.field];
|
||||
|
||||
if (fieldFilter) {
|
||||
if (fieldFilter.operator) return !this.isFilterBlank(fieldFilter.constraints[0].value);
|
||||
else return !this.isFilterBlank(fieldFilter.value);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
hasRowFilter() {
|
||||
return this.filters[this.field] && !this.isFilterBlank(this.filters[this.field].value);
|
||||
},
|
||||
isFilterBlank(filter) {
|
||||
if (filter !== null && filter !== undefined) {
|
||||
if ((typeof filter === 'string' && filter.trim().length == 0) || (filter instanceof Array && filter.length == 0)) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
toggleMenu(event) {
|
||||
this.overlayVisible = !this.overlayVisible;
|
||||
|
||||
event.preventDefault();
|
||||
},
|
||||
onToggleButtonKeyDown(event) {
|
||||
switch (event.code) {
|
||||
case 'Enter':
|
||||
case 'Space':
|
||||
this.toggleMenu(event);
|
||||
|
||||
break;
|
||||
|
||||
case 'Escape':
|
||||
this.overlayVisible = false;
|
||||
break;
|
||||
}
|
||||
},
|
||||
onRowMatchModeChange(matchMode) {
|
||||
let _filters = { ...this.filters };
|
||||
|
||||
_filters[this.field].matchMode = matchMode;
|
||||
this.$emit('matchmode-change', { field: this.field, matchMode: matchMode });
|
||||
this.$emit('filter-change', _filters);
|
||||
this.$emit('filter-apply');
|
||||
this.hide();
|
||||
},
|
||||
onRowMatchModeKeyDown(event) {
|
||||
let item = event.target;
|
||||
|
||||
switch (event.code) {
|
||||
case 'ArrowDown':
|
||||
var nextItem = this.findNextItem(item);
|
||||
|
||||
if (nextItem) {
|
||||
item.removeAttribute('tabindex');
|
||||
nextItem.tabIndex = '0';
|
||||
nextItem.focus();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case 'ArrowUp':
|
||||
var prevItem = this.findPrevItem(item);
|
||||
|
||||
if (prevItem) {
|
||||
item.removeAttribute('tabindex');
|
||||
prevItem.tabIndex = '0';
|
||||
prevItem.focus();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
},
|
||||
isRowMatchModeSelected(matchMode) {
|
||||
return this.filters[this.field].matchMode === matchMode;
|
||||
},
|
||||
onOperatorChange(value) {
|
||||
let _filters = { ...this.filters };
|
||||
|
||||
_filters[this.field].operator = value;
|
||||
this.$emit('filter-change', _filters);
|
||||
|
||||
this.$emit('operator-change', { field: this.field, operator: value });
|
||||
|
||||
if (!this.showApplyButton) {
|
||||
this.$emit('filter-apply');
|
||||
}
|
||||
},
|
||||
onMenuMatchModeChange(value, index) {
|
||||
let _filters = { ...this.filters };
|
||||
|
||||
_filters[this.field].constraints[index].matchMode = value;
|
||||
this.$emit('matchmode-change', { field: this.field, matchMode: value, index: index });
|
||||
|
||||
if (!this.showApplyButton) {
|
||||
this.$emit('filter-apply');
|
||||
}
|
||||
},
|
||||
addConstraint() {
|
||||
let _filters = { ...this.filters };
|
||||
let newConstraint = { value: null, matchMode: this.defaultMatchMode };
|
||||
|
||||
_filters[this.field].constraints.push(newConstraint);
|
||||
this.$emit('constraint-add', { field: this.field, constraing: newConstraint });
|
||||
this.$emit('filter-change', _filters);
|
||||
|
||||
if (!this.showApplyButton) {
|
||||
this.$emit('filter-apply');
|
||||
}
|
||||
},
|
||||
removeConstraint(index) {
|
||||
let _filters = { ...this.filters };
|
||||
let removedConstraint = _filters[this.field].constraints.splice(index, 1);
|
||||
|
||||
this.$emit('constraint-remove', { field: this.field, constraing: removedConstraint });
|
||||
this.$emit('filter-change', _filters);
|
||||
|
||||
if (!this.showApplyButton) {
|
||||
this.$emit('filter-apply');
|
||||
}
|
||||
},
|
||||
filterCallback() {
|
||||
this.$emit('filter-apply');
|
||||
},
|
||||
findNextItem(item) {
|
||||
let nextItem = item.nextElementSibling;
|
||||
|
||||
if (nextItem) return DomHandler.getAttribute(nextItem, 'data-pc-section') === 'filterseparator' ? this.findNextItem(nextItem) : nextItem;
|
||||
else return item.parentElement.firstElementChild;
|
||||
},
|
||||
findPrevItem(item) {
|
||||
let prevItem = item.previousElementSibling;
|
||||
|
||||
if (prevItem) return DomHandler.getAttribute(prevItem, 'data-pc-section') === 'filterseparator' ? this.findPrevItem(prevItem) : prevItem;
|
||||
else return item.parentElement.lastElementChild;
|
||||
},
|
||||
hide() {
|
||||
this.overlayVisible = false;
|
||||
|
||||
DomHandler.focus(this.$refs.icon);
|
||||
},
|
||||
onContentClick(event) {
|
||||
this.selfClick = true;
|
||||
|
||||
OverlayEventBus.emit('overlay-click', {
|
||||
originalEvent: event,
|
||||
target: this.overlay
|
||||
});
|
||||
},
|
||||
onContentMouseDown() {
|
||||
this.selfClick = true;
|
||||
},
|
||||
onOverlayEnter(el) {
|
||||
if (this.filterMenuStyle) {
|
||||
DomHandler.applyStyle(this.overlay, this.filterMenuStyle);
|
||||
}
|
||||
|
||||
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
|
||||
DomHandler.addStyles(el, { position: 'absolute', top: '0', left: '0' });
|
||||
DomHandler.absolutePosition(this.overlay, this.$refs.icon);
|
||||
this.bindOutsideClickListener();
|
||||
this.bindScrollListener();
|
||||
this.bindResizeListener();
|
||||
|
||||
this.overlayEventListener = (e) => {
|
||||
if (!this.isOutsideClicked(e.target)) {
|
||||
this.selfClick = true;
|
||||
}
|
||||
};
|
||||
|
||||
OverlayEventBus.on('overlay-click', this.overlayEventListener);
|
||||
},
|
||||
onOverlayLeave() {
|
||||
this.onOverlayHide();
|
||||
},
|
||||
onOverlayAfterLeave(el) {
|
||||
ZIndexUtils.clear(el);
|
||||
},
|
||||
onOverlayHide() {
|
||||
this.unbindOutsideClickListener();
|
||||
this.unbindResizeListener();
|
||||
this.unbindScrollListener();
|
||||
this.overlay = null;
|
||||
OverlayEventBus.off('overlay-click', this.overlayEventListener);
|
||||
this.overlayEventListener = null;
|
||||
},
|
||||
overlayRef(el) {
|
||||
this.overlay = el;
|
||||
},
|
||||
isOutsideClicked(target) {
|
||||
return !this.isTargetClicked(target) && this.overlay && !(this.overlay.isSameNode(target) || this.overlay.contains(target));
|
||||
},
|
||||
isTargetClicked(target) {
|
||||
return this.$refs.icon && (this.$refs.icon.isSameNode(target) || this.$refs.icon.contains(target));
|
||||
},
|
||||
bindOutsideClickListener() {
|
||||
if (!this.outsideClickListener) {
|
||||
this.outsideClickListener = (event) => {
|
||||
if (this.overlayVisible && !this.selfClick && this.isOutsideClicked(event.target)) {
|
||||
this.overlayVisible = false;
|
||||
}
|
||||
|
||||
this.selfClick = false;
|
||||
};
|
||||
|
||||
document.addEventListener('click', this.outsideClickListener);
|
||||
}
|
||||
},
|
||||
unbindOutsideClickListener() {
|
||||
if (this.outsideClickListener) {
|
||||
document.removeEventListener('click', this.outsideClickListener);
|
||||
this.outsideClickListener = null;
|
||||
this.selfClick = false;
|
||||
}
|
||||
},
|
||||
bindScrollListener() {
|
||||
if (!this.scrollHandler) {
|
||||
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.icon, () => {
|
||||
if (this.overlayVisible) {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.scrollHandler.bindScrollListener();
|
||||
},
|
||||
unbindScrollListener() {
|
||||
if (this.scrollHandler) {
|
||||
this.scrollHandler.unbindScrollListener();
|
||||
}
|
||||
},
|
||||
bindResizeListener() {
|
||||
if (!this.resizeListener) {
|
||||
this.resizeListener = () => {
|
||||
if (this.overlayVisible && !DomHandler.isTouchDevice()) {
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', this.resizeListener);
|
||||
}
|
||||
},
|
||||
unbindResizeListener() {
|
||||
if (this.resizeListener) {
|
||||
window.removeEventListener('resize', this.resizeListener);
|
||||
this.resizeListener = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showMenuButton() {
|
||||
return this.showMenu && (this.display === 'row' ? this.type !== 'boolean' : true);
|
||||
},
|
||||
overlayId() {
|
||||
return UniqueComponentId();
|
||||
},
|
||||
matchModes() {
|
||||
return (
|
||||
this.matchModeOptions ||
|
||||
this.$primevue.config.filterMatchModeOptions[this.type].map((key) => {
|
||||
return { label: this.$primevue.config.locale[key], value: key };
|
||||
})
|
||||
);
|
||||
},
|
||||
isShowMatchModes() {
|
||||
return this.type !== 'boolean' && this.showMatchModes && this.matchModes;
|
||||
},
|
||||
operatorOptions() {
|
||||
return [
|
||||
{ label: this.$primevue.config.locale.matchAll, value: FilterOperator.AND },
|
||||
{ label: this.$primevue.config.locale.matchAny, value: FilterOperator.OR }
|
||||
];
|
||||
},
|
||||
noFilterLabel() {
|
||||
return this.$primevue.config.locale ? this.$primevue.config.locale.noFilter : undefined;
|
||||
},
|
||||
isShowOperator() {
|
||||
return this.showOperator && this.filters[this.field].operator;
|
||||
},
|
||||
operator() {
|
||||
return this.filters[this.field].operator;
|
||||
},
|
||||
fieldConstraints() {
|
||||
return this.filters[this.field].constraints || [this.filters[this.field]];
|
||||
},
|
||||
showRemoveIcon() {
|
||||
return this.fieldConstraints.length > 1;
|
||||
},
|
||||
removeRuleButtonLabel() {
|
||||
return this.$primevue.config.locale ? this.$primevue.config.locale.removeRule : undefined;
|
||||
},
|
||||
addRuleButtonLabel() {
|
||||
return this.$primevue.config.locale ? this.$primevue.config.locale.addRule : undefined;
|
||||
},
|
||||
isShowAddConstraint() {
|
||||
return this.showAddButton && this.filters[this.field].operator && this.fieldConstraints && this.fieldConstraints.length < this.maxConstraints;
|
||||
},
|
||||
clearButtonLabel() {
|
||||
return this.$primevue.config.locale ? this.$primevue.config.locale.clear : undefined;
|
||||
},
|
||||
applyButtonLabel() {
|
||||
return this.$primevue.config.locale ? this.$primevue.config.locale.apply : undefined;
|
||||
},
|
||||
filterMenuButtonAriaLabel() {
|
||||
return this.$primevue.config.locale ? (this.overlayVisible ? this.$primevue.config.locale.showFilterMenu : this.$primevue.config.locale.hideFilterMenu) : undefined;
|
||||
},
|
||||
filterOperatorAriaLabel() {
|
||||
return this.$primevue.config.locale ? this.$primevue.config.locale.filterOperator : undefined;
|
||||
},
|
||||
filterConstraintAriaLabel() {
|
||||
return this.$primevue.config.locale ? this.$primevue.config.locale.filterConstraint : undefined;
|
||||
},
|
||||
ptmHeaderFilterClearParams() {
|
||||
return {
|
||||
context: {
|
||||
hidden: this.hasRowFilter()
|
||||
}
|
||||
};
|
||||
},
|
||||
ptmFilterMenuParams() {
|
||||
return {
|
||||
context: {
|
||||
overlayVisible: this.overlayVisible,
|
||||
active: this.hasFilter()
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
components: {
|
||||
CFDropdown: Dropdown,
|
||||
CFButton: Button,
|
||||
Portal: Portal,
|
||||
FilterSlashIcon: FilterSlashIcon,
|
||||
FilterIcon: FilterIcon,
|
||||
TrashIcon: TrashIcon,
|
||||
PlusIcon: PlusIcon
|
||||
},
|
||||
directives: {
|
||||
focustrap: FocusTrap
|
||||
}
|
||||
};
|
||||
</script>
|
1440
components/lib/datatable/DataTable.d.ts
vendored
Executable file
1440
components/lib/datatable/DataTable.d.ts
vendored
Executable file
File diff suppressed because it is too large
Load diff
1412
components/lib/datatable/DataTable.spec.js
Normal file
1412
components/lib/datatable/DataTable.spec.js
Normal file
File diff suppressed because it is too large
Load diff
2107
components/lib/datatable/DataTable.vue
Executable file
2107
components/lib/datatable/DataTable.vue
Executable file
File diff suppressed because it is too large
Load diff
103
components/lib/datatable/FooterCell.vue
Normal file
103
components/lib/datatable/FooterCell.vue
Normal file
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<td :style="containerStyle" :class="containerClass" role="cell" :colspan="columnProp('colspan')" :rowspan="columnProp('rowspan')" v-bind="{ ...getColumnPT('root'), ...getColumnPT('footerCell') }">
|
||||
<component v-if="column.children && column.children.footer" :is="column.children.footer" :column="column" />
|
||||
{{ columnProp('footer') }}
|
||||
</td>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import { DomHandler, ObjectUtils } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'FooterCell',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
props: {
|
||||
column: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
styleObject: {}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.columnProp('frozen')) {
|
||||
this.updateStickyPosition();
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
if (this.columnProp('frozen')) {
|
||||
this.updateStickyPosition();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
columnProp(prop) {
|
||||
return ObjectUtils.getVNodeProp(this.column, prop);
|
||||
},
|
||||
getColumnPT(key) {
|
||||
const columnMetaData = {
|
||||
props: this.column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index: this.index,
|
||||
size: this.$parentInstance?.$parentInstance?.size,
|
||||
showGridlines: this.$parentInstance?.$parentInstance?.showGridlines || false
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(), key, columnMetaData));
|
||||
},
|
||||
getColumnProp() {
|
||||
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined;
|
||||
},
|
||||
updateStickyPosition() {
|
||||
if (this.columnProp('frozen')) {
|
||||
let align = this.columnProp('alignFrozen');
|
||||
|
||||
if (align === 'right') {
|
||||
let right = 0;
|
||||
let next = this.$el.nextElementSibling;
|
||||
|
||||
if (next) {
|
||||
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right || 0);
|
||||
}
|
||||
|
||||
this.styleObject.right = right + 'px';
|
||||
} else {
|
||||
let left = 0;
|
||||
let prev = this.$el.previousElementSibling;
|
||||
|
||||
if (prev) {
|
||||
left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left || 0);
|
||||
}
|
||||
|
||||
this.styleObject.left = left + 'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
containerClass() {
|
||||
return [this.columnProp('footerClass'), this.columnProp('class'), this.cx('footerCell')];
|
||||
},
|
||||
containerStyle() {
|
||||
let bodyStyle = this.columnProp('footerStyle');
|
||||
let columnStyle = this.columnProp('style');
|
||||
|
||||
return this.columnProp('frozen') ? [columnStyle, bodyStyle, this.styleObject] : [columnStyle, bodyStyle];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
369
components/lib/datatable/HeaderCell.vue
Normal file
369
components/lib/datatable/HeaderCell.vue
Normal file
|
@ -0,0 +1,369 @@
|
|||
<template>
|
||||
<th
|
||||
:style="containerStyle"
|
||||
:class="containerClass"
|
||||
:tabindex="columnProp('sortable') ? '0' : null"
|
||||
role="columnheader"
|
||||
:colspan="columnProp('colspan')"
|
||||
:rowspan="columnProp('rowspan')"
|
||||
:aria-sort="ariaSort"
|
||||
@click="onClick"
|
||||
@keydown="onKeyDown"
|
||||
@mousedown="onMouseDown"
|
||||
@dragstart="onDragStart"
|
||||
@dragover="onDragOver"
|
||||
@dragleave="onDragLeave"
|
||||
@drop="onDrop"
|
||||
v-bind="{ ...getColumnPT('root'), ...getColumnPT('headerCell') }"
|
||||
:data-p-sortable-column="columnProp('sortable')"
|
||||
:data-p-resizable-column="resizableColumns"
|
||||
:data-p-highlight="isColumnSorted()"
|
||||
:data-p-filter-column="filterColumn"
|
||||
:data-p-frozen-column="columnProp('frozen')"
|
||||
:data-p-reorderable-column="reorderableColumns"
|
||||
>
|
||||
<span v-if="resizableColumns && !columnProp('frozen')" :class="cx('columnResizer')" @mousedown="onResizeStart" v-bind="getColumnPT('columnResizer')"></span>
|
||||
<div :class="cx('headerContent')" v-bind="getColumnPT('headerContent')">
|
||||
<component v-if="column.children && column.children.header" :is="column.children.header" :column="column" />
|
||||
<span v-if="columnProp('header')" :class="cx('headerTitle')" v-bind="getColumnPT('headerTitle')">{{ columnProp('header') }}</span>
|
||||
<span v-if="columnProp('sortable')" v-bind="getColumnPT('sort')">
|
||||
<component :is="(column.children && column.children.sorticon) || sortableColumnIcon" :sorted="sortState.sorted" :sortOrder="sortState.sortOrder" data-pc-section="sorticon" :class="cx('sortIcon')" v-bind="getColumnPT('sorticon')" />
|
||||
</span>
|
||||
<span v-if="isMultiSorted()" :class="cx('sortBadge')" v-bind="getColumnPT('sortBadge')">{{ getBadgeValue() }}</span>
|
||||
<DTHeaderCheckbox
|
||||
v-if="columnProp('selectionMode') === 'multiple' && filterDisplay !== 'row'"
|
||||
:checked="allRowsSelected"
|
||||
@change="onHeaderCheckboxChange"
|
||||
:disabled="empty"
|
||||
:headerCheckboxIconTemplate="headerCheckboxIconTemplate"
|
||||
:column="column"
|
||||
:pt="pt"
|
||||
/>
|
||||
<DTColumnFilter
|
||||
v-if="filterDisplay === 'menu' && column.children && column.children.filter"
|
||||
:field="columnProp('filterField') || columnProp('field')"
|
||||
:type="columnProp('dataType')"
|
||||
display="menu"
|
||||
:showMenu="columnProp('showFilterMenu')"
|
||||
:filterElement="column.children && column.children.filter"
|
||||
:filterHeaderTemplate="column.children && column.children.filterheader"
|
||||
:filterFooterTemplate="column.children && column.children.filterfooter"
|
||||
:filterClearTemplate="column.children && column.children.filterclear"
|
||||
:filterApplyTemplate="column.children && column.children.filterapply"
|
||||
:filterIconTemplate="column.children && column.children.filtericon"
|
||||
:filterAddIconTemplate="column.children && column.children.filteraddicon"
|
||||
:filterRemoveIconTemplate="column.children && column.children.filterremoveicon"
|
||||
:filterClearIconTemplate="column.children && column.children.filterclearicon"
|
||||
:filters="filters"
|
||||
:filtersStore="filtersStore"
|
||||
:filterInputProps="filterInputProps"
|
||||
@filter-change="$emit('filter-change', $event)"
|
||||
@filter-apply="$emit('filter-apply')"
|
||||
:filterMenuStyle="columnProp('filterMenuStyle')"
|
||||
:filterMenuClass="columnProp('filterMenuClass')"
|
||||
:showOperator="columnProp('showFilterOperator')"
|
||||
:showClearButton="columnProp('showClearButton')"
|
||||
:showApplyButton="columnProp('showApplyButton')"
|
||||
:showMatchModes="columnProp('showFilterMatchModes')"
|
||||
:showAddButton="columnProp('showAddButton')"
|
||||
:matchModeOptions="columnProp('filterMatchModeOptions')"
|
||||
:maxConstraints="columnProp('maxConstraints')"
|
||||
@operator-change="$emit('operator-change', $event)"
|
||||
@matchmode-change="$emit('matchmode-change', $event)"
|
||||
@constraint-add="$emit('constraint-add', $event)"
|
||||
@constraint-remove="$emit('constraint-remove', $event)"
|
||||
@apply-click="$emit('apply-click', $event)"
|
||||
:column="column"
|
||||
:unstyled="unstyled"
|
||||
:pt="pt"
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import SortAltIcon from 'primevue/icons/sortalt';
|
||||
import SortAmountDownIcon from 'primevue/icons/sortamountdown';
|
||||
import SortAmountUpAltIcon from 'primevue/icons/sortamountupalt';
|
||||
import { DomHandler, ObjectUtils } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
import ColumnFilter from './ColumnFilter.vue';
|
||||
import HeaderCheckbox from './HeaderCheckbox.vue';
|
||||
|
||||
export default {
|
||||
name: 'HeaderCell',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
emits: [
|
||||
'column-click',
|
||||
'column-mousedown',
|
||||
'column-dragstart',
|
||||
'column-dragover',
|
||||
'column-dragleave',
|
||||
'column-drop',
|
||||
'column-resizestart',
|
||||
'checkbox-change',
|
||||
'filter-change',
|
||||
'filter-apply',
|
||||
'operator-change',
|
||||
'matchmode-change',
|
||||
'constraint-add',
|
||||
'constraint-remove',
|
||||
'filter-clear',
|
||||
'apply-click'
|
||||
],
|
||||
props: {
|
||||
column: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
resizableColumns: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
groupRowsBy: {
|
||||
type: [Array, String, Function],
|
||||
default: null
|
||||
},
|
||||
sortMode: {
|
||||
type: String,
|
||||
default: 'single'
|
||||
},
|
||||
groupRowSortField: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
sortField: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
sortOrder: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
multiSortMeta: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
allRowsSelected: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
empty: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
filterDisplay: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
filters: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
filtersStore: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
filterColumn: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
reorderableColumns: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
filterInputProps: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
headerCheckboxIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
styleObject: {}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.columnProp('frozen')) {
|
||||
this.updateStickyPosition();
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
if (this.columnProp('frozen')) {
|
||||
this.updateStickyPosition();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
columnProp(prop) {
|
||||
return ObjectUtils.getVNodeProp(this.column, prop);
|
||||
},
|
||||
getColumnPT(key) {
|
||||
const columnMetaData = {
|
||||
props: this.column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index: this.index,
|
||||
sorted: this.isColumnSorted(),
|
||||
resizable: this.resizableColumns,
|
||||
size: this.$parentInstance?.$parentInstance?.size,
|
||||
showGridlines: this.$parentInstance?.$parentInstance?.showGridlines || false
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(), key, columnMetaData));
|
||||
},
|
||||
getColumnProp() {
|
||||
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined; //@todo:
|
||||
},
|
||||
onClick(event) {
|
||||
this.$emit('column-click', { originalEvent: event, column: this.column });
|
||||
},
|
||||
onKeyDown(event) {
|
||||
if ((event.code === 'Enter' || event.code === 'Space') && event.currentTarget.nodeName === 'TH' && DomHandler.getAttribute(event.currentTarget, 'data-p-sortable-column')) {
|
||||
this.$emit('column-click', { originalEvent: event, column: this.column });
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
onMouseDown(event) {
|
||||
this.$emit('column-mousedown', { originalEvent: event, column: this.column });
|
||||
},
|
||||
onDragStart(event) {
|
||||
this.$emit('column-dragstart', event);
|
||||
},
|
||||
onDragOver(event) {
|
||||
this.$emit('column-dragover', event);
|
||||
},
|
||||
onDragLeave(event) {
|
||||
this.$emit('column-dragleave', event);
|
||||
},
|
||||
onDrop(event) {
|
||||
this.$emit('column-drop', event);
|
||||
},
|
||||
onResizeStart(event) {
|
||||
this.$emit('column-resizestart', event);
|
||||
},
|
||||
getMultiSortMetaIndex() {
|
||||
return this.multiSortMeta.findIndex((meta) => meta.field === this.columnProp('field') || meta.field === this.columnProp('sortField'));
|
||||
},
|
||||
getBadgeValue() {
|
||||
let index = this.getMultiSortMetaIndex();
|
||||
|
||||
return this.groupRowsBy && this.groupRowsBy === this.groupRowSortField && index > -1 ? index : index + 1;
|
||||
},
|
||||
isMultiSorted() {
|
||||
return this.sortMode === 'multiple' && this.columnProp('sortable') && this.getMultiSortMetaIndex() > -1;
|
||||
},
|
||||
isColumnSorted() {
|
||||
return this.sortMode === 'single' ? this.sortField && (this.sortField === this.columnProp('field') || this.sortField === this.columnProp('sortField')) : this.isMultiSorted();
|
||||
},
|
||||
updateStickyPosition() {
|
||||
if (this.columnProp('frozen')) {
|
||||
let align = this.columnProp('alignFrozen');
|
||||
|
||||
if (align === 'right') {
|
||||
let right = 0;
|
||||
let next = this.$el.nextElementSibling;
|
||||
|
||||
if (next) {
|
||||
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right || 0);
|
||||
}
|
||||
|
||||
this.styleObject.right = right + 'px';
|
||||
} else {
|
||||
let left = 0;
|
||||
let prev = this.$el.previousElementSibling;
|
||||
|
||||
if (prev) {
|
||||
left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left || 0);
|
||||
}
|
||||
|
||||
this.styleObject.left = left + 'px';
|
||||
}
|
||||
|
||||
let filterRow = this.$el.parentElement.nextElementSibling;
|
||||
|
||||
if (filterRow) {
|
||||
let index = DomHandler.index(this.$el);
|
||||
|
||||
filterRow.children[index].style.left = this.styleObject.left;
|
||||
filterRow.children[index].style.right = this.styleObject.right;
|
||||
}
|
||||
}
|
||||
},
|
||||
onHeaderCheckboxChange(event) {
|
||||
this.$emit('checkbox-change', event);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
containerClass() {
|
||||
return [this.cx('headerCell'), this.filterColumn ? this.columnProp('filterHeaderClass') : this.columnProp('headerClass'), this.columnProp('class')];
|
||||
},
|
||||
containerStyle() {
|
||||
let headerStyle = this.filterColumn ? this.columnProp('filterHeaderStyle') : this.columnProp('headerStyle');
|
||||
let columnStyle = this.columnProp('style');
|
||||
|
||||
return this.columnProp('frozen') ? [columnStyle, headerStyle, this.styleObject] : [columnStyle, headerStyle];
|
||||
},
|
||||
sortState() {
|
||||
let sorted = false;
|
||||
let sortOrder = null;
|
||||
|
||||
if (this.sortMode === 'single') {
|
||||
sorted = this.sortField && (this.sortField === this.columnProp('field') || this.sortField === this.columnProp('sortField'));
|
||||
sortOrder = sorted ? this.sortOrder : 0;
|
||||
} else if (this.sortMode === 'multiple') {
|
||||
let metaIndex = this.getMultiSortMetaIndex();
|
||||
|
||||
if (metaIndex > -1) {
|
||||
sorted = true;
|
||||
sortOrder = this.multiSortMeta[metaIndex].order;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sorted,
|
||||
sortOrder
|
||||
};
|
||||
},
|
||||
sortableColumnIcon() {
|
||||
const { sorted, sortOrder } = this.sortState;
|
||||
|
||||
if (!sorted) return SortAltIcon;
|
||||
else if (sorted && sortOrder > 0) return SortAmountUpAltIcon;
|
||||
else if (sorted && sortOrder < 0) return SortAmountDownIcon;
|
||||
|
||||
return null;
|
||||
},
|
||||
ariaSort() {
|
||||
if (this.columnProp('sortable')) {
|
||||
const { sorted, sortOrder } = this.sortState;
|
||||
|
||||
if (sorted && sortOrder < 0) return 'descending';
|
||||
else if (sorted && sortOrder > 0) return 'ascending';
|
||||
else return 'none';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DTHeaderCheckbox: HeaderCheckbox,
|
||||
DTColumnFilter: ColumnFilter,
|
||||
SortAltIcon: SortAltIcon,
|
||||
SortAmountUpAltIcon: SortAmountUpAltIcon,
|
||||
SortAmountDownIcon: SortAmountDownIcon
|
||||
}
|
||||
};
|
||||
</script>
|
94
components/lib/datatable/HeaderCheckbox.vue
Executable file
94
components/lib/datatable/HeaderCheckbox.vue
Executable file
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div :class="cx('headerCheckboxWrapper')" @click="onClick" @keydown.space.prevent="onClick" v-bind="getColumnPT('headerCheckboxWrapper')">
|
||||
<div class="p-hidden-accessible" v-bind="getColumnPT('hiddenHeaderInputWrapper')" :data-p-hidden-accessible="true">
|
||||
<input
|
||||
ref="input"
|
||||
type="checkbox"
|
||||
:checked="checked"
|
||||
:disabled="disabled"
|
||||
:tabindex="disabled ? null : '0'"
|
||||
:aria-label="headerCheckboxAriaLabel"
|
||||
@focus="onFocus($event)"
|
||||
@blur="onBlur($event)"
|
||||
v-bind="getColumnPT('hiddenHeaderInput')"
|
||||
/>
|
||||
</div>
|
||||
<div ref="box" :class="cx('headerCheckbox')" v-bind="getColumnPT('headerCheckbox')">
|
||||
<component v-if="headerCheckboxIconTemplate" :is="headerCheckboxIconTemplate" :checked="checked" :class="cx('headerCheckboxIcon')" />
|
||||
<CheckIcon v-else-if="!headerCheckboxIconTemplate && !!checked" :class="cx('headerCheckboxIcon')" v-bind="getColumnPT('headerCheckboxIcon')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import CheckIcon from 'primevue/icons/check';
|
||||
import { DomHandler } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'HeaderCheckbox',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
emits: ['change'],
|
||||
props: {
|
||||
checked: null,
|
||||
disabled: null,
|
||||
column: null,
|
||||
headerCheckboxIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
focused: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getColumnPT(key) {
|
||||
const columnMetaData = {
|
||||
props: this.column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
checked: this.checked,
|
||||
focused: this.focused,
|
||||
disabled: this.disabled
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(), key, columnMetaData));
|
||||
},
|
||||
getColumnProp() {
|
||||
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined; //@todo:
|
||||
},
|
||||
onClick(event) {
|
||||
if (!this.disabled) {
|
||||
this.$emit('change', {
|
||||
originalEvent: event,
|
||||
checked: !this.checked
|
||||
});
|
||||
|
||||
DomHandler.focus(this.$refs.input);
|
||||
}
|
||||
},
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
},
|
||||
onBlur() {
|
||||
this.focused = false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
headerCheckboxAriaLabel() {
|
||||
return this.$primevue.config.locale.aria ? (this.checked ? this.$primevue.config.locale.aria.selectAll : this.$primevue.config.locale.aria.unselectAll) : undefined;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
CheckIcon: CheckIcon
|
||||
}
|
||||
};
|
||||
</script>
|
114
components/lib/datatable/RowCheckbox.vue
Executable file
114
components/lib/datatable/RowCheckbox.vue
Executable file
|
@ -0,0 +1,114 @@
|
|||
<template>
|
||||
<div :class="cx('checkboxWrapper')" @click="onClick" v-bind="getColumnPT('checkboxWrapper')">
|
||||
<div class="p-hidden-accessible" v-bind="getColumnPT('hiddenInputWrapper')" :data-p-hidden-accessible="true">
|
||||
<input
|
||||
ref="input"
|
||||
type="checkbox"
|
||||
:checked="checked"
|
||||
:disabled="$attrs.disabled"
|
||||
:tabindex="$attrs.disabled ? null : '0'"
|
||||
:aria-label="checkboxAriaLabel"
|
||||
@focus="onFocus($event)"
|
||||
@blur="onBlur($event)"
|
||||
@keydown="onKeydown"
|
||||
v-bind="getColumnPT('hiddenInput')"
|
||||
/>
|
||||
</div>
|
||||
<div ref="box" :class="cx('checkbox')" v-bind="getColumnPT('checkbox')">
|
||||
<component v-if="rowCheckboxIconTemplate" :is="rowCheckboxIconTemplate" :checked="checked" :class="cx('checkboxIcon')" />
|
||||
<CheckIcon v-else-if="!rowCheckboxIconTemplate && !!checked" :class="cx('checkboxIcon')" v-bind="getColumnPT('checkboxIcon')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import CheckIcon from 'primevue/icons/check';
|
||||
import { DomHandler } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'RowCheckbox',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
emits: ['change'],
|
||||
props: {
|
||||
value: null,
|
||||
checked: null,
|
||||
column: null,
|
||||
rowCheckboxIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
focused: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getColumnPT(key) {
|
||||
const columnMetaData = {
|
||||
props: this.column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index: this.index,
|
||||
checked: this.checked,
|
||||
focused: this.focused,
|
||||
disabled: this.$attrs.disabled
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(), key, columnMetaData));
|
||||
},
|
||||
getColumnProp() {
|
||||
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined; //@todo:
|
||||
},
|
||||
onClick(event) {
|
||||
if (!this.$attrs.disabled) {
|
||||
this.$emit('change', {
|
||||
originalEvent: event,
|
||||
data: this.value
|
||||
});
|
||||
|
||||
DomHandler.focus(this.$refs.input);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
},
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
},
|
||||
onBlur() {
|
||||
this.focused = false;
|
||||
},
|
||||
onKeydown(event) {
|
||||
switch (event.code) {
|
||||
case 'Space': {
|
||||
this.onClick(event);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
checkboxAriaLabel() {
|
||||
return this.$primevue.config.locale.aria ? (this.checked ? this.$primevue.config.locale.aria.selectRow : this.$primevue.config.locale.aria.unselectRow) : undefined;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
CheckIcon: CheckIcon
|
||||
}
|
||||
};
|
||||
</script>
|
79
components/lib/datatable/RowRadioButton.vue
Executable file
79
components/lib/datatable/RowRadioButton.vue
Executable file
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<div :class="cx('radiobuttonWrapper')" @click="onClick" v-bind="getColumnPT('radiobuttonWrapper')">
|
||||
<div class="p-hidden-accessible" v-bind="getColumnPT('hiddenInputWrapper')" :data-p-hidden-accessible="true">
|
||||
<input ref="input" type="radio" :checked="checked" :disabled="$attrs.disabled" :name="name" tabindex="0" @focus="onFocus($event)" @blur="onBlur($event)" @keydown.space.prevent="onClick" v-bind="getColumnPT('hiddenInput')" />
|
||||
</div>
|
||||
<div ref="box" :class="cx('radiobutton')" v-bind="getColumnPT('radiobutton')">
|
||||
<div :class="cx('radiobuttonIcon')" v-bind="getColumnPT('radiobuttonIcon')"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import { DomHandler } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'RowRadioButton',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
inheritAttrs: false,
|
||||
emits: ['change'],
|
||||
props: {
|
||||
value: null,
|
||||
checked: null,
|
||||
name: null,
|
||||
column: null,
|
||||
index: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
focused: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getColumnPT(key) {
|
||||
const columnMetaData = {
|
||||
props: this.column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index: this.index,
|
||||
checked: this.checked,
|
||||
focused: this.focused,
|
||||
disabled: this.$attrs.disabled
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(), key, columnMetaData));
|
||||
},
|
||||
getColumnProp() {
|
||||
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined; //@todo:
|
||||
},
|
||||
onClick(event) {
|
||||
if (!this.disabled) {
|
||||
if (!this.checked) {
|
||||
this.$emit('change', {
|
||||
originalEvent: event,
|
||||
data: this.value
|
||||
});
|
||||
|
||||
DomHandler.focus(this.$refs.input);
|
||||
}
|
||||
}
|
||||
},
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
},
|
||||
onBlur() {
|
||||
this.focused = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
639
components/lib/datatable/TableBody.vue
Executable file
639
components/lib/datatable/TableBody.vue
Executable file
|
@ -0,0 +1,639 @@
|
|||
<template>
|
||||
<tbody :ref="bodyRef" :class="cx('tbody')" role="rowgroup" :style="bodyStyle" v-bind="ptm('tbody', ptmTBodyOptions)">
|
||||
<template v-if="!empty">
|
||||
<template v-for="(rowData, index) of value">
|
||||
<tr
|
||||
v-if="templates['groupheader'] && rowGroupMode === 'subheader' && shouldRenderRowGroupHeader(value, rowData, getRowIndex(index))"
|
||||
:key="getRowKey(rowData, getRowIndex(index)) + '_subheader'"
|
||||
:class="cx('rowGroupHeader')"
|
||||
:style="rowGroupHeaderStyle"
|
||||
role="row"
|
||||
v-bind="ptm('rowGroupHeader')"
|
||||
>
|
||||
<td :colspan="columnsLength - 1" v-bind="{ ...getColumnPT('bodycell'), ...ptm('rowGroupHeaderCell') }">
|
||||
<button v-if="expandableRowGroups" :class="cx('rowGroupToggler')" @click="onRowGroupToggle($event, rowData)" type="button" v-bind="ptm('rowGroupToggler')">
|
||||
<component v-if="templates['rowgrouptogglericon']" :is="templates['rowgrouptogglericon']" :expanded="isRowGroupExpanded(rowData)" />
|
||||
<template v-else>
|
||||
<span v-if="isRowGroupExpanded(rowData) && expandedRowIcon" :class="[cx('rowGroupTogglerIcon'), expandedRowIcon]" v-bind="ptm('rowGroupTogglerIcon')" />
|
||||
<ChevronDownIcon v-else-if="isRowGroupExpanded(rowData) && !expandedRowIcon" :class="cx('rowGroupTogglerIcon')" v-bind="ptm('rowGroupTogglerIcon')" />
|
||||
<span v-else-if="!isRowGroupExpanded(rowData) && collapsedRowIcon" :class="[cx('rowGroupTogglerIcon'), collapsedRowIcon]" v-bind="ptm('rowGroupTogglerIcon')" />
|
||||
<ChevronRightIcon v-else-if="!isRowGroupExpanded(rowData) && !collapsedRowIcon" :class="cx('rowGroupTogglerIcon')" v-bind="ptm('rowGroupTogglerIcon')" />
|
||||
</template>
|
||||
</button>
|
||||
<component :is="templates['groupheader']" :data="rowData" :index="getRowIndex(index)" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="expandableRowGroups ? isRowGroupExpanded(rowData) : true"
|
||||
:key="getRowKey(rowData, getRowIndex(index))"
|
||||
:class="getRowClass(rowData)"
|
||||
:style="getRowStyle(rowData)"
|
||||
:tabindex="setRowTabindex(index)"
|
||||
role="row"
|
||||
:aria-selected="selectionMode ? isSelected(rowData) : null"
|
||||
@click="onRowClick($event, rowData, getRowIndex(index))"
|
||||
@dblclick="onRowDblClick($event, rowData, getRowIndex(index))"
|
||||
@contextmenu="onRowRightClick($event, rowData, getRowIndex(index))"
|
||||
@touchend="onRowTouchEnd($event)"
|
||||
@keydown.self="onRowKeyDown($event, rowData, getRowIndex(index))"
|
||||
@mousedown="onRowMouseDown($event)"
|
||||
@dragstart="onRowDragStart($event, getRowIndex(index))"
|
||||
@dragover="onRowDragOver($event, getRowIndex(index))"
|
||||
@dragleave="onRowDragLeave($event)"
|
||||
@dragend="onRowDragEnd($event)"
|
||||
@drop="onRowDrop($event)"
|
||||
v-bind="getBodyRowPTOptions('bodyRow', rowData, index)"
|
||||
:data-p-selectable-row="selectionMode ? true : false"
|
||||
:data-p-highlight="selection && isSelected(rowData)"
|
||||
:data-p-highlight-contextmenu="contextMenuSelection && isSelectedWithContextMenu(rowData)"
|
||||
>
|
||||
<template v-for="(col, i) of columns">
|
||||
<DTBodyCell
|
||||
v-if="shouldRenderBodyCell(value, col, getRowIndex(index))"
|
||||
:key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i"
|
||||
:rowData="rowData"
|
||||
:column="col"
|
||||
:rowIndex="getRowIndex(index)"
|
||||
:index="i"
|
||||
:selected="isSelected(rowData)"
|
||||
:frozenRow="frozenRow"
|
||||
:rowspan="rowGroupMode === 'rowspan' ? calculateRowGroupSize(value, col, getRowIndex(index)) : null"
|
||||
:editMode="editMode"
|
||||
:editing="editMode === 'row' && isRowEditing(rowData)"
|
||||
:editingMeta="editingMeta"
|
||||
:responsiveLayout="responsiveLayout"
|
||||
:virtualScrollerContentProps="virtualScrollerContentProps"
|
||||
:ariaControls="expandedRowId + '_' + index + '_expansion'"
|
||||
:name="nameAttributeSelector"
|
||||
:isRowExpanded="isRowExpanded(rowData)"
|
||||
:expandedRowIcon="expandedRowIcon"
|
||||
:collapsedRowIcon="collapsedRowIcon"
|
||||
@radio-change="onRadioChange($event)"
|
||||
@checkbox-change="onCheckboxChange($event)"
|
||||
@row-toggle="onRowToggle($event)"
|
||||
@cell-edit-init="onCellEditInit($event)"
|
||||
@cell-edit-complete="onCellEditComplete($event)"
|
||||
@cell-edit-cancel="onCellEditCancel($event)"
|
||||
@row-edit-init="onRowEditInit($event)"
|
||||
@row-edit-save="onRowEditSave($event)"
|
||||
@row-edit-cancel="onRowEditCancel($event)"
|
||||
@editing-meta-change="onEditingMetaChange"
|
||||
:unstyled="unstyled"
|
||||
:pt="pt"
|
||||
/>
|
||||
</template>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="templates['expansion'] && expandedRows && isRowExpanded(rowData)"
|
||||
:key="getRowKey(rowData, getRowIndex(index)) + '_expansion'"
|
||||
:id="expandedRowId + '_' + index + '_expansion'"
|
||||
:class="cx('rowExpansion')"
|
||||
role="row"
|
||||
v-bind="ptm('rowExpansion')"
|
||||
>
|
||||
<td :colspan="columnsLength" v-bind="{ ...getColumnPT('bodycell'), ...ptm('rowExpansionCell') }">
|
||||
<component :is="templates['expansion']" :data="rowData" :index="getRowIndex(index)" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="templates['groupfooter'] && rowGroupMode === 'subheader' && shouldRenderRowGroupFooter(value, rowData, getRowIndex(index))"
|
||||
:key="getRowKey(rowData, getRowIndex(index)) + '_subfooter'"
|
||||
:class="cx('rowGroupFooter')"
|
||||
role="row"
|
||||
v-bind="ptm('rowGroupFooter')"
|
||||
>
|
||||
<td :colspan="columnsLength - 1" v-bind="{ ...getColumnPT('bodycell'), ...ptm('rowGroupFooterCell') }">
|
||||
<component :is="templates['groupfooter']" :data="rowData" :index="getRowIndex(index)" />
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
<tr v-else :class="cx('emptyMessage')" role="row" v-bind="ptm('emptyMessage')">
|
||||
<td :colspan="columnsLength" v-bind="{ ...getColumnPT('bodycell'), ...ptm('emptyMessageCell') }">
|
||||
<component v-if="templates.empty" :is="templates.empty" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import ChevronDownIcon from 'primevue/icons/chevrondown';
|
||||
import ChevronRightIcon from 'primevue/icons/chevronright';
|
||||
import { DomHandler, ObjectUtils, UniqueComponentId } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
import BodyCell from './BodyCell.vue';
|
||||
|
||||
export default {
|
||||
name: 'TableBody',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
emits: [
|
||||
'rowgroup-toggle',
|
||||
'row-click',
|
||||
'row-dblclick',
|
||||
'row-rightclick',
|
||||
'row-touchend',
|
||||
'row-keydown',
|
||||
'row-mousedown',
|
||||
'row-dragstart',
|
||||
'row-dragover',
|
||||
'row-dragleave',
|
||||
'row-dragend',
|
||||
'row-drop',
|
||||
'row-toggle',
|
||||
'radio-change',
|
||||
'checkbox-change',
|
||||
'cell-edit-init',
|
||||
'cell-edit-complete',
|
||||
'cell-edit-cancel',
|
||||
'row-edit-init',
|
||||
'row-edit-save',
|
||||
'row-edit-cancel',
|
||||
'editing-meta-change'
|
||||
],
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
columns: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
frozenRow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
empty: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rowGroupMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
groupRowsBy: {
|
||||
type: [Array, String, Function],
|
||||
default: null
|
||||
},
|
||||
expandableRowGroups: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expandedRowGroups: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
first: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
dataKey: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
expandedRowIcon: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
collapsedRowIcon: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
expandedRows: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
expandedRowKeys: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
selection: {
|
||||
type: [Array, Object],
|
||||
default: null
|
||||
},
|
||||
selectionKeys: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
selectionMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
contextMenu: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
contextMenuSelection: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
rowClass: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
rowStyle: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
editMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
compareSelectionBy: {
|
||||
type: String,
|
||||
default: 'deepEquals'
|
||||
},
|
||||
editingRows: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
editingRowKeys: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
editingMeta: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
templates: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
scrollable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
responsiveLayout: {
|
||||
type: String,
|
||||
default: 'stack'
|
||||
},
|
||||
virtualScrollerContentProps: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
isVirtualScrollerDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rowGroupHeaderStyleObject: {},
|
||||
tabindexArray: [],
|
||||
isARowSelected: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.frozenRow) {
|
||||
this.updateFrozenRowStickyPosition();
|
||||
}
|
||||
|
||||
if (this.scrollable && this.rowGroupMode === 'subheader') {
|
||||
this.updateFrozenRowGroupHeaderStickyPosition();
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
if (this.frozenRow) {
|
||||
this.updateFrozenRowStickyPosition();
|
||||
}
|
||||
|
||||
if (this.scrollable && this.rowGroupMode === 'subheader') {
|
||||
this.updateFrozenRowGroupHeaderStickyPosition();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
columnProp(col, prop) {
|
||||
return ObjectUtils.getVNodeProp(col, prop);
|
||||
},
|
||||
getColumnPT(key) {
|
||||
const columnMetaData = {
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp({}), key, columnMetaData));
|
||||
},
|
||||
getColumnProp(column) {
|
||||
return column.props && column.props.pt ? column.props.pt : undefined; //@todo
|
||||
},
|
||||
getBodyRowPTOptions(key, rowdata, index) {
|
||||
return this.ptm(key, {
|
||||
context: {
|
||||
index,
|
||||
selectable: this.$parentInstance?.$parentInstance?.rowHover || this.$parentInstance?.$parentInstance?.selectionMode,
|
||||
selected: this.isSelected(rowdata),
|
||||
stripedRows: this.$parentInstance?.$parentInstance?.stripedRows || false
|
||||
}
|
||||
});
|
||||
},
|
||||
shouldRenderRowGroupHeader(value, rowData, i) {
|
||||
let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
|
||||
let prevRowData = value[i - 1];
|
||||
|
||||
if (prevRowData) {
|
||||
let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.groupRowsBy);
|
||||
|
||||
return currentRowFieldData !== previousRowFieldData;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
getRowKey(rowData, index) {
|
||||
return this.dataKey ? ObjectUtils.resolveFieldData(rowData, this.dataKey) : this.getRowIndex(index);
|
||||
},
|
||||
getRowIndex(index) {
|
||||
const getItemOptions = this.getVirtualScrollerProp('getItemOptions');
|
||||
|
||||
return getItemOptions ? getItemOptions(index).index : index;
|
||||
},
|
||||
getRowStyle(rowData) {
|
||||
if (this.rowStyle) {
|
||||
return this.rowStyle(rowData);
|
||||
}
|
||||
},
|
||||
getRowClass(rowData) {
|
||||
let rowStyleClass = [];
|
||||
|
||||
if (this.rowClass) {
|
||||
let rowClassValue = this.rowClass(rowData);
|
||||
|
||||
if (rowClassValue) {
|
||||
rowStyleClass.push(rowClassValue);
|
||||
}
|
||||
}
|
||||
|
||||
return [this.cx('row', { rowData }), rowStyleClass];
|
||||
},
|
||||
shouldRenderRowGroupFooter(value, rowData, i) {
|
||||
if (this.expandableRowGroups && !this.isRowGroupExpanded(rowData)) {
|
||||
return false;
|
||||
} else {
|
||||
let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
|
||||
let nextRowData = value[i + 1];
|
||||
|
||||
if (nextRowData) {
|
||||
let nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.groupRowsBy);
|
||||
|
||||
return currentRowFieldData !== nextRowFieldData;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
shouldRenderBodyCell(value, column, i) {
|
||||
if (this.rowGroupMode) {
|
||||
if (this.rowGroupMode === 'subheader') {
|
||||
return this.groupRowsBy !== this.columnProp(column, 'field');
|
||||
} else if (this.rowGroupMode === 'rowspan') {
|
||||
if (this.isGrouped(column)) {
|
||||
let prevRowData = value[i - 1];
|
||||
|
||||
if (prevRowData) {
|
||||
let currentRowFieldData = ObjectUtils.resolveFieldData(value[i], this.columnProp(column, 'field'));
|
||||
let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.columnProp(column, 'field'));
|
||||
|
||||
return currentRowFieldData !== previousRowFieldData;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return !this.columnProp(column, 'hidden');
|
||||
}
|
||||
},
|
||||
calculateRowGroupSize(value, column, index) {
|
||||
if (this.isGrouped(column)) {
|
||||
let currentRowFieldData = ObjectUtils.resolveFieldData(value[index], this.columnProp(column, 'field'));
|
||||
let nextRowFieldData = currentRowFieldData;
|
||||
let groupRowSpan = 0;
|
||||
|
||||
while (currentRowFieldData === nextRowFieldData) {
|
||||
groupRowSpan++;
|
||||
let nextRowData = value[++index];
|
||||
|
||||
if (nextRowData) {
|
||||
nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.columnProp(column, 'field'));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return groupRowSpan === 1 ? null : groupRowSpan;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
isGrouped(column) {
|
||||
if (this.groupRowsBy && this.columnProp(column, 'field')) {
|
||||
if (Array.isArray(this.groupRowsBy)) return this.groupRowsBy.indexOf(column.props.field) > -1;
|
||||
else return this.groupRowsBy === column.props.field;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isRowEditing(rowData) {
|
||||
if (rowData && this.editingRows) {
|
||||
if (this.dataKey) return this.editingRowKeys ? this.editingRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
|
||||
else return this.findIndex(rowData, this.editingRows) > -1;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
isRowExpanded(rowData) {
|
||||
if (rowData && this.expandedRows) {
|
||||
if (this.dataKey) return this.expandedRowKeys ? this.expandedRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
|
||||
else return this.findIndex(rowData, this.expandedRows) > -1;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
isRowGroupExpanded(rowData) {
|
||||
if (this.expandableRowGroups && this.expandedRowGroups) {
|
||||
let groupFieldValue = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
|
||||
|
||||
return this.expandedRowGroups.indexOf(groupFieldValue) > -1;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
isSelected(rowData) {
|
||||
if (rowData && this.selection) {
|
||||
if (this.dataKey) {
|
||||
return this.selectionKeys ? this.selectionKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
|
||||
} else {
|
||||
if (this.selection instanceof Array) return this.findIndexInSelection(rowData) > -1;
|
||||
else return this.equals(rowData, this.selection);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
isSelectedWithContextMenu(rowData) {
|
||||
if (rowData && this.contextMenuSelection) {
|
||||
return this.equals(rowData, this.contextMenuSelection, this.dataKey);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
findIndexInSelection(rowData) {
|
||||
return this.findIndex(rowData, this.selection);
|
||||
},
|
||||
findIndex(rowData, collection) {
|
||||
let index = -1;
|
||||
|
||||
if (collection && collection.length) {
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
if (this.equals(rowData, collection[i])) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
},
|
||||
equals(data1, data2) {
|
||||
return this.compareSelectionBy === 'equals' ? data1 === data2 : ObjectUtils.equals(data1, data2, this.dataKey);
|
||||
},
|
||||
onRowGroupToggle(event, data) {
|
||||
this.$emit('rowgroup-toggle', { originalEvent: event, data: data });
|
||||
},
|
||||
onRowClick(event, rowData, rowIndex) {
|
||||
this.$emit('row-click', { originalEvent: event, data: rowData, index: rowIndex });
|
||||
},
|
||||
onRowDblClick(event, rowData, rowIndex) {
|
||||
this.$emit('row-dblclick', { originalEvent: event, data: rowData, index: rowIndex });
|
||||
},
|
||||
onRowRightClick(event, rowData, rowIndex) {
|
||||
this.$emit('row-rightclick', { originalEvent: event, data: rowData, index: rowIndex });
|
||||
},
|
||||
onRowTouchEnd(event) {
|
||||
this.$emit('row-touchend', event);
|
||||
},
|
||||
onRowKeyDown(event, rowData, rowIndex) {
|
||||
this.$emit('row-keydown', { originalEvent: event, data: rowData, index: rowIndex });
|
||||
},
|
||||
onRowMouseDown(event) {
|
||||
this.$emit('row-mousedown', event);
|
||||
},
|
||||
onRowDragStart(event, rowIndex) {
|
||||
this.$emit('row-dragstart', { originalEvent: event, index: rowIndex });
|
||||
},
|
||||
onRowDragOver(event, rowIndex) {
|
||||
this.$emit('row-dragover', { originalEvent: event, index: rowIndex });
|
||||
},
|
||||
onRowDragLeave(event) {
|
||||
this.$emit('row-dragleave', event);
|
||||
},
|
||||
onRowDragEnd(event) {
|
||||
this.$emit('row-dragend', event);
|
||||
},
|
||||
onRowDrop(event) {
|
||||
this.$emit('row-drop', event);
|
||||
},
|
||||
onRowToggle(event) {
|
||||
this.$emit('row-toggle', event);
|
||||
},
|
||||
onRadioChange(event) {
|
||||
this.$emit('radio-change', event);
|
||||
},
|
||||
onCheckboxChange(event) {
|
||||
this.$emit('checkbox-change', event);
|
||||
},
|
||||
onCellEditInit(event) {
|
||||
this.$emit('cell-edit-init', event);
|
||||
},
|
||||
onCellEditComplete(event) {
|
||||
this.$emit('cell-edit-complete', event);
|
||||
},
|
||||
onCellEditCancel(event) {
|
||||
this.$emit('cell-edit-cancel', event);
|
||||
},
|
||||
onRowEditInit(event) {
|
||||
this.$emit('row-edit-init', event);
|
||||
},
|
||||
onRowEditSave(event) {
|
||||
this.$emit('row-edit-save', event);
|
||||
},
|
||||
onRowEditCancel(event) {
|
||||
this.$emit('row-edit-cancel', event);
|
||||
},
|
||||
onEditingMetaChange(event) {
|
||||
this.$emit('editing-meta-change', event);
|
||||
},
|
||||
updateFrozenRowStickyPosition() {
|
||||
this.$el.style.top = DomHandler.getOuterHeight(this.$el.previousElementSibling) + 'px';
|
||||
},
|
||||
updateFrozenRowGroupHeaderStickyPosition() {
|
||||
let tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling);
|
||||
|
||||
this.rowGroupHeaderStyleObject.top = tableHeaderHeight + 'px';
|
||||
},
|
||||
getVirtualScrollerProp(option, options) {
|
||||
options = options || this.virtualScrollerContentProps;
|
||||
|
||||
return options ? options[option] : null;
|
||||
},
|
||||
bodyRef(el) {
|
||||
// For VirtualScroller
|
||||
const contentRef = this.getVirtualScrollerProp('contentRef');
|
||||
|
||||
contentRef && contentRef(el);
|
||||
},
|
||||
setRowTabindex(index) {
|
||||
if (this.selection === null && (this.selectionMode === 'single' || this.selectionMode === 'multiple')) {
|
||||
return index === 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
columnsLength() {
|
||||
let hiddenColLength = 0;
|
||||
|
||||
this.columns.forEach((column) => {
|
||||
if (this.columnProp(column, 'selectionMode') === 'single') hiddenColLength--;
|
||||
if (this.columnProp(column, 'hidden')) hiddenColLength++;
|
||||
});
|
||||
|
||||
return this.columns ? this.columns.length - hiddenColLength : 0;
|
||||
},
|
||||
rowGroupHeaderStyle() {
|
||||
if (this.scrollable) {
|
||||
return { top: this.rowGroupHeaderStyleObject.top };
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
bodyStyle() {
|
||||
return this.getVirtualScrollerProp('contentStyle');
|
||||
},
|
||||
expandedRowId() {
|
||||
return UniqueComponentId();
|
||||
},
|
||||
nameAttributeSelector() {
|
||||
return UniqueComponentId();
|
||||
},
|
||||
ptmTBodyOptions() {
|
||||
return {
|
||||
context: {
|
||||
scrollable: this.$parentInstance?.$parentInstance?.scrollable
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DTBodyCell: BodyCell,
|
||||
ChevronDownIcon: ChevronDownIcon,
|
||||
ChevronRightIcon: ChevronRightIcon
|
||||
}
|
||||
};
|
||||
</script>
|
136
components/lib/datatable/TableFooter.vue
Executable file
136
components/lib/datatable/TableFooter.vue
Executable file
|
@ -0,0 +1,136 @@
|
|||
<template>
|
||||
<tfoot v-if="hasFooter" :class="cx('tfoot')" :style="sx('tfoot')" role="rowgroup" v-bind="columnGroup ? { ...ptm('tfoot', ptmTFootOptions), ...getColumnGroupPT('root') } : ptm('tfoot', ptmTFootOptions)" data-pc-section="tfoot">
|
||||
<tr v-if="!columnGroup" role="row" v-bind="ptm('footerRow')">
|
||||
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
|
||||
<DTFooterCell v-if="!columnProp(col, 'hidden')" :column="col" :pt="pt" />
|
||||
</template>
|
||||
</tr>
|
||||
<template v-else>
|
||||
<tr v-for="(row, i) of getFooterRows()" :key="i" role="row" v-bind="{ ...ptm('footerRow'), ...getRowPT(row, 'root', i) }">
|
||||
<template v-for="(col, j) of getFooterColumns(row)" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || j">
|
||||
<DTFooterCell v-if="!columnProp(col, 'hidden')" :column="col" :index="i" :pt="pt" />
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
</tfoot>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import { ObjectUtils } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
import FooterCell from './FooterCell.vue';
|
||||
|
||||
export default {
|
||||
name: 'TableFooter',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
props: {
|
||||
columnGroup: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
columns: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
columnProp(col, prop) {
|
||||
return ObjectUtils.getVNodeProp(col, prop);
|
||||
},
|
||||
getColumnGroupPT(key) {
|
||||
const columnGroupMetaData = {
|
||||
props: this.getColumnGroupProps(),
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
type: 'footer',
|
||||
scrollable: this.ptmTFootOptions.context.scrollable
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`columnGroup.${key}`, { columnGroup: columnGroupMetaData }), this.ptm(`columnGroup.${key}`, columnGroupMetaData), this.ptmo(this.getColumnGroupProps(), key, columnGroupMetaData));
|
||||
},
|
||||
getColumnGroupProps() {
|
||||
return this.columnGroup && this.columnGroup.props && this.columnGroup.props.pt ? this.columnGroup.props.pt : undefined; //@todo
|
||||
},
|
||||
getRowPT(row, key, index) {
|
||||
const rowMetaData = {
|
||||
props: row.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`row.${key}`, { row: rowMetaData }), this.ptm(`row.${key}`, rowMetaData), this.ptmo(this.getRowProp(row), key, rowMetaData));
|
||||
},
|
||||
getRowProp(row) {
|
||||
return row.props && row.props.pt ? row.props.pt : undefined; //@todo
|
||||
},
|
||||
getFooterRows() {
|
||||
let rows = [];
|
||||
|
||||
let columnGroup = this.columnGroup;
|
||||
|
||||
if (columnGroup.children && columnGroup.children.default) {
|
||||
for (let child of columnGroup.children.default()) {
|
||||
if (child.type.name === 'Row') {
|
||||
rows.push(child);
|
||||
} else if (child.children && child.children instanceof Array) {
|
||||
rows = child.children;
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
},
|
||||
getFooterColumns(row) {
|
||||
let cols = [];
|
||||
|
||||
if (row.children && row.children.default) {
|
||||
row.children.default().forEach((child) => {
|
||||
if (child.children && child.children instanceof Array) cols = [...cols, ...child.children];
|
||||
else if (child.type.name === 'Column') cols.push(child);
|
||||
});
|
||||
|
||||
return cols;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasFooter() {
|
||||
let hasFooter = false;
|
||||
|
||||
if (this.columnGroup) {
|
||||
hasFooter = true;
|
||||
} else if (this.columns) {
|
||||
for (let col of this.columns) {
|
||||
if (this.columnProp(col, 'footer') || (col.children && col.children.footer)) {
|
||||
hasFooter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasFooter;
|
||||
},
|
||||
ptmTFootOptions() {
|
||||
return {
|
||||
context: {
|
||||
scrollable: this.$parentInstance?.$parentInstance?.scrollable
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DTFooterCell: FooterCell
|
||||
}
|
||||
};
|
||||
</script>
|
342
components/lib/datatable/TableHeader.vue
Executable file
342
components/lib/datatable/TableHeader.vue
Executable file
|
@ -0,0 +1,342 @@
|
|||
<template>
|
||||
<thead :class="cx('thead')" :style="sx('thead')" role="rowgroup" v-bind="columnGroup ? { ...ptm('thead', ptmTHeadOptions), ...getColumnGroupPT('root') } : ptm('thead', ptmTHeadOptions)" data-pc-section="thead">
|
||||
<template v-if="!columnGroup">
|
||||
<tr role="row" v-bind="ptm('headerRow')">
|
||||
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
|
||||
<DTHeaderCell
|
||||
v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || groupRowsBy !== columnProp(col, 'field'))"
|
||||
:column="col"
|
||||
:index="i"
|
||||
@column-click="$emit('column-click', $event)"
|
||||
@column-mousedown="$emit('column-mousedown', $event)"
|
||||
@column-dragstart="$emit('column-dragstart', $event)"
|
||||
@column-dragover="$emit('column-dragover', $event)"
|
||||
@column-dragleave="$emit('column-dragleave', $event)"
|
||||
@column-drop="$emit('column-drop', $event)"
|
||||
:groupRowsBy="groupRowsBy"
|
||||
:groupRowSortField="groupRowSortField"
|
||||
:reorderableColumns="reorderableColumns"
|
||||
:resizableColumns="resizableColumns"
|
||||
@column-resizestart="$emit('column-resizestart', $event)"
|
||||
:sortMode="sortMode"
|
||||
:sortField="sortField"
|
||||
:sortOrder="sortOrder"
|
||||
:multiSortMeta="multiSortMeta"
|
||||
:allRowsSelected="allRowsSelected"
|
||||
:empty="empty"
|
||||
@checkbox-change="$emit('checkbox-change', $event)"
|
||||
:filters="filters"
|
||||
:filterDisplay="filterDisplay"
|
||||
:filtersStore="filtersStore"
|
||||
:filterInputProps="filterInputProps"
|
||||
@filter-change="$emit('filter-change', $event)"
|
||||
@filter-apply="$emit('filter-apply')"
|
||||
@operator-change="$emit('operator-change', $event)"
|
||||
@matchmode-change="$emit('matchmode-change', $event)"
|
||||
@constraint-add="$emit('constraint-add', $event)"
|
||||
@constraint-remove="$emit('constraint-remove', $event)"
|
||||
@apply-click="$emit('apply-click', $event)"
|
||||
:headerCheckboxIconTemplate="headerCheckboxIconTemplate"
|
||||
:pt="pt"
|
||||
/>
|
||||
</template>
|
||||
</tr>
|
||||
<tr v-if="filterDisplay === 'row'" role="row" v-bind="ptm('headerRow')">
|
||||
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
|
||||
<th
|
||||
v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || groupRowsBy !== columnProp(col, 'field'))"
|
||||
:style="getFilterColumnHeaderStyle(col)"
|
||||
:class="getFilterColumnHeaderClass(col)"
|
||||
v-bind="{ ...getColumnPT(col, 'root', i), ...getColumnPT(col, 'headerCell', i) }"
|
||||
>
|
||||
<DTHeaderCheckbox v-if="columnProp(col, 'selectionMode') === 'multiple'" :checked="allRowsSelected" :disabled="empty" @change="$emit('checkbox-change', $event)" :column="col" :pt="pt" />
|
||||
<DTColumnFilter
|
||||
v-if="col.children && col.children.filter"
|
||||
:field="columnProp(col, 'filterField') || columnProp(col, 'field')"
|
||||
:type="columnProp(col, 'dataType')"
|
||||
display="row"
|
||||
:showMenu="columnProp(col, 'showFilterMenu')"
|
||||
:filterElement="col.children && col.children.filter"
|
||||
:filterHeaderTemplate="col.children && col.children.filterheader"
|
||||
:filterFooterTemplate="col.children && col.children.filterfooter"
|
||||
:filterClearTemplate="col.children && col.children.filterclear"
|
||||
:filterApplyTemplate="col.children && col.children.filterapply"
|
||||
:filterIconTemplate="col.children && col.children.filtericon"
|
||||
:filterAddIconTemplate="col.children && col.children.filteraddicon"
|
||||
:filterRemoveIconTemplate="col.children && col.children.filterremoveicon"
|
||||
:filterClearIconTemplate="col.children && col.children.filterclearicon"
|
||||
:filters="filters"
|
||||
:filtersStore="filtersStore"
|
||||
:filterInputProps="filterInputProps"
|
||||
@filter-change="$emit('filter-change', $event)"
|
||||
@filter-apply="$emit('filter-apply')"
|
||||
:filterMenuStyle="columnProp(col, 'filterMenuStyle')"
|
||||
:filterMenuClass="columnProp(col, 'filterMenuClass')"
|
||||
:showOperator="columnProp(col, 'showFilterOperator')"
|
||||
:showClearButton="columnProp(col, 'showClearButton')"
|
||||
:showApplyButton="columnProp(col, 'showApplyButton')"
|
||||
:showMatchModes="columnProp(col, 'showFilterMatchModes')"
|
||||
:showAddButton="columnProp(col, 'showAddButton')"
|
||||
:matchModeOptions="columnProp(col, 'filterMatchModeOptions')"
|
||||
:maxConstraints="columnProp(col, 'maxConstraints')"
|
||||
@operator-change="$emit('operator-change', $event)"
|
||||
@matchmode-change="$emit('matchmode-change', $event)"
|
||||
@constraint-add="$emit('constraint-add', $event)"
|
||||
@constraint-remove="$emit('constraint-remove', $event)"
|
||||
@apply-click="$emit('apply-click', $event)"
|
||||
:column="col"
|
||||
:unstyled="unstyled"
|
||||
:pt="pt"
|
||||
/>
|
||||
</th>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
<template v-else>
|
||||
<tr v-for="(row, i) of getHeaderRows()" :key="i" role="row" v-bind="{ ...ptm('headerRow'), ...getRowPT(row, 'root', i) }">
|
||||
<template v-for="(col, j) of getHeaderColumns(row)" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || j">
|
||||
<DTHeaderCell
|
||||
v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || groupRowsBy !== columnProp(col, 'field')) && typeof col.children !== 'string'"
|
||||
:column="col"
|
||||
@column-click="$emit('column-click', $event)"
|
||||
@column-mousedown="$emit('column-mousedown', $event)"
|
||||
:groupRowsBy="groupRowsBy"
|
||||
:groupRowSortField="groupRowSortField"
|
||||
:sortMode="sortMode"
|
||||
:sortField="sortField"
|
||||
:sortOrder="sortOrder"
|
||||
:multiSortMeta="multiSortMeta"
|
||||
:allRowsSelected="allRowsSelected"
|
||||
:empty="empty"
|
||||
@checkbox-change="$emit('checkbox-change', $event)"
|
||||
:filters="filters"
|
||||
:filterDisplay="filterDisplay"
|
||||
:filtersStore="filtersStore"
|
||||
@filter-change="$emit('filter-change', $event)"
|
||||
@filter-apply="$emit('filter-apply')"
|
||||
@operator-change="$emit('operator-change', $event)"
|
||||
@matchmode-change="$emit('matchmode-change', $event)"
|
||||
@constraint-add="$emit('constraint-add', $event)"
|
||||
@constraint-remove="$emit('constraint-remove', $event)"
|
||||
@apply-click="$emit('apply-click', $event)"
|
||||
:headerCheckboxIconTemplate="headerCheckboxIconTemplate"
|
||||
:pt="pt"
|
||||
/>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
</thead>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseComponent from 'primevue/basecomponent';
|
||||
import { ObjectUtils } from 'primevue/utils';
|
||||
import { mergeProps } from 'vue';
|
||||
import ColumnFilter from './ColumnFilter.vue';
|
||||
import HeaderCell from './HeaderCell.vue';
|
||||
import HeaderCheckbox from './HeaderCheckbox.vue';
|
||||
|
||||
export default {
|
||||
name: 'TableHeader',
|
||||
hostName: 'DataTable',
|
||||
extends: BaseComponent,
|
||||
emits: [
|
||||
'column-click',
|
||||
'column-mousedown',
|
||||
'column-dragstart',
|
||||
'column-dragover',
|
||||
'column-dragleave',
|
||||
'column-drop',
|
||||
'column-resizestart',
|
||||
'checkbox-change',
|
||||
'filter-change',
|
||||
'filter-apply',
|
||||
'operator-change',
|
||||
'matchmode-change',
|
||||
'constraint-add',
|
||||
'constraint-remove',
|
||||
'filter-clear',
|
||||
'apply-click'
|
||||
],
|
||||
props: {
|
||||
columnGroup: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
columns: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
rowGroupMode: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
groupRowsBy: {
|
||||
type: [Array, String, Function],
|
||||
default: null
|
||||
},
|
||||
resizableColumns: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
allRowsSelected: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
empty: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
sortMode: {
|
||||
type: String,
|
||||
default: 'single'
|
||||
},
|
||||
groupRowSortField: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
sortField: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
sortOrder: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
multiSortMeta: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
filterDisplay: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
filters: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
filtersStore: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
reorderableColumns: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
filterInputProps: {
|
||||
type: null,
|
||||
default: null
|
||||
},
|
||||
headerCheckboxIconTemplate: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
columnProp(col, prop) {
|
||||
return ObjectUtils.getVNodeProp(col, prop);
|
||||
},
|
||||
getColumnGroupPT(key) {
|
||||
const columnGroupMetaData = {
|
||||
props: this.getColumnGroupProps(),
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
type: 'header',
|
||||
scrollable: this.$parentInstance?.$parentInstance?.scrollable
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`columnGroup.${key}`, { columnGroup: columnGroupMetaData }), this.ptm(`columnGroup.${key}`, columnGroupMetaData), this.ptmo(this.getColumnGroupProps(), key, columnGroupMetaData));
|
||||
},
|
||||
getColumnGroupProps() {
|
||||
return this.columnGroup && this.columnGroup.props && this.columnGroup.props.pt ? this.columnGroup.props.pt : undefined; //@todo
|
||||
},
|
||||
getRowPT(row, key, index) {
|
||||
const rowMetaData = {
|
||||
props: row.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`row.${key}`, { row: rowMetaData }), this.ptm(`row.${key}`, rowMetaData), this.ptmo(this.getRowProp(row), key, rowMetaData));
|
||||
},
|
||||
getRowProp(row) {
|
||||
return row.props && row.props.pt ? row.props.pt : undefined; //@todo
|
||||
},
|
||||
getColumnPT(column, key, index) {
|
||||
const columnMetaData = {
|
||||
props: column.props,
|
||||
parent: {
|
||||
props: this.$props,
|
||||
state: this.$data
|
||||
},
|
||||
context: {
|
||||
index
|
||||
}
|
||||
};
|
||||
|
||||
return mergeProps(this.ptm(`column.${key}`, { column: columnMetaData }), this.ptm(`column.${key}`, columnMetaData), this.ptmo(this.getColumnProp(column), key, columnMetaData));
|
||||
},
|
||||
getColumnProp(column) {
|
||||
return column.props && column.props.pt ? column.props.pt : undefined; //@todo
|
||||
},
|
||||
getFilterColumnHeaderClass(column) {
|
||||
return [this.cx('headerCell', { column }), this.columnProp(column, 'filterHeaderClass'), this.columnProp(column, 'class')];
|
||||
},
|
||||
getFilterColumnHeaderStyle(column) {
|
||||
return [this.columnProp(column, 'filterHeaderStyle'), this.columnProp(column, 'style')];
|
||||
},
|
||||
getHeaderRows() {
|
||||
let rows = [];
|
||||
|
||||
let columnGroup = this.columnGroup;
|
||||
|
||||
if (columnGroup.children && columnGroup.children.default) {
|
||||
for (let child of columnGroup.children.default()) {
|
||||
if (child.type.name === 'Row') {
|
||||
rows.push(child);
|
||||
} else if (child.children && child.children instanceof Array) {
|
||||
rows = child.children;
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
},
|
||||
getHeaderColumns(row) {
|
||||
let cols = [];
|
||||
|
||||
if (row.children && row.children.default) {
|
||||
row.children.default().forEach((child) => {
|
||||
if (child.children && child.children instanceof Array) cols = [...cols, ...child.children];
|
||||
else if (child.type.name === 'Column') cols.push(child);
|
||||
});
|
||||
|
||||
return cols;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ptmTHeadOptions() {
|
||||
return {
|
||||
context: {
|
||||
scrollable: this.$parentInstance?.$parentInstance?.scrollable
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DTHeaderCell: HeaderCell,
|
||||
DTHeaderCheckbox: HeaderCheckbox,
|
||||
DTColumnFilter: ColumnFilter
|
||||
}
|
||||
};
|
||||
</script>
|
9
components/lib/datatable/package.json
Normal file
9
components/lib/datatable/package.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"main": "./datatable.cjs.js",
|
||||
"module": "./datatable.esm.js",
|
||||
"unpkg": "./datatable.min.js",
|
||||
"types": "./DataTable.d.ts",
|
||||
"browser": {
|
||||
"./sfc": "./DataTable.vue"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue