From 50f4289a9f7cba0e3ab55996de24c34937ea3d02 Mon Sep 17 00:00:00 2001 From: Imshann <851188611@qq.com> Date: Thu, 24 Feb 2022 14:21:04 +0800 Subject: [PATCH] 优化Form组件 --- build/Button/Button.js | 4 ++-- build/Form/Form.js | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------- build/FormItem/FormItem.js | 93 +++++++++++++++++++++++++++++++++++++++++---------------------------------------------------- dist/ng-antd.js | 6 +++--- src/Button/Button.js | 2 +- src/Form/Form.js | 24 ++++++++++++++---------- src/FormItem/FormItem.js | 39 ++++++++++++++++++++++----------------- 7 files changed, 147 insertions(+), 150 deletions(-) diff --git a/build/Button/Button.js b/build/Button/Button.js index 0007270..2162cec 100644 --- a/build/Button/Button.js +++ b/build/Button/Button.js @@ -1,5 +1,5 @@ -import style from "antd/lib/button/style/index.css"; import template from "./Button.html"; +import style from "antd/lib/button/style/index.css"; angular.module("esNgAntd").directive("antdButton", ["esNgAntd", function (esNgAntd) { return { template: template, @@ -30,7 +30,7 @@ angular.module("esNgAntd").directive("antdButton", ["esNgAntd", function (esNgAn } }; }, - link: function ($scope) { + link: function ($scope, $element) { esNgAntd.createStyle("ant-btn", style); let className = ["ant-btn"]; diff --git a/build/Form/Form.js b/build/Form/Form.js index 6e9b485..ca3ecf0 100644 --- a/build/Form/Form.js +++ b/build/Form/Form.js @@ -1,76 +1,75 @@ import template from "./Form.html"; import style from "antd/lib/form/style/index.css"; -angular.module("esNgAntd").directive("antdForm", function (esNgAntd) { - return { - controllerAs: "antdForm", - restrict: "E", - transclude: true, - replace: true, - scope: { - name: "@", - labelCol: "=", - wrapperCol: "=", - onFinish: "&", - form: "=", - }, - template: template, - controller: function ($scope, $element, $attrs) { - this.getContext = function () { - return $scope; - }; +angular.module("esNgAntd").directive("antdForm", ["esNgAntd", function (esNgAntd) { + return { + template: template, + restrict: "E", + replace: true, + transclude: true, + scope: { + name: "@", + labelCol: "=", + wrapperCol: "=", + form: "=", + onFinish: "&" + }, + controller: function ($scope, $element) { + this.getContext = function () { + return $scope; + }; - $scope.state = { - formItems: [], - }; + $scope.state = { + formItems: [] + }; - $scope.resetFields = function () { - $scope.state.formItems.forEach(function (item) { - if (typeof item.setValue === "function") { - item.setValue(item.defaultValue || null); - } else { - item.value = null; - } - }); - }; + $scope.resetFields = function () { + $scope.state.formItems.forEach(function (item) { + if (typeof item.setValue === "function") { + item.setValue(item.defaultValue || null); + } else { + item.value = null; + } + }); + }; - $scope.submit = function () { - $scope.handleSubmit(); - }; + $scope.submit = function () { + $scope.handleSubmit(); + }; - $scope.handleSubmit = function () { - let values = {}; - $scope.state.formItems.forEach(function (item) { - let name = item.antdFormItem && item.antdFormItem.name; - let value = null; + $scope.handleSubmit = function () { + let values = {}; + $scope.state.formItems.forEach(function (item) { + let name = item.antdFormItem && item.antdFormItem.name; + let value = null; - if (item.state.type === "checkbox") { - value = item.state.checked; - } else { - value = item.value || item.state.value; - } + if (item.state.type === "checkbox") { + value = item.state.checked; + } else { + value = item.value || item.state.value; + } - values[name] = value; - }); - $scope.onFinish({ - values: values, - }); - }; - }, - link: function ($scope, $element, $attrs, $controllers, $transclude) { - esNgAntd.createStyle("ant-form", style); + values[name] = value; + }); + $scope.onFinish({ + values: values + }); + }; + }, + link: function ($scope, $element) { + esNgAntd.createStyle("ant-form", style); - if ($scope.form !== undefined) { - $scope.form = $scope; - } + if ($scope.form !== undefined) { + $scope.form = $scope; + } - if ($scope.name) { - let inputs = $element[0].querySelectorAll("input"); + if ($scope.name) { + let inputs = $element[0].querySelectorAll("input"); - for (let i = 0; i < inputs.length; i++) { - const element = inputs[i]; - element.id = $scope.name + "_" + element.id; - } - } - }, - }; -}); + for (let i = 0; i < inputs.length; i++) { + const element = inputs[i]; + element.id = $scope.name + "_" + element.id; + } + } + } + }; +}]); \ No newline at end of file diff --git a/build/FormItem/FormItem.js b/build/FormItem/FormItem.js index 57751aa..f20b162 100644 --- a/build/FormItem/FormItem.js +++ b/build/FormItem/FormItem.js @@ -1,60 +1,49 @@ /** * 表单域 - * - * @Author: Shann - * @LastEditors: Shann - * @Date: 2021-07-26 08:53:33 - * @LastEditTime: 2021-08-05 14:03:05 - * @FilePath: \angular-js-for-bootstrap\src\Essa\FormItem\FormItem.js - * @Copyright: Copyright 2021-2021, all rights reserved. Essa.cn */ import template from "./FormItem.html"; angular.module("esNgAntd").directive("antdFormItem", function () { - return { - controllerAs: "antdFormItem", - restrict: "E", - transclude: true, - replace: true, - scope: { - name: "@", - label: "@", - labelCol: "=", - wrapperCol: "=", - required: "@", - }, - template: template, - controller: function ($scope, $element, $attrs) { - this.getContext = function () { - return $scope; - }; + return { + template: template, + restrict: "E", + replace: true, + transclude: true, + scope: { + name: "@", + label: "@", + labelCol: "=", + wrapperCol: "=", + required: "=" + }, + require: ["^?antdForm"], + controller: function ($scope, $element, $attrs) { + this.getContext = function () { + return $scope; + }; - $scope.state = { - labelCol: null, - wrapperCol: null, - }; - }, - require: ["?^antdForm"], - link: function ($scope, $element, $attrs, $controllers, $transclude) { - let [antdForm] = $controllers; - $scope.antdForm = antdForm.getContext(); + $scope.state = { + labelCol: null, + wrapperCol: null + }; + }, + link: function ($scope, $element, $attrs, $controllers) { + let [antdForm] = $controllers; - if ($scope.labelCol && $scope.labelCol.span) { - $scope.state.labelCol = $scope.labelCol.span; - } else if ( - $scope.antdForm.labelCol && - $scope.antdForm.labelCol.span - ) { - $scope.state.labelCol = $scope.antdForm.labelCol.span; - } + if (antdForm) { + $scope.antdForm = antdForm.getContext(); + } - if ($scope.wrapperCol && $scope.wrapperCol.span) { - $scope.state.wrapperCol = $scope.wrapperCol.span; - } else if ( - $scope.antdForm.wrapperCol && - $scope.antdForm.wrapperCol.span - ) { - $scope.state.wrapperCol = $scope.antdForm.wrapperCol.span; - } - }, - }; -}); + if ($scope.labelCol && $scope.labelCol.span) { + $scope.state.labelCol = $scope.labelCol.span; + } else if ($scope.antdForm.labelCol && $scope.antdForm.labelCol.span) { + $scope.state.labelCol = $scope.antdForm.labelCol.span; + } + + if ($scope.wrapperCol && $scope.wrapperCol.span) { + $scope.state.wrapperCol = $scope.wrapperCol.span; + } else if ($scope.antdForm.wrapperCol && $scope.antdForm.wrapperCol.span) { + $scope.state.wrapperCol = $scope.antdForm.wrapperCol.span; + } + } + }; +}); \ No newline at end of file diff --git a/dist/ng-antd.js b/dist/ng-antd.js index ec69391..c7c18f4 100644 --- a/dist/ng-antd.js +++ b/dist/ng-antd.js @@ -49,7 +49,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Bre /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var antd_lib_button_style_index_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! antd/lib/button/style/index.css */ \"./node_modules/antd/lib/button/style/index.css\");\n/* harmony import */ var _Button_html__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Button.html */ \"./build/Button/Button.html\");\n\n\nangular.module(\"esNgAntd\").directive(\"antdButton\", [\"esNgAntd\", function (esNgAntd) {\n return {\n template: _Button_html__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n restrict: \"E\",\n replace: true,\n transclude: true,\n scope: {\n type: \"@\",\n size: \"@\",\n htmlType: \"@\",\n ghost: \"=\",\n loading: \"=\"\n },\n controller: function ($scope, $element) {\n $scope.state = {\n disabled: null,\n className: \"\"\n };\n $scope.watch = {\n loading: newVal => {\n if (newVal !== undefined) {\n if (newVal === \"true\") {\n $scope.state.className += \" ant-btn-loading\";\n } else {\n $scope.state.className = $scope.state.className.replace(\" ant-btn-loading\", \"\");\n }\n }\n }\n };\n },\n link: function ($scope) {\n esNgAntd.createStyle(\"ant-btn\", antd_lib_button_style_index_css__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n let className = [\"ant-btn\"];\n\n if ($scope.type) {\n className.push(\"ant-btn-\" + $scope.type);\n }\n\n if ($scope.size && [\"lg\", \"sm\", \"xs\"].includes($scope.size)) {\n className.push(\"ant-btn-\" + $scope.size);\n }\n\n if ($scope.ghost) {\n className.push(\"ant-btn-background-ghost\");\n }\n\n $scope.state.className = className.join(\" \");\n\n if ($scope.htmlType) {\n $element[0].setAttribute(\"type\", $scope.htmlType);\n }\n }\n };\n}]);\n\n//# sourceURL=webpack://ng-antd/./build/Button/Button.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Button_html__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Button.html */ \"./build/Button/Button.html\");\n/* harmony import */ var antd_lib_button_style_index_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! antd/lib/button/style/index.css */ \"./node_modules/antd/lib/button/style/index.css\");\n\n\nangular.module(\"esNgAntd\").directive(\"antdButton\", [\"esNgAntd\", function (esNgAntd) {\n return {\n template: _Button_html__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n restrict: \"E\",\n replace: true,\n transclude: true,\n scope: {\n type: \"@\",\n size: \"@\",\n htmlType: \"@\",\n ghost: \"=\",\n loading: \"=\"\n },\n controller: function ($scope, $element) {\n $scope.state = {\n disabled: null,\n className: \"\"\n };\n $scope.watch = {\n loading: newVal => {\n if (newVal !== undefined) {\n if (newVal === \"true\") {\n $scope.state.className += \" ant-btn-loading\";\n } else {\n $scope.state.className = $scope.state.className.replace(\" ant-btn-loading\", \"\");\n }\n }\n }\n };\n },\n link: function ($scope, $element) {\n esNgAntd.createStyle(\"ant-btn\", antd_lib_button_style_index_css__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n let className = [\"ant-btn\"];\n\n if ($scope.type) {\n className.push(\"ant-btn-\" + $scope.type);\n }\n\n if ($scope.size && [\"lg\", \"sm\", \"xs\"].includes($scope.size)) {\n className.push(\"ant-btn-\" + $scope.size);\n }\n\n if ($scope.ghost) {\n className.push(\"ant-btn-background-ghost\");\n }\n\n $scope.state.className = className.join(\" \");\n\n if ($scope.htmlType) {\n $element[0].setAttribute(\"type\", $scope.htmlType);\n }\n }\n };\n}]);\n\n//# sourceURL=webpack://ng-antd/./build/Button/Button.js?"); /***/ }), @@ -137,7 +137,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Form_html__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Form.html */ \"./build/Form/Form.html\");\n/* harmony import */ var antd_lib_form_style_index_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! antd/lib/form/style/index.css */ \"./node_modules/antd/lib/form/style/index.css\");\n\n\nangular.module(\"esNgAntd\").directive(\"antdForm\", function (esNgAntd) {\n return {\n controllerAs: \"antdForm\",\n restrict: \"E\",\n transclude: true,\n replace: true,\n scope: {\n name: \"@\",\n labelCol: \"=\",\n wrapperCol: \"=\",\n onFinish: \"&\",\n form: \"=\",\n },\n template: _Form_html__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n controller: function ($scope, $element, $attrs) {\n this.getContext = function () {\n return $scope;\n };\n\n $scope.state = {\n formItems: [],\n };\n\n $scope.resetFields = function () {\n $scope.state.formItems.forEach(function (item) {\n if (typeof item.setValue === \"function\") {\n item.setValue(item.defaultValue || null);\n } else {\n item.value = null;\n }\n });\n };\n\n $scope.submit = function () {\n $scope.handleSubmit();\n };\n\n $scope.handleSubmit = function () {\n let values = {};\n $scope.state.formItems.forEach(function (item) {\n let name = item.antdFormItem && item.antdFormItem.name;\n let value = null;\n\n if (item.state.type === \"checkbox\") {\n value = item.state.checked;\n } else {\n value = item.value || item.state.value;\n }\n\n values[name] = value;\n });\n $scope.onFinish({\n values: values,\n });\n };\n },\n link: function ($scope, $element, $attrs, $controllers, $transclude) {\n esNgAntd.createStyle(\"ant-form\", antd_lib_form_style_index_css__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n if ($scope.form !== undefined) {\n $scope.form = $scope;\n }\n\n if ($scope.name) {\n let inputs = $element[0].querySelectorAll(\"input\");\n\n for (let i = 0; i < inputs.length; i++) {\n const element = inputs[i];\n element.id = $scope.name + \"_\" + element.id;\n }\n }\n },\n };\n});\n\n\n//# sourceURL=webpack://ng-antd/./build/Form/Form.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Form_html__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Form.html */ \"./build/Form/Form.html\");\n/* harmony import */ var antd_lib_form_style_index_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! antd/lib/form/style/index.css */ \"./node_modules/antd/lib/form/style/index.css\");\n\n\nangular.module(\"esNgAntd\").directive(\"antdForm\", [\"esNgAntd\", function (esNgAntd) {\n return {\n template: _Form_html__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n restrict: \"E\",\n replace: true,\n transclude: true,\n scope: {\n name: \"@\",\n labelCol: \"=\",\n wrapperCol: \"=\",\n form: \"=\",\n onFinish: \"&\"\n },\n controller: function ($scope, $element) {\n this.getContext = function () {\n return $scope;\n };\n\n $scope.state = {\n formItems: []\n };\n\n $scope.resetFields = function () {\n $scope.state.formItems.forEach(function (item) {\n if (typeof item.setValue === \"function\") {\n item.setValue(item.defaultValue || null);\n } else {\n item.value = null;\n }\n });\n };\n\n $scope.submit = function () {\n $scope.handleSubmit();\n };\n\n $scope.handleSubmit = function () {\n let values = {};\n $scope.state.formItems.forEach(function (item) {\n let name = item.antdFormItem && item.antdFormItem.name;\n let value = null;\n\n if (item.state.type === \"checkbox\") {\n value = item.state.checked;\n } else {\n value = item.value || item.state.value;\n }\n\n values[name] = value;\n });\n $scope.onFinish({\n values: values\n });\n };\n },\n link: function ($scope, $element) {\n esNgAntd.createStyle(\"ant-form\", antd_lib_form_style_index_css__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n if ($scope.form !== undefined) {\n $scope.form = $scope;\n }\n\n if ($scope.name) {\n let inputs = $element[0].querySelectorAll(\"input\");\n\n for (let i = 0; i < inputs.length; i++) {\n const element = inputs[i];\n element.id = $scope.name + \"_\" + element.id;\n }\n }\n }\n };\n}]);\n\n//# sourceURL=webpack://ng-antd/./build/Form/Form.js?"); /***/ }), @@ -148,7 +148,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _For /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _FormItem_html__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./FormItem.html */ \"./build/FormItem/FormItem.html\");\n/**\n * 表单域\n *\n * @Author: Shann\n * @LastEditors: Shann\n * @Date: 2021-07-26 08:53:33\n * @LastEditTime: 2021-08-05 14:03:05\n * @FilePath: \\angular-js-for-bootstrap\\src\\Essa\\FormItem\\FormItem.js\n * @Copyright: Copyright 2021-2021, all rights reserved. Essa.cn\n */\n\nangular.module(\"esNgAntd\").directive(\"antdFormItem\", function () {\n return {\n controllerAs: \"antdFormItem\",\n restrict: \"E\",\n transclude: true,\n replace: true,\n scope: {\n name: \"@\",\n label: \"@\",\n labelCol: \"=\",\n wrapperCol: \"=\",\n required: \"@\",\n },\n template: _FormItem_html__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n controller: function ($scope, $element, $attrs) {\n this.getContext = function () {\n return $scope;\n };\n\n $scope.state = {\n labelCol: null,\n wrapperCol: null,\n };\n },\n require: [\"?^antdForm\"],\n link: function ($scope, $element, $attrs, $controllers, $transclude) {\n let [antdForm] = $controllers;\n $scope.antdForm = antdForm.getContext();\n\n if ($scope.labelCol && $scope.labelCol.span) {\n $scope.state.labelCol = $scope.labelCol.span;\n } else if (\n $scope.antdForm.labelCol &&\n $scope.antdForm.labelCol.span\n ) {\n $scope.state.labelCol = $scope.antdForm.labelCol.span;\n }\n\n if ($scope.wrapperCol && $scope.wrapperCol.span) {\n $scope.state.wrapperCol = $scope.wrapperCol.span;\n } else if (\n $scope.antdForm.wrapperCol &&\n $scope.antdForm.wrapperCol.span\n ) {\n $scope.state.wrapperCol = $scope.antdForm.wrapperCol.span;\n }\n },\n };\n});\n\n\n//# sourceURL=webpack://ng-antd/./build/FormItem/FormItem.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _FormItem_html__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./FormItem.html */ \"./build/FormItem/FormItem.html\");\n/**\n * 表单域\n */\n\nangular.module(\"esNgAntd\").directive(\"antdFormItem\", function () {\n return {\n template: _FormItem_html__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n restrict: \"E\",\n replace: true,\n transclude: true,\n scope: {\n name: \"@\",\n label: \"@\",\n labelCol: \"=\",\n wrapperCol: \"=\",\n required: \"=\"\n },\n require: [\"^?antdForm\"],\n controller: function ($scope, $element, $attrs) {\n this.getContext = function () {\n return $scope;\n };\n\n $scope.state = {\n labelCol: null,\n wrapperCol: null\n };\n },\n link: function ($scope, $element, $attrs, $controllers) {\n let [antdForm] = $controllers;\n\n if (antdForm) {\n $scope.antdForm = antdForm.getContext();\n }\n\n if ($scope.labelCol && $scope.labelCol.span) {\n $scope.state.labelCol = $scope.labelCol.span;\n } else if ($scope.antdForm.labelCol && $scope.antdForm.labelCol.span) {\n $scope.state.labelCol = $scope.antdForm.labelCol.span;\n }\n\n if ($scope.wrapperCol && $scope.wrapperCol.span) {\n $scope.state.wrapperCol = $scope.wrapperCol.span;\n } else if ($scope.antdForm.wrapperCol && $scope.antdForm.wrapperCol.span) {\n $scope.state.wrapperCol = $scope.antdForm.wrapperCol.span;\n }\n }\n };\n});\n\n//# sourceURL=webpack://ng-antd/./build/FormItem/FormItem.js?"); /***/ }), diff --git a/src/Button/Button.js b/src/Button/Button.js index ec3f5cd..6519526 100644 --- a/src/Button/Button.js +++ b/src/Button/Button.js @@ -25,7 +25,7 @@ class Button { }, }; - constructor() { + constructor($element) { esNgAntd.createStyle("ant-btn", style); let className = ["ant-btn"]; diff --git a/src/Form/Form.js b/src/Form/Form.js index 2eddfcd..96fb64e 100644 --- a/src/Form/Form.js +++ b/src/Form/Form.js @@ -4,21 +4,13 @@ import style from "antd/lib/form/style/index.css"; class Form { useModules = ["esNgAntd"]; - template = template; - - props = { - name: String, - labelCol: Object, - wrapperCol: Object, - onFinish: Function, - form: Object, - }; + context = true; state = { formItems: [], }; - constructor() { + constructor($element) { esNgAntd.createStyle("ant-form", style); if (this.form !== undefined) { @@ -64,4 +56,16 @@ class Form { values: values, }); } + + render() { + return template; + } } + +Form.propTypes = { + name: PropTypes.string, + labelCol: PropTypes.object, + wrapperCol: PropTypes.object, + form: PropTypes.object, + onFinish: PropTypes.function, +}; diff --git a/src/FormItem/FormItem.js b/src/FormItem/FormItem.js index 2a23286..142aca1 100644 --- a/src/FormItem/FormItem.js +++ b/src/FormItem/FormItem.js @@ -1,32 +1,25 @@ /** * 表单域 - * - * @Author: Shann - * @LastEditors: Shann - * @Date: 2021-07-26 08:53:33 - * @LastEditTime: 2021-08-05 14:03:05 - * @FilePath: \angular-js-for-bootstrap\src\Essa\FormItem\FormItem.js - * @Copyright: Copyright 2021-2021, all rights reserved. Essa.cn */ import template from "./FormItem.html"; class FormItem { - props = { - name: String, - label: String, - labelCol: Object, - wrapperCol: Object, - required: Boolean, - }; + + require = ["^?antdForm"]; + + context = true; + state = { labelCol: null, wrapperCol: null, }; - template = template; + constructor($element, $attrs, $controllers) { + let [antdForm] = $controllers; - constructor(antdForm) { - this.antdForm = antdForm.getContext(); + if (antdForm) { + this.antdForm = antdForm.getContext(); + } if (this.props.labelCol && this.props.labelCol.span) { this.state.labelCol = this.props.labelCol.span; @@ -46,4 +39,16 @@ class FormItem { this.state.wrapperCol = this.antdForm.wrapperCol.span; } } + + render() { + return template; + } } + +FormItem.propTypes = { + name: PropTypes.string, + label: PropTypes.string, + labelCol: PropTypes.object, + wrapperCol: PropTypes.object, + required: PropTypes.boolean, +}; -- libgit2 0.21.2