feat(editor): Use n8n date time picker in data tables (no-changelog) (#19060)

This commit is contained in:
Svetoslav Dekov
2025-09-01 17:24:00 +03:00
committed by GitHub
parent 81c26676a3
commit caeaa679c6
3 changed files with 123 additions and 14 deletions

View File

@@ -689,6 +689,7 @@
/* Ag Grid */
--grid-row-selected-background: var(--p-color-secondary-070);
--grid-cell-editing-border: 2px solid var(--color-secondary);
}
:root {

View File

@@ -24,8 +24,8 @@ import type {
CellEditingStartedEvent,
CellEditingStoppedEvent,
CellKeyDownEvent,
ValueFormatterParams,
SortDirection,
SortChangedEvent,
} from 'ag-grid-community';
import {
ModuleRegistry,
@@ -67,11 +67,10 @@ import AddColumnButton from '@/features/dataStore/components/dataGrid/AddColumnB
import AddRowButton from '@/features/dataStore/components/dataGrid/AddRowButton.vue';
import { isDataStoreValue } from '@/features/dataStore/typeGuards';
import NullEmptyCellRenderer from '@/features/dataStore/components/dataGrid/NullEmptyCellRenderer.vue';
import ElDatePickerCellEditor from '@/features/dataStore/components/dataGrid/ElDatePickerCellEditor.vue';
import { onClickOutside } from '@vueuse/core';
import type { SortChangedEvent } from 'ag-grid-community';
import { useClipboard } from '@/composables/useClipboard';
import { reorderItem } from '@/features/dataStore/utils';
import { convertToDisplayDate } from '@/utils/formatters/dateFormatter';
// Register only the modules we actually use
ModuleRegistry.registerModules([
@@ -359,8 +358,14 @@ const createColumnDef = (col: DataStoreColumn, extraProps: Partial<ColDef> = {})
}
// Setup date editor
if (col.type === 'date') {
columnDef.cellEditor = 'agDateCellEditor';
columnDef.cellEditorPopup = false;
columnDef.cellEditorSelector = () => ({
component: ElDatePickerCellEditor,
});
columnDef.valueFormatter = (params) => {
const value = params.value as Date | null | undefined;
if (value === null || value === undefined) return '';
return value.toISOString();
};
}
return {
...columnDef,
@@ -433,11 +438,6 @@ const initColumnDefinitions = () => {
headerComponentParams: {
allowMenuActions: false,
},
valueFormatter: (params: ValueFormatterParams<DataStoreRow>) => {
if (!params.value) return '';
const { date, time } = convertToDisplayDate(params.value as Date | string | number);
return `${date}, ${time}`;
},
};
colDefs.value = [
// Always add the ID column, it's not returned by the back-end but all data stores have it
@@ -876,8 +876,6 @@ defineExpose({
--ag-input-background-color: var(--color-text-xlight);
--ag-focus-shadow: none;
--cell-editing-border: 2px solid var(--color-secondary);
:global(.ag-cell) {
display: flex;
align-items: center;
@@ -949,7 +947,7 @@ defineExpose({
padding-top: var(--spacing-xs);
&:where(:focus-within, :active) {
border: var(--cell-editing-border);
border: var(--grid-cell-editing-border);
}
}
}
@@ -968,7 +966,7 @@ defineExpose({
box-shadow: none;
&:global(.boolean-cell) {
border: var(--cell-editing-border) !important;
border: var(--grid-cell-editing-border) !important;
&:global(.ag-cell-focus) {
background-color: var(--grid-cell-active-background);

View File

@@ -0,0 +1,110 @@
<script setup lang="ts">
import { onMounted, ref, nextTick } from 'vue';
import type { ICellEditorParams } from 'ag-grid-community';
import { DateTime } from 'luxon';
const props = defineProps<{
params: ICellEditorParams;
}>();
const pickerRef = ref<HTMLElement | null>(null);
const dateValue = ref<Date | null>(null);
const initialValue = ref<Date | null>(null);
const inputWidth = ref(props.params.column.getActualWidth() - 4); // -4 for the border
onMounted(async () => {
const initial = props.params.value as unknown;
if (initial === null || initial === undefined) {
dateValue.value = null;
} else if (initial instanceof Date) {
const dt = DateTime.fromJSDate(initial);
// the date editor shows local time but we want the UTC so we need to offset the value here
dateValue.value = dt.minus({ minutes: dt.offset }).toJSDate();
}
initialValue.value = dateValue.value;
await nextTick();
try {
// Focus to open the calendar popover
// Element Plus exposes a focus() method on the picker instance
// Using any to avoid tying to internal types
(pickerRef.value as unknown as { focus?: () => void })?.focus?.();
} catch {}
});
function onChange() {
props.params.stopEditing();
}
function onClear() {
dateValue.value = null;
props.params.stopEditing();
}
function onKeydown(e: KeyboardEvent) {
if (e.key === 'Escape') {
e.stopPropagation();
dateValue.value = initialValue.value;
props.params.stopEditing();
} else if (e.key === 'Enter') {
e.stopPropagation();
props.params.stopEditing();
}
}
defineExpose({
getValue: () => {
if (dateValue.value === null) return null;
const dt = DateTime.fromJSDate(dateValue.value);
// the editor returns local time but we initially offset the value to UTC so we need to add the offset back here
return dt.plus({ minutes: dt.offset }).toJSDate();
},
isPopup: () => true,
});
</script>
<template>
<div class="datastore-datepicker-wrapper">
<el-date-picker
ref="pickerRef"
v-model="dateValue"
type="datetime"
:style="{ width: `${inputWidth}px` }"
:clearable="true"
:editable="false"
:teleported="false"
:placeholder="''"
@change="onChange"
@clear="onClear"
@keydown="onKeydown"
/>
</div>
</template>
<style lang="scss">
.datastore-datepicker-wrapper {
border-radius: var(--border-radius-base);
.el-input__prefix {
display: none;
}
.el-input__suffix {
display: flex;
flex-direction: column;
justify-content: center;
color: var(--color-foreground-dark);
}
.el-input__inner {
border: none !important;
padding-left: var(--ag-input-padding-start);
}
&:where(:focus-within, :active) {
box-shadow: none;
border: var(--grid-cell-editing-border);
}
}
</style>