fix #4442: adapt DateTimeField to be more declarative

Reworks the current implementation of the DateTimeField to be more
declarative by using a ViewModel and data bindings as well as formulas,
in order to reduce code and unwanted complexity.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
Christian Ebner 2023-08-09 12:55:26 +02:00 committed by Thomas Lamprecht
parent 8fc1d232ea
commit 0e3cb037d6

View File

@ -6,205 +6,166 @@ Ext.define('Proxmox.DateTimeField', {
layout: 'hbox', layout: 'hbox',
referenceHolder: true, viewModel: {
data: {
datetime: null,
minDatetime: null,
maxDatetime: null,
},
formulas: {
date: {
get: function(get) {
return get('datetime');
},
set: function(date) {
if (!date) {
this.set('datetime', null);
return;
}
let datetime = new Date(this.get('datetime'));
datetime.setDate(date.getDate());
datetime.setMonth(date.getMonth());
datetime.setFullYear(date.getFullYear());
this.set('datetime', datetime);
},
},
time: {
get: function(get) {
return get('datetime');
},
set: function(time) {
if (!time) {
this.set('datetime', null);
return;
}
let datetime = new Date(this.get('datetime'));
datetime.setHours(time.getHours());
datetime.setMinutes(time.getMinutes());
datetime.setSeconds(time.getSeconds());
datetime.setMilliseconds(time.getMilliseconds());
this.set('datetime', datetime);
},
},
minDate: {
get: function(get) {
let datetime = get('minDatetime');
return datetime ? new Date(datetime) : null;
},
},
maxDate: {
get: function(get) {
let datetime = get('maxDatetime');
return datetime ? new Date(datetime) : null;
},
},
minTime: {
get: function(get) {
let current = get('datetime');
let min = get('minDatetime');
if (min && current && !this.isSameDay(current, min)) {
return new Date(min).setHours('00', '00', '00', '000');
}
return min;
},
},
maxTime: {
get: function(get) {
let current = get('datetime');
let max = get('maxDatetime');
if (max && current && !this.isSameDay(current, max)) {
return new Date(max).setHours('23', '59', '59', '999');
}
return max;
},
},
},
// Helper function to check if dates are the same day of the year
isSameDay: function(date1, date2) {
return date1.getDate() === date2.getDate() &&
date1.getMonth() === date2.getMonth() &&
date1.getFullYear() === date2.getFullYear();
},
},
config: { config: {
value: null,
submitFormat: 'U', submitFormat: 'U',
disabled: false, disabled: false,
}, },
setValue: function(value) { setValue: function(value) {
let me = this; this.getViewModel().set('datetime', value);
me.setDate(value);
me.setTime(value);
// Notify all 'value' bindings of state change
me.publishState('value', value);
}, },
getValue: function() { getValue: function() {
let me = this; return this.getViewModel().get('datetime');
let date = me.lookupReference('dateentry').getValue();
if (date === undefined || date === null) { return null; }
let time = me.lookupReference('timeentry').getValue();
if (time === undefined || time === null) { return null; }
date.setHours(time.getHours());
date.setMinutes(time.getMinutes());
date.setSeconds(time.getSeconds());
return date;
}, },
getSubmitValue: function() { getSubmitValue: function() {
let me = this; let me = this;
let format = me.submitFormat; let value = me.getValue();
let value = me.getValue(); return value ? Ext.Date.format(value, me.submitFormat) : null;
return value ? Ext.Date.format(value, format) : null;
}, },
setDate: function(date) { setMinValue: function(value) {
let me = this; this.getViewModel().set('minDatetime', value);
let dateEntry = me.lookupReference('dateentry');
dateEntry.setValue(date);
dateEntry.publishState('value', date);
}, },
setTime: function(time) { getMinValue: function() {
return this.getViewModel().get('minDatetime');
},
setMaxValue: function(value) {
this.getViewModel().set('maxDatetime', value);
},
getMaxValue: function() {
return this.getViewModel().get('maxDatetime');
},
initComponent: function() {
let me = this; let me = this;
let timeEntry = me.lookupReference('timeentry'); me.callParent();
timeEntry.setValue(time);
timeEntry.publishState('value', time); let vm = me.getViewModel();
vm.set('datetime', me.config.value);
// Propagate state change to binding
vm.bind('{datetime}', function(value) {
me.publishState('value', value);
me.fireEvent('change', value);
});
}, },
items: [ items: [
{ {
xtype: 'datefield', xtype: 'datefield',
editable: false, editable: false,
reference: 'dateentry',
flex: 1, flex: 1,
format: 'Y-m-d', format: 'Y-m-d',
bind: { bind: {
disabled: '{disabled}', value: '{date}',
}, minValue: '{minDate}',
listeners: { maxValue: '{maxDate}',
'change': function(field, newValue, oldValue) {
let dateTimeField = field.up('fieldcontainer');
dateTimeField.setDate(newValue);
let value = dateTimeField.getValue();
dateTimeField.publishState('value', value);
},
}, },
}, },
{ {
xtype: 'timefield', xtype: 'timefield',
reference: 'timeentry',
format: 'H:i', format: 'H:i',
width: 80, width: 80,
value: '00:00', value: '00:00',
increment: 60, increment: 60,
bind: { bind: {
disabled: '{disabled}', value: '{time}',
}, minValue: '{minTime}',
listeners: { maxValue: '{maxTime}',
'change': function(field, newValue, oldValue) {
let dateTimeField = field.up('fieldcontainer');
dateTimeField.setTime(newValue);
let value = dateTimeField.getValue();
dateTimeField.publishState('value', value);
},
}, },
}, },
], ],
setMinValue: function(value) {
let me = this;
let current = me.getValue();
if (!value || !current) {
return;
}
// Clone to avoid modifying the referenced value
let clone = new Date(value);
let minhours = clone.getHours();
let minminutes = clone.getMinutes();
let hours = current.getHours();
let minutes = current.getMinutes();
clone.setHours(0);
clone.setMinutes(0);
clone.setSeconds(0);
current.setHours(0);
current.setMinutes(0);
current.setSeconds(0);
let time = new Date();
if (current-clone > 0) {
time.setHours(0);
time.setMinutes(0);
time.setSeconds(0);
time.setMilliseconds(0);
} else {
time.setHours(minhours);
time.setMinutes(minminutes);
}
me.lookup('timeentry').setMinValue(time);
// current time is smaller than the time part of the new minimum
// so we have to add 1 to the day
if (minhours*60+minminutes > hours*60+minutes) {
clone.setDate(clone.getDate()+1);
}
me.lookup('dateentry').setMinValue(clone);
},
setMaxValue: function(value) {
let me = this;
let current = me.getValue();
if (!value || !current) {
return;
}
// Clone to avoid modifying the referenced value
let clone = new Date(value);
let maxhours = clone.getHours();
let maxminutes = clone.getMinutes();
let hours = current.getHours();
let minutes = current.getMinutes();
clone.setHours(0);
clone.setMinutes(0);
clone.setSeconds(0);
clone.setMilliseconds(0);
current.setHours(0);
current.setMinutes(0);
current.setSeconds(0);
current.setMilliseconds(0);
let time = new Date();
if (clone-current > 0) {
time.setHours(23);
time.setMinutes(59);
time.setSeconds(59);
} else {
time.setHours(maxhours);
time.setMinutes(maxminutes);
}
me.lookup('timeentry').setMaxValue(time);
// current time is bigger than the time part of the new maximum
// so we have to subtract 1 to the day
if (maxhours*60+maxminutes < hours*60+minutes) {
clone.setDate(clone.getDate()-1);
}
me.lookup('dateentry').setMaxValue(clone);
},
initComponent: function() {
let me = this;
me.callParent();
let value = me.value || new Date();
me.lookupReference('dateentry').setValue(value);
me.lookupReference('timeentry').setValue(value);
if (me.minValue) {
me.setMinValue(me.minValue);
}
if (me.maxValue) {
me.setMaxValue(me.maxValue);
}
me.relayEvents(me.lookupReference('dateentry'), ['change']);
me.relayEvents(me.lookupReference('timeentry'), ['change']);
},
}); });