Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/showcase/doc/common/apidoc/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -21050,6 +21050,14 @@
"default": "",
"description": "Specifies the selection mode."
},
{
"name": "selectable",
"optional": true,
"readonly": false,
"type": "Function",
"default": "",
"description": "if selectionMode is not null, this function is called to determine if a row is selectable."
},
{
"name": "compareSelectionBy",
"optional": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<DeferredDemo @load="loadDemoData">
<div class="card">
<DataTable v-model:selection="selectedProducts" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
<Column selectionMode="multiple" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -29,7 +29,7 @@ export default {
code: {
basic: `
<DataTable v-model:selection="selectedProducts" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
<Column selectionMode="multiple" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -40,7 +40,7 @@ export default {
<template>
<div class="card">
<DataTable v-model:selection="selectedProducts" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
<Column selectionMode="multiple" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -62,6 +62,11 @@ export default {
},
mounted() {
ProductService.getProductsMini().then((data) => (this.products = data));
},
methods: {
selectable(rowData, rowIndex) {
return rowData.id !== '1002';
}
}
};
<\/script>
Expand All @@ -70,7 +75,7 @@ export default {
<template>
<div class="card">
<DataTable v-model:selection="selectedProducts" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="multiple" headerStyle="width: 3rem"></Column>
<Column selectionMode="multiple" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -91,6 +96,10 @@ const products = ref();
const selectedProducts = ref();
const metaKey = ref(true);

const selectable = (rowData, rowIndex) => {
return rowData.id !== '1002';
}

<\/script>
`,
data: `
Expand All @@ -114,6 +123,9 @@ const metaKey = ref(true);
methods: {
loadDemoData() {
ProductService.getProductsMini().then((data) => (this.products = data));
},
selectable(rowData, rowIndex) {
return rowData.id !== '1002';
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<DeferredDemo @load="loadDemoData">
<div class="card">
<DataTable v-model:selection="selectedProduct" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="single" headerStyle="width: 3rem"></Column>
<Column selectionMode="single" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -30,7 +30,7 @@ export default {
code: {
basic: `
<DataTable v-model:selection="selectedProduct" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="single" headerStyle="width: 3rem"></Column>
<Column selectionMode="single" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -41,7 +41,7 @@ export default {
<template>
<div class="card">
<DataTable v-model:selection="selectedProduct" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="single" headerStyle="width: 3rem"></Column>
<Column selectionMode="single" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -62,6 +62,11 @@ export default {
},
mounted() {
ProductService.getProductsMini().then((data) => (this.products = data));
},
methods: {
selectable(rowData, rowIndex) {
return rowIndex !== 0;
}
}
};
<\/script>
Expand All @@ -70,7 +75,7 @@ export default {
<template>
<div class="card">
<DataTable v-model:selection="selectedProduct" :value="products" dataKey="id" tableStyle="min-width: 50rem">
<Column selectionMode="single" headerStyle="width: 3rem"></Column>
<Column selectionMode="single" headerStyle="width: 3rem" :selectable="selectable"></Column>
<Column field="code" header="Code"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
Expand All @@ -90,6 +95,10 @@ onMounted(() => {
const products = ref();
const selectedProduct = ref();

const selectable = (rowData, rowIndex) => {
return rowIndex !== 0;
}

<\/script>
`,
data: `
Expand All @@ -113,6 +122,9 @@ const selectedProduct = ref();
methods: {
loadDemoData() {
ProductService.getProductsMini().then((data) => (this.products = data));
},
selectable(rowData, rowIndex) {
return rowIndex !== 0;
}
}
};
Expand Down
4 changes: 4 additions & 0 deletions packages/primevue/src/column/BaseColumn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ export default {
type: String,
default: null
},
selectable: {
type: Function,
default: null
},
expander: {
type: Boolean,
default: false
Expand Down
8 changes: 6 additions & 2 deletions packages/primevue/src/datatable/BodyCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
/>
<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" />
<DTRadioButton v-if="columnProp('selectionMode') === 'single'" :value="rowData" :name="name" :checked="selected" @change="toggleRowWithRadio($event, rowIndex)" :column="column" :disabled="isSelectionModeDisabled" :index="index" :unstyled="unstyled" :pt="pt" />
<DTCheckbox
v-else-if="columnProp('selectionMode') === 'multiple'"
:value="rowData"
Expand All @@ -50,6 +50,7 @@
:aria-selected="selected ? true : undefined"
@change="toggleRowWithCheckbox($event, rowIndex)"
:column="column"
:disabled="isSelectionModeDisabled"
:index="index"
:unstyled="unstyled"
:pt="pt"
Expand Down Expand Up @@ -134,7 +135,7 @@

<script>
import { getAttribute, getFirstFocusableElement, getNextElementSibling, getOuterWidth, getPreviousElementSibling, invokeElementMethod } from '@primeuix/utils/dom';
import { resolveFieldData } from '@primeuix/utils/object';
import { isEmpty, resolveFieldData } from '@primeuix/utils/object';
import BaseComponent from '@primevue/core/basecomponent';
import { getVNodeProp } from '@primevue/core/utils';
import BarsIcon from '@primevue/icons/bars';
Expand Down Expand Up @@ -575,6 +576,9 @@ export default {
},
cancelButtonAriaLabel() {
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.cancelEdit : undefined;
},
isSelectionModeDisabled() {
return isEmpty(this.column.props?.selectable) ? false : !this.column.props?.selectable(this.rowData, this.rowIndex);
}
},
components: {
Expand Down
5 changes: 5 additions & 0 deletions packages/primevue/src/datatable/DataTable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,11 @@ export interface DataTableProps<T = any> {
* Specifies the selection mode.
*/
selectionMode?: HintedString<'single' | 'multiple'> | undefined;
/**
* if selectionMode is not null, this function is called to determine if a row is selectable.
* @defaultValue null
*/
selectable?: (rowData: NoInfer<T>, rowIndex: number) => boolean | undefined;
/**
* Algorithm to define if a row is selected.
* @defaultValue deepEquals
Expand Down
21 changes: 16 additions & 5 deletions packages/primevue/src/datatable/DataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1095,21 +1095,31 @@ export default {
this.$emit('row-select', { originalEvent: event.originalEvent, data: rowData, index: event.index, type: 'checkbox' });
}
},
getSelectableColumn() {
return this.columns.find((column) => ['multiple', 'single'].includes(column.props?.selectionMode));
},
toggleRowsWithCheckbox(event) {
if (this.selectAll !== null) {
this.$emit('select-all-change', event);
} else {
const { originalEvent, checked } = event;
const isSelectable = this.getSelectableColumn()?.props?.selectable;
let _selection = [];

let returnSelection = [];
if (checked) {
_selection = this.frozenValue ? [...this.frozenValue, ...this.processedData] : this.processedData;
this.$emit('row-select-all', { originalEvent, data: _selection });
returnSelection = Object.keys(_selection).map((key) => {
const currentRow = _selection[key]
if (isSelectable?.(currentRow, key) || isEmpty(isSelectable)){
return currentRow
}
}).filter(Boolean);
this.$emit('row-select-all', { originalEvent, data: returnSelection });
} else {
this.$emit('row-unselect-all', { originalEvent });
}

this.$emit('update:selection', _selection);
this.$emit('update:selection', returnSelection);
}
},
isSingleSelectionMode() {
Expand Down Expand Up @@ -2115,8 +2125,9 @@ export default {
return this.selectAll;
} else {
const val = this.frozenValue ? [...this.frozenValue, ...this.processedData] : this.processedData;

return isNotEmpty(val) && this.selection && Array.isArray(this.selection) && val.every((v) => this.selection.some((s) => this.equals(s, v)));
const isSelectable = this.getSelectableColumn()?.props?.selectable;
const isSelectVals = val.filter((v, index) => isSelectable?.(v, index) || isEmpty(isSelectable));
return isNotEmpty(isSelectVals) && this.selection && Array.isArray(this.selection) && isSelectVals.every((v) => this.selection.some((s) => this.equals(s, v)));
}
},
groupRowSortField() {
Expand Down
10 changes: 7 additions & 3 deletions packages/primevue/src/datatable/RowCheckbox.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<Checkbox :modelValue="checked" :binary="true" :disabled="$attrs.disabled" :aria-label="checkboxAriaLabel" @change="onChange" :unstyled="unstyled" :pt="getColumnPT('pcRowCheckbox')">
<Checkbox :modelValue="checked" :binary="true" :disabled="disabled" :aria-label="checkboxAriaLabel" @change="onChange" :unstyled="unstyled" :pt="getColumnPT('pcRowCheckbox')">
<template #icon="slotProps">
<component v-if="rowCheckboxIconTemplate" :is="rowCheckboxIconTemplate" :checked="slotProps.checked" :class="slotProps.class" />
<CheckIcon v-else-if="!rowCheckboxIconTemplate && slotProps.checked" :class="slotProps.class" v-bind="getColumnPT('pcRowCheckbox.icon')" />
Expand Down Expand Up @@ -29,6 +29,10 @@ export default {
index: {
type: Number,
default: null
},
disabled: {
type: Boolean,
default: false
}
},
methods: {
Expand All @@ -43,7 +47,7 @@ export default {
context: {
index: this.index,
checked: this.checked,
disabled: this.$attrs.disabled
disabled: this.disabled
}
};

Expand All @@ -53,7 +57,7 @@ export default {
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined; //@todo:
},
onChange(event) {
if (!this.$attrs.disabled) {
if (!this.disabled) {
this.$emit('change', {
originalEvent: event,
data: this.value
Expand Down
10 changes: 7 additions & 3 deletions packages/primevue/src/datatable/RowRadioButton.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<RadioButton :modelValue="checked" :binary="true" :disabled="$attrs.disabled" :name="name" @change="onChange" :unstyled="unstyled" :pt="getColumnPT('pcRowRadiobutton')" />
<RadioButton :modelValue="checked" :binary="true" :disabled="disabled" :name="name" @change="onChange" :unstyled="unstyled" :pt="getColumnPT('pcRowRadiobutton')" />
</template>

<script>
Expand All @@ -20,6 +20,10 @@ export default {
index: {
type: Number,
default: null
},
disabled: {
type: Boolean,
default: false
}
},
methods: {
Expand All @@ -34,7 +38,7 @@ export default {
context: {
index: this.index,
checked: this.checked,
disabled: this.$attrs.disabled
disabled: this.disabled
}
};

Expand All @@ -44,7 +48,7 @@ export default {
return this.column.props && this.column.props.pt ? this.column.props.pt : undefined; //@todo:
},
onChange(event) {
if (!this.$attrs.disabled) {
if (!this.disabled) {
this.$emit('change', {
originalEvent: event,
data: this.value
Expand Down
Loading