diff --git a/api/etemplate.php b/api/etemplate.php
index 4887831ca6..f7cf3f05db 100644
--- a/api/etemplate.php
+++ b/api/etemplate.php
@@ -130,6 +130,22 @@ function send_template()
// fix --> (et2-prefix and self-closing is handled below)
$str = preg_replace('##u', '', $str);
+ // fix <(textbox|int(eger)?|float) precision="int(eger)?|float" .../> -->
+ $str = preg_replace_callback('#<(textbox|int(eger)?|float|number).*?\s(type="(int(eger)?|float)")?.*?/>#u',
+ static function($matches)
+ {
+ if ($matches[1] === 'textbox' && !in_array($matches[4], ['float', 'int', 'integer'], true))
+ {
+ return $matches[0]; // regular textbox --> nothing to do
+ }
+ $type = $matches[1] === 'float' || $matches[4] === 'float' ? 'float' : 'int';
+ $tag = str_replace('<'.$matches[1], '';
+
+ }, $str);
+
// fix -->
$str = preg_replace('##u', '', $str);
diff --git a/api/js/etemplate/Et2Textbox/Et2Number.ts b/api/js/etemplate/Et2Textbox/Et2Number.ts
new file mode 100644
index 0000000000..aa3b5f76aa
--- /dev/null
+++ b/api/js/etemplate/Et2Textbox/Et2Number.ts
@@ -0,0 +1,108 @@
+/**
+ * EGroupware eTemplate2 - Number widget (WebComponent)
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link https://www.egroupware.org
+ * @author Ralf Becker
+ */
+
+import {Et2Textbox} from "./Et2Textbox";
+
+export class Et2Number extends Et2Textbox
+{
+ static get properties()
+ {
+ return {
+ ...super.properties,
+ /**
+ * Minimum value
+ */
+ min: Number,
+ /**
+ * Maximum value
+ */
+ max: Number,
+ /**
+ * Step value
+ */
+ step: Number,
+ /**
+ * Precision of float number or 0 for integer
+ */
+ precision: Number,
+ }
+ }
+
+ transformAttributes(attrs)
+ {
+ if (attrs.precision === 0 && typeof attrs.step === 'undefined')
+ {
+ attrs.step = 1;
+ }
+ if (typeof attrs.validator === 'undefined')
+ {
+ attrs.validator = attrs.precision === 0 ? '/^-?[0-9]*$/' : '/^-?[0-9]*[,.]?[0-9]*$/';
+ }
+ attrs.type = 'number';
+ super.transformAttributes(attrs);
+ }
+
+ /**
+ * Somehow the setter is not inherited fro the parent, not defining it here leaves the validator a string!
+ *
+ * @param regexp
+ */
+ set validator(regexp)
+ {
+ super.validator = regexp;
+ }
+ get validator()
+ {
+ return super.validator;
+ }
+
+ set_value(val)
+ {
+ if (""+val !== "")
+ {
+ if (typeof this.precision !== 'undefined')
+ {
+ val = parseFloat(val).toFixed(this.precision);
+ }
+ else
+ {
+ val = parseFloat(val);
+ }
+ // use decimal separator from user prefs
+ const format = this.egw().preference('number_format');
+ const sep = format ? format[0] : '.';
+ if(typeof val === 'string' && format && sep && sep !== '.')
+ {
+ val = val.replace('.', sep);
+ }
+ }
+ this.value = val;
+ }
+
+ getValue()
+ {
+ let val = this.value;
+
+ if (""+val !== "")
+ {
+ if (typeof this.precision !== 'undefined')
+ {
+ val = parseFloat(val).toFixed(this.precision);
+ }
+ else
+ {
+ val = parseFloat(val);
+ }
+ }
+ return val;
+ }
+}
+// @ts-ignore TypeScript is not recognizing that Et2Textbox is a LitElement
+customElements.define("et2-number", Et2Number);
\ No newline at end of file
diff --git a/api/js/etemplate/Et2Textbox/Et2NumberReadonly.ts b/api/js/etemplate/Et2Textbox/Et2NumberReadonly.ts
new file mode 100644
index 0000000000..5c738ba72a
--- /dev/null
+++ b/api/js/etemplate/Et2Textbox/Et2NumberReadonly.ts
@@ -0,0 +1,52 @@
+/**
+ * EGroupware eTemplate2 - Number widget (WebComponent)
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link https://www.egroupware.org
+ * @author Ralf Becker
+ */
+
+import {Et2TextboxReadonly} from "./Et2TextboxReadonly";
+
+export class Et2NumberReadonly extends Et2TextboxReadonly
+{
+ static get properties()
+ {
+ return {
+ ...super.properties,
+ /**
+ * Precision of float number or 0 for integer
+ */
+ precision: Number,
+ }
+ }
+
+ set_value(val)
+ {
+ if (""+val !== "")
+ {
+ if (typeof this.precision !== 'undefined')
+ {
+ val = parseFloat(val).toFixed(this.precision);
+ }
+ else
+ {
+ val = parseFloat(val);
+ }
+ }
+ // use decimal separator from user prefs
+ const format = this.egw().preference('number_format');
+ const sep = format ? format[0] : '.';
+ if(typeof val === 'string' && format && sep && sep !== '.')
+ {
+ val = val.replace('.', sep);
+ }
+ // can not call super.set_value(), as it does not call the setter for value
+ super.value = val;
+ }
+}
+
+// @ts-ignore TypeScript is not recognizing that Et2Textbox is a LitElement
+customElements.define("et2-number_ro", Et2NumberReadonly);
\ No newline at end of file
diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts
index 13fa14d783..89913a1294 100644
--- a/api/js/etemplate/etemplate2.ts
+++ b/api/js/etemplate/etemplate2.ts
@@ -42,6 +42,8 @@ import './Et2Select/Et2SelectReadonly';
import './Et2Textarea/Et2Textarea';
import './Et2Textbox/Et2Textbox';
import './Et2Textbox/Et2TextboxReadonly';
+import './Et2Textbox/Et2Number';
+import './Et2Textbox/Et2NumberReadonly';
import './Et2Colorpicker/Et2Colorpicker';
import './Et2Taglist/Et2Taglist';