Hi to all!
After a little bit of research I've come up with an attempt to a date+time widget. Here is the template:
<table cellspacing="0" cellpadding="0"
class="dijit dijitReset dijitInlineTable"
style="display: -moz-inline-stack; border: none"
waiRole="presentation"
id="widget_dt_${id}"
tabindex="0"
dojoAttachPoint="containerNode">
<tr class="dijitReset">
<td class="required_${required}">
<input type="hidden"
name="${name}"
dojoAttachPoint="textbox,textNode"
style="display: none">
</td>
<td class="required_${required}"
dojoAttachPoint="focusNode"
class="dijitReset dijitInputField">
<input type="text"
dojoType="dijit.form.DateTextBox"
dojoAttachPoint="dateNode"
id="${id}_datePart"
required="${required}"
style="width: 10em;">
<td class="required_${required}"
class="dijitReset dijitInputField">
<input type="text"
dojoType="dijit.form.TimeTextBox"
dojoAttachPoint="timeNode"
id="${id}_timePart"
required="${required}"
style="width: 8em;">
</td>
</tr>
</table>
class="dijit dijitReset dijitInlineTable"
style="display: -moz-inline-stack; border: none"
waiRole="presentation"
id="widget_dt_${id}"
tabindex="0"
dojoAttachPoint="containerNode">
<tr class="dijitReset">
<td class="required_${required}">
<input type="hidden"
name="${name}"
dojoAttachPoint="textbox,textNode"
style="display: none">
</td>
<td class="required_${required}"
dojoAttachPoint="focusNode"
class="dijitReset dijitInputField">
<input type="text"
dojoType="dijit.form.DateTextBox"
dojoAttachPoint="dateNode"
id="${id}_datePart"
required="${required}"
style="width: 10em;">
<td class="required_${required}"
class="dijitReset dijitInputField">
<input type="text"
dojoType="dijit.form.TimeTextBox"
dojoAttachPoint="timeNode"
id="${id}_timePart"
required="${required}"
style="width: 8em;">
</td>
</tr>
</table>
and here is the .js part (DateTime.js):
dojo.provide("dojoc.widget.DateTime");
dojo.require("dijit.form._FormWidget");
dojo.require("dijit.form.TimeTextBox");
dojo.require("dijit.form.DateTextBox");
dojo.require("dojo.date");
dojo.require("dojo.date.locale");
dojo.require("dojo.date.stamp");
dojo.require("dojo.parser");
dojo.declare("dojoc.widget.DateTime", [ dijit.form._FormWidget, dijit.form.TimeTextBox ], {
id: 'dt',
name: 'dt',
required: true,
constraints: {},
value: dojo.date.locale.format(new Date(),
{ timePattern:"HH:mm", datePattern:"yyyy/MM/dd"}),
templatePath: dojo.moduleUrl("dojoc.widget","DateTime/DateTime.html"),
templateString: "",
src: "",
disabled: false,
widgetsInTemplate: true,
startup: function() {
this.setValue(this.value);
},
postCreate: function() {
this.connect(this.dateNode, "onChange", "_onChange");
this.connect(this.timeNode, "onChange", "_onChange");
this.setDisabled();
},
setDisabled: function() {
this.dateNode.setDisabled(this.disabled);
this.timeNode.setDisabled(this.disabled);
},
setDisplayedValue: function(value) {
var myDate=dojo.date.locale.parse(value);
this.dateNode.setValue(myDate);
this.timeNode.setValue(myDate);
},
getDisplayedValue: function() {
var dateVal=this.dateNode.textbox.value;
var timeVal=this.timeNode.textbox.value;
return dateVal+" "+timeVal;
},
getValue: function() {
var dateVal=dojo.date.stamp.toISOString(this.dateNode.getValue(),
{ selector: "date", timePattern: "yyyy-MM-dd" });
// var dateVal=this.dateNode.textbox.value;
var timeVal=this.timeNode.textbox.value;
return dateVal+"T"+timeVal;
},
setValue: function(val, options) {
var myDate=new Date(val);
this.dateNode.setValue(myDate);
this.timeNode.setValue(myDate);
},
toString: function(val, options) {
if(val) {
var dateVal=dojo.date.locale.format(val, {selector:'date', datePattern: 'yyyy-MM-dd'});
var timeVal=dojo.date.locale.format(val, {selector:'time', timePattern: 'HH:mm:ss'});
return dateVal+"T"+timeVal;
} else {
return this.getValue();
}
},
updateValue: function() {
this.textNode.value=this.getValue();
this.validate(false);
},
_open: function() {
return true;
},
_close: function() {
return true;
},
_onBlur: function (e) {
return true;
},
_onChange: function(e) {
this.updateValue();
this.onChange();
},
validator: function(value,constraints){
var date=dojo.date.stamp.fromISOString(value);
var minDate=dojo.date.stamp.fromISOString(constraints.min);
var maxDate=dojo.date.stamp.fromISOString(constraints.max);
if(typeof constraints.min != "undefined") {
var minDate=dojo.date.stamp.fromISOString(constraints.min);
var isMajor=(date >= minDate);
} else {
var isMajor=true;
}
if(typeof constraints.max != "undefined") {
var maxDate=dojo.date.stamp.fromISOString(constraints.max);
var isMinor=(date <= maxDate);
} else {
var isMinor=true;
}
if(isMinor && isMajor) {
return true;
} else {
return false;
}
},
validate: function(isFocused) {
var isValid = this.isValid(isFocused);
var isEmpty = this._isEmpty(this.textbox.value);
var state = (isValid || isEmpty) ? "" : "Error";
this.timeNode.state = state;
this.dateNode.state = state;
this.state = state;
this.timeNode._setStateClass();
this.dateNode._setStateClass();
this._setStateClass();
dijit.setWaiState(this.timeNode.focusNode, "invalid", (isValid? "false" : "true"));
dijit.setWaiState(this.dateNode.focusNode, "invalid", (isValid? "false" : "true"));
},
isValid: function(isFocused) {
return this.validator(this.textNode.value, this.constraints);
}
}
);
dojo.require("dijit.form._FormWidget");
dojo.require("dijit.form.TimeTextBox");
dojo.require("dijit.form.DateTextBox");
dojo.require("dojo.date");
dojo.require("dojo.date.locale");
dojo.require("dojo.date.stamp");
dojo.require("dojo.parser");
dojo.declare("dojoc.widget.DateTime", [ dijit.form._FormWidget, dijit.form.TimeTextBox ], {
id: 'dt',
name: 'dt',
required: true,
constraints: {},
value: dojo.date.locale.format(new Date(),
{ timePattern:"HH:mm", datePattern:"yyyy/MM/dd"}),
templatePath: dojo.moduleUrl("dojoc.widget","DateTime/DateTime.html"),
templateString: "",
src: "",
disabled: false,
widgetsInTemplate: true,
startup: function() {
this.setValue(this.value);
},
postCreate: function() {
this.connect(this.dateNode, "onChange", "_onChange");
this.connect(this.timeNode, "onChange", "_onChange");
this.setDisabled();
},
setDisabled: function() {
this.dateNode.setDisabled(this.disabled);
this.timeNode.setDisabled(this.disabled);
},
setDisplayedValue: function(value) {
var myDate=dojo.date.locale.parse(value);
this.dateNode.setValue(myDate);
this.timeNode.setValue(myDate);
},
getDisplayedValue: function() {
var dateVal=this.dateNode.textbox.value;
var timeVal=this.timeNode.textbox.value;
return dateVal+" "+timeVal;
},
getValue: function() {
var dateVal=dojo.date.stamp.toISOString(this.dateNode.getValue(),
{ selector: "date", timePattern: "yyyy-MM-dd" });
// var dateVal=this.dateNode.textbox.value;
var timeVal=this.timeNode.textbox.value;
return dateVal+"T"+timeVal;
},
setValue: function(val, options) {
var myDate=new Date(val);
this.dateNode.setValue(myDate);
this.timeNode.setValue(myDate);
},
toString: function(val, options) {
if(val) {
var dateVal=dojo.date.locale.format(val, {selector:'date', datePattern: 'yyyy-MM-dd'});
var timeVal=dojo.date.locale.format(val, {selector:'time', timePattern: 'HH:mm:ss'});
return dateVal+"T"+timeVal;
} else {
return this.getValue();
}
},
updateValue: function() {
this.textNode.value=this.getValue();
this.validate(false);
},
_open: function() {
return true;
},
_close: function() {
return true;
},
_onBlur: function (e) {
return true;
},
_onChange: function(e) {
this.updateValue();
this.onChange();
},
validator: function(value,constraints){
var date=dojo.date.stamp.fromISOString(value);
var minDate=dojo.date.stamp.fromISOString(constraints.min);
var maxDate=dojo.date.stamp.fromISOString(constraints.max);
if(typeof constraints.min != "undefined") {
var minDate=dojo.date.stamp.fromISOString(constraints.min);
var isMajor=(date >= minDate);
} else {
var isMajor=true;
}
if(typeof constraints.max != "undefined") {
var maxDate=dojo.date.stamp.fromISOString(constraints.max);
var isMinor=(date <= maxDate);
} else {
var isMinor=true;
}
if(isMinor && isMajor) {
return true;
} else {
return false;
}
},
validate: function(isFocused) {
var isValid = this.isValid(isFocused);
var isEmpty = this._isEmpty(this.textbox.value);
var state = (isValid || isEmpty) ? "" : "Error";
this.timeNode.state = state;
this.dateNode.state = state;
this.state = state;
this.timeNode._setStateClass();
this.dateNode._setStateClass();
this._setStateClass();
dijit.setWaiState(this.timeNode.focusNode, "invalid", (isValid? "false" : "true"));
dijit.setWaiState(this.dateNode.focusNode, "invalid", (isValid? "false" : "true"));
},
isValid: function(isFocused) {
return this.validator(this.textNode.value, this.constraints);
}
}
);
It is ugly, I've not tested it with IE and it has a seriouse cosmetic bug, since the containing table get focus when you tab on all fields of a form, instead of being ignored, but it should be a start.
Any suggestions?
Akel

If you still need this...
Thanks to some feedback I got from Wombat in regards to a similar problem I had with my Date Range widget I think I've found your issue.
If you extend dijit._Templated rather than dijit.form.TimeTextBox you will resolve the issue of the container node.
You'll be missing the _isEmpty function that was inherited from ValidationTextBox via TimeTextBox but you can reference it explicitly
Or just drop it into your class so you don't have to deal with the possibility of them changing the function since it's private.