Initial commit
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
export function useDataTableActions(gridApi, rowData) {
|
||||
const showDetailsDialog = ref(false)
|
||||
const showDeleteDialog = ref(false)
|
||||
const selectedRowData = ref(null)
|
||||
const deleteRowData = ref(null)
|
||||
|
||||
const getRowDataByIndex = (rowIndex) => {
|
||||
if (!gridApi.value) {
|
||||
console.error('Grid API not available')
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const node = gridApi.value.getDisplayedRowAtIndex(rowIndex)
|
||||
if (node && node.data) {
|
||||
return node.data
|
||||
}
|
||||
|
||||
if (rowData.value && rowData.value[rowIndex]) {
|
||||
return rowData.value[rowIndex]
|
||||
}
|
||||
|
||||
console.error('Row data not found for index:', rowIndex)
|
||||
return null
|
||||
} catch (error) {
|
||||
console.error('Error getting row data:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const showDetails = (rowIndex, nodeId) => {
|
||||
console.log('Show details called with:', { rowIndex, nodeId })
|
||||
|
||||
const rowData = getRowDataByIndex(rowIndex)
|
||||
if (rowData) {
|
||||
selectedRowData.value = { ...rowData }
|
||||
showDetailsDialog.value = true
|
||||
console.log('Selected row data:', selectedRowData.value)
|
||||
} else {
|
||||
console.error('Failed to get row data for details')
|
||||
}
|
||||
}
|
||||
|
||||
const editRow = (rowIndex, nodeId) => {
|
||||
console.log('Edit row called with:', { rowIndex, nodeId })
|
||||
|
||||
if (!gridApi.value) {
|
||||
console.error('Grid API not available for editing')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
gridApi.value.startEditingCell({
|
||||
rowIndex: rowIndex,
|
||||
colKey: 'fullName'
|
||||
})
|
||||
console.log('Started inline editing for row:', rowIndex)
|
||||
} catch (error) {
|
||||
console.error('Error starting inline edit:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteRow = (rowIndex, nodeId) => {
|
||||
console.log('Delete row called with:', { rowIndex, nodeId })
|
||||
|
||||
const rowData = getRowDataByIndex(rowIndex)
|
||||
if (rowData) {
|
||||
deleteRowData.value = { ...rowData }
|
||||
showDeleteDialog.value = true
|
||||
console.log('Delete row data:', deleteRowData.value)
|
||||
} else {
|
||||
console.error('Failed to get row data for delete')
|
||||
}
|
||||
}
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (deleteRowData.value && gridApi.value) {
|
||||
try {
|
||||
const indexToDelete = rowData.value.findIndex(item => {
|
||||
return (
|
||||
(item.id && item.id === deleteRowData.value.id) ||
|
||||
(item.email && item.email === deleteRowData.value.email) ||
|
||||
(item.fullName === deleteRowData.value.fullName &&
|
||||
item.salary === deleteRowData.value.salary)
|
||||
)
|
||||
})
|
||||
|
||||
if (indexToDelete !== -1) {
|
||||
rowData.value.splice(indexToDelete, 1)
|
||||
|
||||
gridApi.value.applyTransaction({
|
||||
remove: [deleteRowData.value]
|
||||
})
|
||||
|
||||
console.log('Row deleted successfully')
|
||||
} else {
|
||||
console.error('Row not found for deletion')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting row:', error)
|
||||
}
|
||||
}
|
||||
|
||||
showDeleteDialog.value = false
|
||||
deleteRowData.value = null
|
||||
}
|
||||
|
||||
const cancelDelete = () => {
|
||||
showDeleteDialog.value = false
|
||||
deleteRowData.value = null
|
||||
}
|
||||
|
||||
const saveUser = (editedData) => {
|
||||
if (!editedData || !gridApi.value) return
|
||||
|
||||
try {
|
||||
const indexToUpdate = rowData.value.findIndex(item => {
|
||||
return (
|
||||
(item.id && item.id === editedData.id) ||
|
||||
(item.email && item.email === selectedRowData.value.email) ||
|
||||
(item.fullName === selectedRowData.value.fullName)
|
||||
)
|
||||
})
|
||||
|
||||
if (indexToUpdate !== -1) {
|
||||
rowData.value[indexToUpdate] = { ...editedData }
|
||||
|
||||
const node = gridApi.value.getRowNode(indexToUpdate)
|
||||
if (node) {
|
||||
node.setData(editedData)
|
||||
}
|
||||
|
||||
console.log('User updated successfully:', editedData)
|
||||
} else {
|
||||
console.error('User not found for update')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating user:', error)
|
||||
}
|
||||
|
||||
showDetailsDialog.value = false
|
||||
selectedRowData.value = null
|
||||
}
|
||||
|
||||
const exportToCSV = () => {
|
||||
if (gridApi.value) {
|
||||
gridApi.value.exportDataAsCsv({
|
||||
fileName: `datatable-export-${new Date().toISOString().split('T')[0]}.csv`,
|
||||
columnSeparator: ',',
|
||||
suppressQuotes: false,
|
||||
onlySelected: false,
|
||||
skipGroups: true,
|
||||
skipHeader: false,
|
||||
skipFooters: true,
|
||||
skipPinnedTop: true,
|
||||
skipPinnedBottom: true,
|
||||
allColumns: false,
|
||||
onlySelectedAllPages: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const exportToExcel = () => {
|
||||
if (gridApi.value) {
|
||||
gridApi.value.exportDataAsExcel({
|
||||
fileName: `datatable-export-${new Date().toISOString().split('T')[0]}.xlsx`,
|
||||
sheetName: 'Data Export',
|
||||
onlySelected: false,
|
||||
skipGroups: true,
|
||||
skipHeader: false,
|
||||
skipFooters: true,
|
||||
skipPinnedTop: true,
|
||||
skipPinnedBottom: true,
|
||||
allColumns: false,
|
||||
onlySelectedAllPages: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const setupGlobalHandlers = () => {
|
||||
window.handleShowDetails = showDetails
|
||||
window.handleEditRow = editRow
|
||||
window.handleDeleteRow = deleteRow
|
||||
}
|
||||
|
||||
const cleanupGlobalHandlers = () => {
|
||||
delete window.handleShowDetails
|
||||
delete window.handleEditRow
|
||||
delete window.handleDeleteRow
|
||||
}
|
||||
|
||||
return {
|
||||
showDetailsDialog,
|
||||
showDeleteDialog,
|
||||
selectedRowData,
|
||||
deleteRowData,
|
||||
showDetails,
|
||||
editRow,
|
||||
deleteRow,
|
||||
confirmDelete,
|
||||
cancelDelete,
|
||||
saveUser,
|
||||
exportToCSV,
|
||||
exportToExcel,
|
||||
setupGlobalHandlers,
|
||||
cleanupGlobalHandlers
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,614 @@
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
export function useDataTableGrid(data, isMobile, isTablet) {
|
||||
let gridApi = ref(null);
|
||||
const rowData = ref([]);
|
||||
|
||||
const statusCellRenderer = (params) => {
|
||||
const s = params.value;
|
||||
let label = "Applied";
|
||||
let colorClass = "info";
|
||||
|
||||
if (s === 1) {
|
||||
label = "Current";
|
||||
colorClass = "primary";
|
||||
}
|
||||
if (s === 2) {
|
||||
label = "Professional";
|
||||
colorClass = "success";
|
||||
}
|
||||
if (s === 3) {
|
||||
label = "Rejected";
|
||||
colorClass = "error";
|
||||
}
|
||||
if (s === 4) {
|
||||
label = "Resigned";
|
||||
colorClass = "warning";
|
||||
}
|
||||
|
||||
const chipSize = isMobile.value ? "x-small" : "small";
|
||||
const fontSize = isMobile.value ? "10px" : "12px";
|
||||
|
||||
return `<div class="v-chip v-chip--size-${chipSize} v-chip--variant-flat bg-${colorClass}"
|
||||
style="
|
||||
height:${isMobile.value ? "20px" : "24px"};
|
||||
padding:0 ${isMobile.value ? "6px" : "8px"};
|
||||
display:inline-flex;
|
||||
align-items:center;
|
||||
border-radius:12px;
|
||||
font-size:${fontSize};
|
||||
font-weight:500;
|
||||
color:white;
|
||||
border: inherit;
|
||||
">${label}</div>`;
|
||||
};
|
||||
|
||||
const statusCellEditor = () => {
|
||||
const statusOptions = [
|
||||
{ value: 0, label: "Applied" },
|
||||
{ value: 1, label: "Current" },
|
||||
{ value: 2, label: "Professional" },
|
||||
{ value: 3, label: "Rejected" },
|
||||
{ value: 4, label: "Resigned" },
|
||||
];
|
||||
let currentValue = 0;
|
||||
let selectElement = null;
|
||||
|
||||
const editorObject = {
|
||||
init: (params) => {
|
||||
currentValue = params.value || 0;
|
||||
selectElement = document.createElement("select");
|
||||
selectElement.style.cssText =
|
||||
"width: 100%; height: 100%; border: none; outline: none; font-size: 12px; background: white; color: black; padding: 4px;";
|
||||
|
||||
statusOptions.forEach((option) => {
|
||||
const optionElement = document.createElement("option");
|
||||
optionElement.value = option.value;
|
||||
optionElement.text = option.label;
|
||||
if (option.value == currentValue) {
|
||||
optionElement.selected = true;
|
||||
}
|
||||
selectElement.appendChild(optionElement);
|
||||
});
|
||||
|
||||
selectElement.addEventListener("change", (e) => {
|
||||
currentValue = parseInt(e.target.value);
|
||||
});
|
||||
|
||||
selectElement.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" || e.key === "Tab") {
|
||||
e.preventDefault();
|
||||
currentValue = parseInt(selectElement.value);
|
||||
params.stopEditing();
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
params.stopEditing(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getGui: () => selectElement,
|
||||
|
||||
getValue: () => parseInt(currentValue),
|
||||
|
||||
afterGuiAttached: () => {
|
||||
selectElement.focus();
|
||||
selectElement.click();
|
||||
},
|
||||
|
||||
isPopup: () => false,
|
||||
};
|
||||
return editorObject;
|
||||
};
|
||||
|
||||
const actionButtonsRenderer = (params) => {
|
||||
const iconSize = isMobile.value ? "14px" : "16px";
|
||||
const buttonSize = isMobile.value ? "24px" : "28px";
|
||||
const gap = isMobile.value ? "3px" : "4px";
|
||||
|
||||
const rowIndex = params.node.rowIndex;
|
||||
const nodeId = params.node.id;
|
||||
|
||||
return `
|
||||
<div style="display: flex; gap: ${gap}; align-items: center; justify-content: center; padding: 2px 0;">
|
||||
<button
|
||||
class="action-btn details-btn"
|
||||
style="
|
||||
background: rgba(var(--v-theme-primary), 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(var(--v-theme-primary), 0.3);
|
||||
color: rgb(var(--v-theme-primary));
|
||||
border-radius: 12px;
|
||||
width: ${buttonSize};
|
||||
height: ${buttonSize};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 8px 32px rgba(var(--v-theme-primary), 0.12);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
"
|
||||
onmouseover="
|
||||
this.style.transform='translateY(-2px) scale(1.05)';
|
||||
this.style.background='rgba(var(--v-theme-primary), 0.25)';
|
||||
this.style.borderColor='rgba(var(--v-theme-primary), 0.5)';
|
||||
this.style.boxShadow='0 12px 40px rgba(var(--v-theme-primary), 0.2)';
|
||||
"
|
||||
onmouseout="
|
||||
this.style.transform='translateY(0) scale(1)';
|
||||
this.style.background='rgba(var(--v-theme-primary), 0.15)';
|
||||
this.style.borderColor='rgba(var(--v-theme-primary), 0.3)';
|
||||
this.style.boxShadow='0 8px 32px rgba(var(--v-theme-primary), 0.12)';
|
||||
"
|
||||
onclick="handleShowDetails(${rowIndex}, '${nodeId}')"
|
||||
title="View Details"
|
||||
>
|
||||
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="action-btn edit-btn"
|
||||
style="
|
||||
background: rgba(var(--v-theme-warning), 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(var(--v-theme-warning), 0.3);
|
||||
color: rgb(var(--v-theme-warning));
|
||||
border-radius: 12px;
|
||||
width: ${buttonSize};
|
||||
height: ${buttonSize};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 8px 32px rgba(var(--v-theme-warning), 0.12);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
"
|
||||
onmouseover="
|
||||
this.style.transform='translateY(-2px) scale(1.05)';
|
||||
this.style.background='rgba(var(--v-theme-warning), 0.25)';
|
||||
this.style.borderColor='rgba(var(--v-theme-warning), 0.5)';
|
||||
this.style.boxShadow='0 12px 40px rgba(var(--v-theme-warning), 0.2)';
|
||||
"
|
||||
onmouseout="
|
||||
this.style.transform='translateY(0) scale(1)';
|
||||
this.style.background='rgba(var(--v-theme-warning), 0.15)';
|
||||
this.style.borderColor='rgba(var(--v-theme-warning), 0.3)';
|
||||
this.style.boxShadow='0 8px 32px rgba(var(--v-theme-warning), 0.12)';
|
||||
"
|
||||
onclick="handleEditRow(${rowIndex}, '${nodeId}')"
|
||||
title="Edit Row"
|
||||
>
|
||||
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
||||
<path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="action-btn delete-btn"
|
||||
style="
|
||||
background: rgba(var(--v-theme-error), 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(var(--v-theme-error), 0.3);
|
||||
color: rgb(var(--v-theme-error));
|
||||
border-radius: 12px;
|
||||
width: ${buttonSize};
|
||||
height: ${buttonSize};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 8px 32px rgba(var(--v-theme-error), 0.12);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
"
|
||||
onmouseover="
|
||||
this.style.transform='translateY(-2px) scale(1.05)';
|
||||
this.style.background='rgba(var(--v-theme-error), 0.25)';
|
||||
this.style.borderColor='rgba(var(--v-theme-error), 0.5)';
|
||||
this.style.boxShadow='0 12px 40px rgba(var(--v-theme-error), 0.2)';
|
||||
"
|
||||
onmouseout="
|
||||
this.style.transform='translateY(0) scale(1)';
|
||||
this.style.background='rgba(var(--v-theme-error), 0.15)';
|
||||
this.style.borderColor='rgba(var(--v-theme-error), 0.3)';
|
||||
this.style.boxShadow='0 8px 32px rgba(var(--v-theme-error), 0.12)';
|
||||
"
|
||||
onclick="handleDeleteRow(${rowIndex}, '${nodeId}')"
|
||||
title="Delete Row"
|
||||
>
|
||||
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6m3 0V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
const columnDefs = ref([
|
||||
{
|
||||
headerName: "NAME",
|
||||
field: "fullName",
|
||||
sortable: true,
|
||||
filter: true,
|
||||
flex: 1,
|
||||
minWidth: 150,
|
||||
hide: false,
|
||||
editable: true,
|
||||
cellEditor: "agTextCellEditor",
|
||||
cellEditorParams: {
|
||||
maxLength: 50,
|
||||
},
|
||||
onCellValueChanged: (params) => {
|
||||
console.log("Name changed:", params.newValue);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: "EMAIL",
|
||||
field: "email",
|
||||
sortable: true,
|
||||
filter: true,
|
||||
flex: 1.5,
|
||||
minWidth: 200,
|
||||
hide: false,
|
||||
editable: true,
|
||||
cellEditor: "agTextCellEditor",
|
||||
cellEditorParams: {
|
||||
maxLength: 100,
|
||||
},
|
||||
onCellValueChanged: (params) => {
|
||||
console.log("Email changed:", params.newValue);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: "DATE",
|
||||
field: "startDate",
|
||||
sortable: true,
|
||||
filter: true,
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
hide: false,
|
||||
editable: true,
|
||||
cellEditor: "agDateCellEditor",
|
||||
cellEditorParams: {
|
||||
preventEdit: (params) => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
valueFormatter: (params) => {
|
||||
if (!params.value) return "";
|
||||
|
||||
const date = new Date(params.value);
|
||||
if (isNaN(date.getTime())) return "";
|
||||
|
||||
const day = date.getDate().toString().padStart(2, "0");
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||
const year = date.getFullYear();
|
||||
|
||||
return `${day}/${month}/${year}`;
|
||||
},
|
||||
valueParser: (params) => {
|
||||
if (!params.newValue) return null;
|
||||
|
||||
const dateStr = params.newValue;
|
||||
if (typeof dateStr === "string" && dateStr.includes("/")) {
|
||||
const parts = dateStr.split("/");
|
||||
if (parts.length === 3) {
|
||||
const day = parseInt(parts[0]);
|
||||
const month = parseInt(parts[1]) - 1;
|
||||
const year = parseInt(parts[2]);
|
||||
|
||||
const date = new Date(year, month, day);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Date(dateStr);
|
||||
},
|
||||
filterParams: {
|
||||
buttons: ["reset"],
|
||||
closeOnApply: false,
|
||||
suppressAndOrCondition: true,
|
||||
comparator: (filterLocalDateAtMidnight, cellValue) => {
|
||||
if (!cellValue) return -1;
|
||||
|
||||
const cellDate = new Date(cellValue);
|
||||
cellDate.setHours(0, 0, 0, 0);
|
||||
|
||||
if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return filterLocalDateAtMidnight < cellDate ? -1 : 1;
|
||||
},
|
||||
},
|
||||
onCellValueChanged: (params) => {
|
||||
if (
|
||||
params.newValue === "" ||
|
||||
params.newValue === null ||
|
||||
params.newValue === undefined
|
||||
) {
|
||||
params.node.setDataValue(params.colDef.field, params.oldValue);
|
||||
}
|
||||
console.log("Date changed:", params.newValue);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: "SALARY",
|
||||
field: "salary",
|
||||
sortable: true,
|
||||
filter: true,
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
hide: false,
|
||||
editable: true,
|
||||
cellEditor: "agNumberCellEditor",
|
||||
cellEditorParams: {
|
||||
precision: 0,
|
||||
},
|
||||
filterParams: {
|
||||
buttons: ["reset"],
|
||||
closeOnApply: false,
|
||||
suppressAndOrCondition: true,
|
||||
inRangeInclusive: true,
|
||||
debounceMs: 200,
|
||||
},
|
||||
valueFormatter: (params) => {
|
||||
if (params.value) {
|
||||
const intValue = Math.floor(Number(params.value));
|
||||
return `$${intValue.toLocaleString()}`;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
onCellValueChanged: (params) => {
|
||||
if (
|
||||
params.newValue !== null &&
|
||||
params.newValue !== undefined &&
|
||||
params.newValue !== ""
|
||||
) {
|
||||
const numValue = parseInt(params.newValue);
|
||||
if (!isNaN(numValue)) {
|
||||
params.node.setDataValue(params.colDef.field, numValue);
|
||||
} else {
|
||||
params.node.setDataValue(params.colDef.field, params.oldValue);
|
||||
}
|
||||
} else {
|
||||
params.node.setDataValue(params.colDef.field, params.oldValue);
|
||||
}
|
||||
console.log("Salary changed:", params.newValue);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: "AGE",
|
||||
field: "age",
|
||||
sortable: true,
|
||||
filter: true,
|
||||
flex: 0.7,
|
||||
minWidth: 80,
|
||||
hide: false,
|
||||
editable: true,
|
||||
cellEditor: "agNumberCellEditor",
|
||||
cellEditorParams: {
|
||||
precision: 0,
|
||||
showStepperButtons: false,
|
||||
},
|
||||
|
||||
valueFormatter: (params) => {
|
||||
if (
|
||||
params.value === null ||
|
||||
params.value === undefined ||
|
||||
params.value === ""
|
||||
) {
|
||||
return "";
|
||||
}
|
||||
const numValue = Number(params.value);
|
||||
return isNaN(numValue) ? "" : Math.floor(numValue).toString();
|
||||
},
|
||||
|
||||
onCellValueChanged: (params) => {
|
||||
const newValue = params.newValue;
|
||||
|
||||
if (newValue === null || newValue === undefined || newValue === "") {
|
||||
params.node.setDataValue(params.colDef.field, params.oldValue);
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanValue = String(newValue).trim();
|
||||
if (cleanValue === "") {
|
||||
params.node.setDataValue(params.colDef.field, params.oldValue);
|
||||
return;
|
||||
}
|
||||
|
||||
const parsed = parseInt(cleanValue, 10);
|
||||
if (isNaN(parsed)) {
|
||||
console.warn("Age: Invalid number format:", cleanValue);
|
||||
params.node.setDataValue(params.colDef.field, params.oldValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed < 1 || parsed > 120) {
|
||||
console.warn("Age: Out of range (1-120):", parsed);
|
||||
params.node.setDataValue(params.colDef.field, params.oldValue);
|
||||
return;
|
||||
}
|
||||
|
||||
params.node.setDataValue(params.colDef.field, parsed);
|
||||
console.log("Age changed from", params.oldValue, "to", parsed);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: "STATUS",
|
||||
field: "status",
|
||||
sortable: true,
|
||||
filter: true,
|
||||
flex: 1,
|
||||
minWidth: 100,
|
||||
hide: false,
|
||||
editable: true,
|
||||
cellRenderer: statusCellRenderer,
|
||||
cellEditor: statusCellEditor,
|
||||
onCellValueChanged: (params) => {
|
||||
console.log("Status changed:", params.newValue);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: "ACTION",
|
||||
field: "action",
|
||||
sortable: false,
|
||||
filter: false,
|
||||
flex: 1,
|
||||
minWidth: 100,
|
||||
hide: false,
|
||||
editable: false,
|
||||
cellRenderer: actionButtonsRenderer,
|
||||
suppressMenu: true,
|
||||
suppressSorting: true,
|
||||
suppressFilter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const defaultColDef = computed(() => ({
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filter: true,
|
||||
floatingFilter: false,
|
||||
cellStyle: { display: "flex", alignItems: "center" },
|
||||
flex: 1,
|
||||
minWidth: isMobile.value ? 80 : 100,
|
||||
maxWidth: 400,
|
||||
wrapText: false,
|
||||
autoHeight: false,
|
||||
suppressMenu: false,
|
||||
}));
|
||||
|
||||
const gridOptions = computed(() => ({
|
||||
theme: "legacy",
|
||||
headerHeight: isMobile.value ? 48 : 56,
|
||||
rowHeight: isMobile.value ? 44 : 52,
|
||||
animateRows: true,
|
||||
rowSelection: { type: "multiRow" },
|
||||
pagination: true,
|
||||
paginationPageSize: isMobile.value ? 5 : 10,
|
||||
paginationPageSizeSelector: isMobile.value ? [5, 10, 20] : [5, 10, 20, 50],
|
||||
suppressRowClickSelection: false,
|
||||
rowMultiSelectWithClick: true,
|
||||
enableCellTextSelection: true,
|
||||
suppressHorizontalScroll: false,
|
||||
alwaysShowHorizontalScroll: false,
|
||||
suppressColumnVirtualisation: false,
|
||||
autoGroupColumnDef: {
|
||||
minWidth: 200,
|
||||
maxWidth: 400,
|
||||
flex: 1,
|
||||
},
|
||||
editType: "fullRow",
|
||||
stopEditingWhenCellsLoseFocus: true,
|
||||
enterNavigatesVertically: true,
|
||||
enterNavigatesVerticallyAfterEdit: true,
|
||||
singleClickEdit: false,
|
||||
suppressClickEdit: false,
|
||||
includeHiddenColumnsInQuickFilter: false,
|
||||
cacheQuickFilter: true,
|
||||
enableAdvancedFilter: false,
|
||||
includeHiddenColumnsInAdvancedFilter: false,
|
||||
suppressBrowserResizeObserver: false,
|
||||
maintainColumnOrder: true,
|
||||
suppressMenuHide: true,
|
||||
enableRangeSelection: true,
|
||||
enableFillHandle: false,
|
||||
enableRangeHandle: false,
|
||||
}));
|
||||
|
||||
const updateColumnVisibility = () => {
|
||||
if (!gridApi.value) return;
|
||||
|
||||
if (isMobile.value) {
|
||||
gridApi.value.setColumnVisible("email", false);
|
||||
gridApi.value.setColumnVisible("startDate", false);
|
||||
gridApi.value.setColumnVisible("age", false);
|
||||
} else if (isTablet.value) {
|
||||
gridApi.value.setColumnVisible("email", true);
|
||||
gridApi.value.setColumnVisible("startDate", false);
|
||||
gridApi.value.setColumnVisible("age", true);
|
||||
} else {
|
||||
gridApi.value.setColumnVisible("email", true);
|
||||
gridApi.value.setColumnVisible("startDate", true);
|
||||
gridApi.value.setColumnVisible("age", true);
|
||||
}
|
||||
};
|
||||
|
||||
const updatePagination = () => {
|
||||
if (!gridApi.value) return;
|
||||
|
||||
const pageSize = isMobile.value ? 5 : 10;
|
||||
const pageSizeSelector = isMobile.value ? [5, 10, 20] : [5, 10, 20, 50];
|
||||
|
||||
gridApi.value.paginationSetPageSize(pageSize);
|
||||
gridApi.value.setGridOption("paginationPageSizeSelector", pageSizeSelector);
|
||||
};
|
||||
|
||||
function onGridReady(params) {
|
||||
gridApi.value = params.api;
|
||||
rowData.value = data.map((item) => ({
|
||||
...item,
|
||||
salary:
|
||||
typeof item.salary === "string" ? parseInt(item.salary) : item.salary,
|
||||
age: typeof item.age === "string" ? parseInt(item.age) : item.age,
|
||||
}));
|
||||
|
||||
updateColumnVisibility();
|
||||
updatePagination();
|
||||
setTimeout(() => {
|
||||
gridApi.value.sizeColumnsToFit();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function onCellValueChanged(params) {
|
||||
console.log("Cell value changed:", {
|
||||
field: params.colDef.field,
|
||||
oldValue: params.oldValue,
|
||||
newValue: params.newValue,
|
||||
data: params.data,
|
||||
});
|
||||
|
||||
const dataIndex = rowData.value.findIndex((item) => {
|
||||
return (
|
||||
(item.id && item.id === params.data.id) ||
|
||||
(item.email && item.email === params.data.email) ||
|
||||
item.fullName === params.data.fullName
|
||||
);
|
||||
});
|
||||
|
||||
if (dataIndex !== -1) {
|
||||
rowData.value[dataIndex] = { ...params.data };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
gridApi,
|
||||
columnDefs,
|
||||
rowData,
|
||||
defaultColDef,
|
||||
gridOptions,
|
||||
onGridReady,
|
||||
onCellValueChanged,
|
||||
updateColumnVisibility,
|
||||
updatePagination,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
export function useResponsive() {
|
||||
const windowWidth = ref(window.innerWidth)
|
||||
const isMobile = ref(window.innerWidth < 768)
|
||||
const isTablet = ref(window.innerWidth >= 768 && window.innerWidth < 1024)
|
||||
|
||||
const handleResize = () => {
|
||||
windowWidth.value = window.innerWidth
|
||||
isMobile.value = window.innerWidth < 768
|
||||
isTablet.value = window.innerWidth >= 768 && window.innerWidth < 1024
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
return {
|
||||
windowWidth,
|
||||
isMobile,
|
||||
isTablet
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user