refactor: input-number

doc-form
tangjinzhou 2021-12-19 23:15:27 +08:00
parent 384ea35ca7
commit 2a47de6c57
23 changed files with 1813 additions and 1078 deletions

View File

@ -0,0 +1,3 @@
export default function (val: any) {
return val !== undefined && val !== null;
}

View File

@ -1,26 +1,113 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/input-number/demo/addon.vue correctly 1`] = `
<div class="ant-space ant-space-vertical">
<div class="ant-space-item" style="margin-bottom: 8px;">
<div class="ant-input-number-group-wrapper">
<div class="ant-input-number-wrapper ant-input-number-group">
<div class="ant-input-number-group-addon">+</div>
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number-group-addon">$</div>
</div>
</div>
</div>
<!---->
<div class="ant-space-item" style="margin-bottom: 8px;">
<div class="ant-input-number-group-wrapper">
<div class="ant-input-number-wrapper ant-input-number-group">
<div class="ant-input-number-group-addon">
<div style="width: 60px;" class="ant-select ant-select-single ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"></span><span class="ant-select-selection-item">+</span>
<!---->
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
</div>
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number-group-addon">
<div style="width: 60px;" class="ant-select ant-select-single ant-select-show-arrow">
<!---->
<!---->
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"></span><span class="ant-select-selection-item">$</span>
<!---->
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
<!---->
</div>
</div>
</div>
</div>
</div>
<!---->
<div class="ant-space-item" style="margin-bottom: 8px;">
<div class="ant-input-number-group-wrapper">
<div class="ant-input-number-wrapper ant-input-number-group">
<!---->
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number-group-addon"><span role="img" aria-label="setting" class="anticon anticon-setting"><svg focusable="false" class="" data-icon="setting" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"></path></svg></span></div>
</div>
</div>
</div>
<!---->
<div class="ant-space-item">
<div class="ant-input-number-group-wrapper">
<div class="ant-input-number-wrapper ant-input-number-group">
<!---->
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number-group-addon">
<!----><span class="ant-cascader-picker" style="width: 150px;" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="cascader" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
</div>
</div>
</div>
</div>
<!---->
</div>
`;
exports[`renders ./components/input-number/demo/basic.vue correctly 1`] = `
<div>
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="10" min="1" step="1" id="inputNumber"></div>
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" id="inputNumber" class="ant-input-number-input"></div>
</div> 当前值3
</div>
`;
exports[`renders ./components/input-number/demo/borderless.vue correctly 1`] = `
<div>
<div class="ant-input-number ant-input-number-borderless">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" id="inputNumber" class="ant-input-number-input"></div>
</div>
</div>
`;
exports[`renders ./components/input-number/demo/digit.vue correctly 1`] = `
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="0" aria-valuemax="10" class="ant-input-number-input" autocomplete="off" max="10" min="0" step="0.1"></div>
<div class="ant-input-number" style="width: 200px;">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="0" aria-valuemax="10" aria-valuenow="1" step="1e-14" class="ant-input-number-input"></div>
</div>
`;
exports[`renders ./components/input-number/demo/disabled.vue correctly 1`] = `
<div>
<div class="ant-input-number ant-input-number-disabled">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" disabled="" max="10" min="1" step="1"></div>
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" class="ant-input-number-input" disabled=""></div>
</div>
<div style="margin-top: 20px;"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Toggle disabled</span>
@ -31,29 +118,59 @@ exports[`renders ./components/input-number/demo/disabled.vue correctly 1`] = `
exports[`renders ./components/input-number/demo/formatter.vue correctly 1`] = `
<div>
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="-9007199254740991" aria-valuenow="1000" class="ant-input-number-input" autocomplete="off" min="-9007199254740991" step="1"></div>
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="1000" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" class="ant-input-number-input" autocomplete="off" max="100" min="0" step="1"></div>
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
</div>
</div>
`;
exports[`renders ./components/input-number/demo/keyboard.vue correctly 1`] = `
<div class="ant-space ant-space-horizontal ant-space-align-center">
<div class="ant-space-item" style="margin-right: 8px;">
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
</div>
</div>
<!---->
<div class="ant-space-item"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span>Toggle keyboard</span></label></div>
<!---->
</div>
`;
exports[`renders ./components/input-number/demo/out-of-range.vue correctly 1`] = `
<div class="ant-space ant-space-horizontal ant-space-align-center">
<div class="ant-space-item" style="margin-right: 8px;">
<div class="ant-input-number ant-input-number-out-of-range">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="99" step="1" class="ant-input-number-input"></div>
</div>
</div>
<!---->
<div class="ant-space-item"><button class="ant-btn ant-btn-primary" type="button">
<!----><span>Reset</span>
</button></div>
<!---->
</div>
`;
exports[`renders ./components/input-number/demo/size.vue correctly 1`] = `
<div>
<div class="ant-input-number-lg ant-input-number">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="100000" min="1" step="1"></div>
<div class="ant-input-number ant-input-number-lg">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="100000" min="1" step="1"></div>
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
</div>
<div class="ant-input-number-sm ant-input-number">
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="100000" min="1" step="1"></div>
<div class="ant-input-number ant-input-number-sm">
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
</div>
</div>
`;

View File

@ -22,8 +22,6 @@ describe('InputNumber', () => {
);
wrapper.find('input').element.value = '';
wrapper.find('input').trigger('input');
expect(onChange).toHaveBeenLastCalledWith('');
wrapper.find('input').trigger('blur');
expect(onChange).toHaveBeenLastCalledWith(null);
});
});

View File

@ -0,0 +1,64 @@
<docs>
---
order: 2
title:
zh-CN: 前置/后置标签
en-US: Pre / Post tab
---
## zh-CN
用于配置一些固定组合
## en-US
Using pre & post tabs example.
</docs>
<template>
<a-space direction="vertical">
<a-input-number v-model:value="value1" addon-before="+" addon-after="$"></a-input-number>
<a-input-number v-model:value="value2">
<template #addonBefore>
<a-select v-model:value="addonBeforeValue" style="width: 60px">
<a-select-option value="add">+</a-select-option>
<a-select-option value="minus">-</a-select-option>
</a-select>
</template>
<template #addonAfter>
<a-select v-model:value="addonAfterValue" style="width: 60px">
<a-select-option value="USD">$</a-select-option>
<a-select-option value="EUR"></a-select-option>
<a-select-option value="GBP">£</a-select-option>
<a-select-option value="CNY">¥</a-select-option>
</a-select>
</template>
</a-input-number>
<a-input-number v-model:value="value3">
<template #addonAfter><SettingOutlined /></template>
</a-input-number>
<a-input-number v-model:value="value4">
<template #addonAfter>
<a-cascader placeholder="cascader" style="width: 150px" />
</template>
</a-input-number>
</a-space>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { SettingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
components: { SettingOutlined },
setup() {
return {
value1: ref(100),
value2: ref(100),
value3: ref(100),
value4: ref(100),
addonBeforeValue: ref('add'),
addonAfterValue: ref('USD'),
};
},
});
</script>

View File

@ -0,0 +1,35 @@
<docs>
---
order: 6
title:
zh-CN: 无边框
en-US: Borderless
---
## zh-CN
没有边框
## en-US
No border.
</docs>
<template>
<div>
<a-input-number id="inputNumber" v-model:value="value" :bordered="false" :min="1" :max="10" />
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const value = ref<number>(3);
return {
value,
};
},
});
</script>

View File

@ -1,29 +1,38 @@
<docs>
---
order: 0
order: 3
title:
zh-CN: 小数
en-US: Decimals
zh-CN: 高精度小数
en-US: High precision decimals
---
## zh-CN
和原生的数字输入框一样value 的精度由 step 的小数位数决定
通过 `stringMode` 开启高精度小数支持`change` 事件将返回 string 类型
对于旧版浏览器你需要 BigInt polyfill
## en-US
A numeric-only input box whose values can be increased or decreased using a decimal step. The number of decimals (also known as precision) is determined by the step prop.
Use `stringMode` to support high precision decimals support.
`change` will return string value instead. You need polyfill of BigInt if browser not support.
</docs>
<template>
<a-input-number v-model:value="value" :min="0" :max="10" :step="0.1" />
<a-input-number
v-model:value="value"
style="width: 200px"
:min="0"
:max="10"
:step="0.00000000000001"
string-mode
/>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const value = ref<number>();
const value = ref<string>('1');
return {
value,

View File

@ -1,10 +1,14 @@
<template>
<demo-sort>
<basic />
<addonVue />
<size />
<disabled />
<digit />
<formatter />
<keyboardVue />
<outOfRangeVue />
<borderlessVue />
</demo-sort>
</template>
<script lang="ts">
@ -13,7 +17,10 @@ import Disabled from './disabled.vue';
import Digit from './digit.vue';
import Formatter from './formatter.vue';
import Size from './size.vue';
import addonVue from './addon.vue';
import borderlessVue from './borderless.vue';
import keyboardVue from './keyboard.vue';
import outOfRangeVue from './out-of-range.vue';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
import { defineComponent } from 'vue';
@ -27,6 +34,10 @@ export default defineComponent({
Digit,
Formatter,
Size,
addonVue,
borderlessVue,
keyboardVue,
outOfRangeVue,
},
});
</script>

View File

@ -0,0 +1,36 @@
<docs>
---
order: 5
title:
zh-CN: 键盘行为
en-US: Keyboard
---
## zh-CN
使用 `keyboard` 属性可以控制键盘行为
## en-US
Control keyboard behavior by `keyboard`.
</docs>
<template>
<a-space>
<a-input-number v-model:value="value" :keyboard="keyboard" :min="1" :max="10" />
<a-checkbox v-model:checked="keyboard">Toggle keyboard</a-checkbox>
</a-space>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const value = ref<number>(3);
return {
value,
keyboard: ref(true),
};
},
});
</script>

View File

@ -0,0 +1,36 @@
<docs>
---
order: 6
title:
zh-CN: 超出边界
en-US: Out of range
---
## zh-CN
当通过受控将 `value` 超出边界时提供警告样式
## en-US
Show warning style when `value` is out of range by control.
</docs>
<template>
<a-space>
<a-input-number v-model:value="value" :min="1" :max="10" />
<a-button type="primary" @click="value = 99">Reset</a-button>
</a-space>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const value = ref<number>(99);
return {
value,
};
},
});
</script>

View File

@ -1,38 +1,27 @@
import type { PropType, ExtractPropTypes } from 'vue';
import { defineComponent, inject, nextTick, onMounted, ref } from 'vue';
import PropTypes from '../_util/vue-types';
import { getOptionProps } from '../_util/props-util';
import type { PropType, ExtractPropTypes, HTMLAttributes, App } from 'vue';
import { watch, defineComponent, nextTick, onMounted, ref } from 'vue';
import classNames from '../_util/classNames';
import UpOutlined from '@ant-design/icons-vue/UpOutlined';
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
import VcInputNumber from '../vc-input-number/src';
import { defaultConfigProvider } from '../config-provider';
import { tuple, withInstall } from '../_util/type';
import type { EventHandler } from '../_util/EventInterface';
import VcInputNumber, { inputNumberProps as baseInputNumberProps } from './src/InputNumber';
import type { SizeType } from '../config-provider';
import { useInjectFormItemContext } from '../form/FormItemContext';
const inputNumberProps = {
prefixCls: PropTypes.string,
min: PropTypes.number,
max: PropTypes.number,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
step: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).def(1),
defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
disabled: PropTypes.looseBool,
size: PropTypes.oneOf(tuple('large', 'small', 'default')),
formatter: PropTypes.func,
parser: PropTypes.func,
decimalSeparator: PropTypes.string,
placeholder: PropTypes.string,
name: PropTypes.string,
id: PropTypes.string,
precision: PropTypes.number,
autofocus: PropTypes.looseBool,
onPressEnter: {
type: Function as PropType<EventHandler>,
},
onChange: Function as PropType<(num: number) => void>,
import useConfigInject from '../_util/hooks/useConfigInject';
import { cloneElement } from '../_util/vnode';
import omit from '../_util/omit';
import PropTypes from '../_util/vue-types';
import isValidValue from '../_util/isValidValue';
export const inputNumberProps = {
...baseInputNumberProps,
size: { type: String as PropType<SizeType> },
bordered: { type: Boolean, default: true },
placeholder: String,
name: String,
id: String,
type: String,
addonBefore: PropTypes.any,
addonAfter: PropTypes.any,
'update:value': baseInputNumberProps.onChange,
};
export type InputNumberProps = Partial<ExtractPropTypes<typeof inputNumberProps>>;
@ -41,17 +30,33 @@ const InputNumber = defineComponent({
name: 'AInputNumber',
inheritAttrs: false,
props: inputNumberProps,
emits: ['input', 'change', 'blur', 'update:value'],
setup(props, { emit }) {
emits: ['focus', 'blur', 'change', 'input', 'update:value'],
slots: ['addonBefore', 'addonAfter'],
setup(props, { emit, expose, attrs, slots }) {
const formItemContext = useInjectFormItemContext();
const { prefixCls, size, direction } = useConfigInject('input-number', props);
const mergedValue = ref(props.value === undefined ? props.defaultValue : props.value);
watch(
() => props.value,
() => {
mergedValue.value = props.value;
},
);
const inputNumberRef = ref(null);
const focus = () => {
inputNumberRef.value.focus();
inputNumberRef.value?.focus();
};
const blur = () => {
inputNumberRef.value.blur();
inputNumberRef.value?.blur();
};
expose({
focus,
blur,
});
const handleChange = (val: number) => {
if (props.value === undefined) {
mergedValue.value = val;
}
emit('update:value', val);
emit('change', val);
formItemContext.onFieldChange();
@ -60,6 +65,9 @@ const InputNumber = defineComponent({
emit('blur');
formItemContext.onFieldBlur();
};
const handleFocus = () => {
emit('focus');
};
onMounted(() => {
nextTick(() => {
if (process.env.NODE_ENV === 'test') {
@ -69,53 +77,88 @@ const InputNumber = defineComponent({
}
});
});
return {
configProvider: inject('configProvider', defaultConfigProvider),
inputNumberRef,
focus,
blur,
formItemContext,
handleBlur,
handleChange,
};
},
render() {
return () => {
const {
prefixCls: customizePrefixCls,
size,
class: className,
id = this.formItemContext.id.value,
bordered,
readonly,
style,
addonBefore = slots.addonBefore?.(),
addonAfter = slots.addonAfter?.(),
...others
} = {
...getOptionProps(this),
...this.$attrs,
} as any;
const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('input-number', customizePrefixCls);
} = { ...(attrs as HTMLAttributes), ...props };
const preCls = prefixCls.value;
const mergeSize = size.value;
const inputNumberClass = classNames(
{
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
[`${preCls}-lg`]: mergeSize === 'large',
[`${preCls}-sm`]: mergeSize === 'small',
[`${preCls}-rtl`]: direction.value === 'rtl',
[`${preCls}-readonly`]: readonly,
[`${preCls}-borderless`]: !bordered,
},
className,
);
const upIcon = <UpOutlined class={`${prefixCls}-handler-up-inner`} />;
const downIcon = <DownOutlined class={`${prefixCls}-handler-down-inner`} />;
const vcInputNumberProps = {
prefixCls,
upHandler: upIcon,
downHandler: downIcon,
...others,
class: inputNumberClass,
onChange: this.handleChange,
onBlur: this.handleBlur,
id,
const element = (
<VcInputNumber
{...omit(others, ['size', 'defaultValue'])}
ref={inputNumberRef}
value={mergedValue.value}
class={inputNumberClass}
prefixCls={preCls}
readonly={readonly}
onChange={handleChange}
onBlur={handleBlur}
onFocus={handleFocus}
v-slots={{
upHandler: () => <UpOutlined class={`${preCls}-handler-up-inner`} />,
downHandler: () => <DownOutlined class={`${preCls}-handler-down-inner`} />,
}}
/>
);
if (isValidValue(addonBefore) || isValidValue(addonAfter)) {
const wrapperClassName = `${preCls}-group`;
const addonClassName = `${wrapperClassName}-addon`;
const addonBeforeNode = addonBefore ? (
<div class={addonClassName}>{addonBefore}</div>
) : null;
const addonAfterNode = addonAfter ? <div class={addonClassName}>{addonAfter}</div> : null;
const mergedWrapperClassName = classNames(`${preCls}-wrapper`, wrapperClassName, {
[`${wrapperClassName}-rtl`]: direction.value === 'rtl',
});
const mergedGroupClassName = classNames(
`${preCls}-group-wrapper`,
{
[`${preCls}-group-wrapper-sm`]: mergeSize === 'small',
[`${preCls}-group-wrapper-lg`]: mergeSize === 'large',
[`${preCls}-group-wrapper-rtl`]: direction.value === 'rtl',
},
className,
);
return (
<div class={mergedGroupClassName} style={style}>
<div class={mergedWrapperClassName}>
{addonBeforeNode}
{element}
{addonAfterNode}
</div>
</div>
);
}
return cloneElement(element, { style });
};
return <VcInputNumber {...vcInputNumberProps} ref="inputNumberRef" />;
},
});
export default withInstall(InputNumber);
export default Object.assign(InputNumber, {
install: (app: App) => {
app.component(InputNumber.name, InputNumber);
return app;
},
});

View File

@ -0,0 +1,592 @@
// base rc-input-number@7.3.4
import type { DecimalClass, ValueType } from './utils/MiniDecimal';
import getMiniDecimal, {
roundDownUnsignedDecimal,
roundUpUnsignedDecimal,
toFixed,
} from './utils/MiniDecimal';
import StepHandler from './StepHandler';
import { getNumberPrecision, num2str, trimNumber, validateNumber } from './utils/numberUtil';
import useCursor from './hooks/useCursor';
import useFrame from './hooks/useFrame';
import type { HTMLAttributes, PropType } from 'vue';
import { watch, computed, ref, defineComponent } from 'vue';
import type { ChangeEvent, KeyboardEventHandler } from '../../_util/EventInterface';
import KeyCode from '../../_util/KeyCode';
import classNames from '../../_util/classNames';
/**
* We support `stringMode` which need handle correct type when user call in onChange
* format max or min value
* 1. if isInvalid return null
* 2. if precision is undefined, return decimal
* 3. format with precision
* I. if max > 0, round down with precision. Example: max= 3.5, precision=0 afterFormat: 3
* II. if max < 0, round up with precision. Example: max= -3.5, precision=0 afterFormat: -4
* III. if min > 0, round up with precision. Example: min= 3.5, precision=0 afterFormat: 4
* IV. if min < 0, round down with precision. Example: max= -3.5, precision=0 afterFormat: -3
*/
const getDecimalValue = (stringMode: boolean, decimalValue: DecimalClass) => {
if (stringMode || decimalValue.isEmpty()) {
return decimalValue.toString();
}
return decimalValue.toNumber();
};
const getDecimalIfValidate = (value: ValueType, precision: number | undefined, isMax?: boolean) => {
const decimal = getMiniDecimal(value);
if (decimal.isInvalidate()) {
return null;
}
if (precision === undefined) {
return decimal;
}
const { negative, integerStr, decimalStr, negativeStr } = trimNumber(decimal.toString());
const unSignedNumberStr = integerStr + '.' + decimalStr;
if ((isMax && !negative) || (!isMax && negative)) {
return getMiniDecimal(negativeStr + roundDownUnsignedDecimal(unSignedNumberStr, precision));
} else {
return getMiniDecimal(negativeStr + roundUpUnsignedDecimal(unSignedNumberStr, precision));
}
};
export const inputNumberProps = {
/** value will show as string */
stringMode: { type: Boolean as PropType<boolean> },
defaultValue: { type: [String, Number] as PropType<ValueType> },
value: { type: [String, Number] as PropType<ValueType> },
prefixCls: { type: String as PropType<string> },
min: { type: [String, Number] as PropType<ValueType> },
max: { type: [String, Number] as PropType<ValueType> },
step: { type: [String, Number] as PropType<ValueType>, default: 1 },
tabindex: { type: Number as PropType<number> },
controls: { type: Boolean as PropType<boolean>, default: true },
readonly: { type: Boolean as PropType<boolean> },
disabled: { type: Boolean as PropType<boolean> },
autofocus: { type: Boolean as PropType<boolean> },
keyboard: { type: Boolean as PropType<boolean>, default: true },
/** Parse display value to validate number */
parser: { type: Function as PropType<(displayValue: string | undefined) => ValueType> },
/** Transform `value` to display value show in input */
formatter: {
type: Function as PropType<
(value: ValueType | undefined, info: { userTyping: boolean; input: string }) => string
>,
},
/** Syntactic sugar of `formatter`. Config precision of display. */
precision: { type: Number as PropType<number> },
/** Syntactic sugar of `formatter`. Config decimal separator of display. */
decimalSeparator: { type: String as PropType<string> },
onInput: { type: Function as PropType<(text: string) => void> },
onChange: { type: Function as PropType<(value: ValueType) => void> },
onPressEnter: { type: Function as PropType<KeyboardEventHandler> },
onStep: {
type: Function as PropType<
(value: ValueType, info: { offset: ValueType; type: 'up' | 'down' }) => void
>,
},
};
export default defineComponent({
name: 'InnerInputNumber',
inheritAttrs: false,
props: inputNumberProps,
slots: ['upHandler', 'downHandler'],
setup(props, { attrs, slots, emit, expose }) {
const inputRef = ref<HTMLInputElement>();
const focus = ref(false);
const userTypingRef = ref(false);
const compositionRef = ref(false);
const decimalValue = ref(getMiniDecimal(props.value));
function setUncontrolledDecimalValue(newDecimal: DecimalClass) {
if (props.value === undefined) {
decimalValue.value = newDecimal;
}
}
// ====================== Parser & Formatter ======================
/**
* `precision` is used for formatter & onChange.
* It will auto generate by `value` & `step`.
* But it will not block user typing.
*
* Note: Auto generate `precision` is used for legacy logic.
* We should remove this since we already support high precision with BigInt.
*
* @param number Provide which number should calculate precision
* @param userTyping Change by user typing
*/
const getPrecision = (numStr: string, userTyping: boolean) => {
if (userTyping) {
return undefined;
}
if (props.precision >= 0) {
return props.precision;
}
return Math.max(getNumberPrecision(numStr), getNumberPrecision(props.step));
};
// >>> Parser
const mergedParser = (num: string | number) => {
const numStr = String(num);
if (props.parser) {
return props.parser(numStr);
}
let parsedStr = numStr;
if (props.decimalSeparator) {
parsedStr = parsedStr.replace(props.decimalSeparator, '.');
}
// [Legacy] We still support auto convert `$ 123,456` to `123456`
return parsedStr.replace(/[^\w.-]+/g, '');
};
// >>> Formatter
const inputValue = ref<string | number>('');
const mergedFormatter = (number: string, userTyping: boolean) => {
if (props.formatter) {
return props.formatter(number, { userTyping, input: String(inputValue.value) });
}
let str = typeof number === 'number' ? num2str(number) : number;
// User typing will not auto format with precision directly
if (!userTyping) {
const mergedPrecision = getPrecision(str, userTyping);
if (validateNumber(str) && (props.decimalSeparator || mergedPrecision >= 0)) {
// Separator
const separatorStr = props.decimalSeparator || '.';
str = toFixed(str, separatorStr, mergedPrecision);
}
}
return str;
};
// ========================== InputValue ==========================
/**
* Input text value control
*
* User can not update input content directly. It update with follow rules by priority:
* 1. controlled `value` changed
* * [SPECIAL] Typing like `1.` should not immediately convert to `1`
* 2. User typing with format (not precision)
* 3. Blur or Enter trigger revalidate
*/
const initValue = (() => {
const initValue = props.value;
if (decimalValue.value.isInvalidate() && ['string', 'number'].includes(typeof initValue)) {
return Number.isNaN(initValue) ? '' : initValue;
}
return mergedFormatter(decimalValue.value.toString(), false);
})();
inputValue.value = initValue;
// Should always be string
function setInputValue(newValue: DecimalClass, userTyping: boolean) {
inputValue.value = mergedFormatter(
// Invalidate number is sometime passed by external control, we should let it go
// Otherwise is controlled by internal interactive logic which check by userTyping
// You can ref 'show limited value when input is not focused' test for more info.
newValue.isInvalidate() ? newValue.toString(false) : newValue.toString(!userTyping),
userTyping,
);
}
// >>> Max & Min limit
const maxDecimal = computed(() => getDecimalIfValidate(props.max, props.precision, true));
const minDecimal = computed(() => getDecimalIfValidate(props.min, props.precision, false));
const upDisabled = computed(() => {
if (!maxDecimal.value || !decimalValue.value || decimalValue.value.isInvalidate()) {
return false;
}
return maxDecimal.value.lessEquals(decimalValue.value);
});
const downDisabled = computed(() => {
if (!minDecimal.value || !decimalValue.value || decimalValue.value.isInvalidate()) {
return false;
}
return decimalValue.value.lessEquals(minDecimal.value);
});
// Cursor controller
const [recordCursor, restoreCursor] = useCursor(inputRef, focus);
// ============================= Data =============================
/**
* Find target value closet within range.
* e.g. [11, 28]:
* 3 => 11
* 23 => 23
* 99 => 28
*/
const getRangeValue = (target: DecimalClass) => {
// target > max
if (maxDecimal.value && !target.lessEquals(maxDecimal.value)) {
return maxDecimal.value;
}
// target < min
if (minDecimal.value && !minDecimal.value.lessEquals(target)) {
return minDecimal.value;
}
return null;
};
/**
* Check value is in [min, max] range
*/
const isInRange = (target: DecimalClass) => !getRangeValue(target);
/**
* Trigger `onChange` if value validated and not equals of origin.
* Return the value that re-align in range.
*/
const triggerValueUpdate = (newValue: DecimalClass, userTyping: boolean): DecimalClass => {
let updateValue = newValue;
let isRangeValidate = isInRange(updateValue) || updateValue.isEmpty();
// Skip align value when trigger value is empty.
// We just trigger onChange(null)
// This should not block user typing
if (!updateValue.isEmpty() && !userTyping) {
// Revert value in range if needed
updateValue = getRangeValue(updateValue) || updateValue;
isRangeValidate = true;
}
if (!props.readonly && !props.disabled && isRangeValidate) {
const numStr = updateValue.toString();
const mergedPrecision = getPrecision(numStr, userTyping);
if (mergedPrecision >= 0) {
updateValue = getMiniDecimal(toFixed(numStr, '.', mergedPrecision));
}
// Trigger event
if (!updateValue.equals(decimalValue.value)) {
setUncontrolledDecimalValue(updateValue);
props.onChange?.(
updateValue.isEmpty() ? null : getDecimalValue(props.stringMode, updateValue),
);
// Reformat input if value is not controlled
if (props.value === undefined) {
setInputValue(updateValue, userTyping);
}
}
return updateValue;
}
return decimalValue.value;
};
// ========================== User Input ==========================
const onNextPromise = useFrame();
// >>> Collect input value
const collectInputValue = (inputStr: string) => {
recordCursor();
// Update inputValue incase input can not parse as number
inputValue.value = inputStr;
// Parse number
if (!compositionRef.value) {
const finalValue = mergedParser(inputStr);
const finalDecimal = getMiniDecimal(finalValue);
if (!finalDecimal.isNaN()) {
triggerValueUpdate(finalDecimal, true);
}
}
// Trigger onInput later to let user customize value if they want do handle something after onChange
props.onInput?.(inputStr);
// optimize for chinese input experience
// https://github.com/ant-design/ant-design/issues/8196
onNextPromise(() => {
let nextInputStr = inputStr;
if (!props.parser) {
nextInputStr = inputStr.replace(/。/g, '.');
}
if (nextInputStr !== inputStr) {
collectInputValue(nextInputStr);
}
});
};
// >>> Composition
const onCompositionStart = () => {
compositionRef.value = true;
};
const onCompositionEnd = () => {
compositionRef.value = false;
collectInputValue(inputRef.value.value);
};
// >>> Input
const onInternalInput = (e: ChangeEvent) => {
collectInputValue(e.target.value);
};
// ============================= Step =============================
const onInternalStep = (up: boolean) => {
// Ignore step since out of range
if ((up && upDisabled.value) || (!up && downDisabled.value)) {
return;
}
// Clear typing status since it may caused by up & down key.
// We should sync with input value.
userTypingRef.value = false;
let stepDecimal = getMiniDecimal(props.step);
if (!up) {
stepDecimal = stepDecimal.negate();
}
const target = (decimalValue.value || getMiniDecimal(0)).add(stepDecimal.toString());
const updatedValue = triggerValueUpdate(target, false);
props.onStep?.(getDecimalValue(props.stringMode, updatedValue), {
offset: props.step,
type: up ? 'up' : 'down',
});
inputRef.value?.focus();
};
// ============================ Flush =============================
/**
* Flush current input content to trigger value change & re-formatter input if needed
*/
const flushInputValue = (userTyping: boolean) => {
const parsedValue = getMiniDecimal(mergedParser(inputValue.value));
let formatValue: DecimalClass = parsedValue;
if (!parsedValue.isNaN()) {
// Only validate value or empty value can be re-fill to inputValue
// Reassign the formatValue within ranged of trigger control
formatValue = triggerValueUpdate(parsedValue, userTyping);
} else {
formatValue = decimalValue.value;
}
if (props.value !== undefined) {
// Reset back with controlled value first
setInputValue(decimalValue.value, false);
} else if (!formatValue.isNaN()) {
// Reset input back since no validate value
setInputValue(formatValue, false);
}
};
const onKeyDown: KeyboardEventHandler = event => {
const { which } = event;
userTypingRef.value = true;
if (which === KeyCode.ENTER) {
if (!compositionRef.value) {
userTypingRef.value = false;
}
flushInputValue(false);
props.onPressEnter?.(event);
}
if (props.keyboard === false) {
return;
}
// Do step
if (!compositionRef.value && [KeyCode.UP, KeyCode.DOWN].includes(which)) {
onInternalStep(KeyCode.UP === which);
event.preventDefault();
}
};
const onKeyUp = () => {
userTypingRef.value = false;
};
// >>> Focus & Blur
const onBlur = () => {
flushInputValue(false);
focus.value = false;
userTypingRef.value = false;
emit('blur');
};
// ========================== Controlled ==========================
// Input by precision
watch(
() => props.precision,
() => {
if (!decimalValue.value.isInvalidate()) {
setInputValue(decimalValue.value, false);
}
},
{ flush: 'post' },
);
// Input by value
watch(
() => props.value,
() => {
const newValue = getMiniDecimal(props.value);
decimalValue.value = newValue;
const currentParsedValue = getMiniDecimal(mergedParser(inputValue.value));
// When user typing from `1.2` to `1.`, we should not convert to `1` immediately.
// But let it go if user set `formatter`
if (!newValue.equals(currentParsedValue) || !userTypingRef.value || props.formatter) {
// Update value as effect
setInputValue(newValue, userTypingRef.value);
}
},
{ flush: 'post' },
);
// ============================ Cursor ============================
watch(
inputValue,
() => {
if (props.formatter) {
restoreCursor();
}
},
{ flush: 'post' },
);
watch(
() => props.disabled,
val => {
if (val) {
focus.value = false;
}
},
);
expose({
focus: () => {
inputRef.value?.focus();
},
blur: () => {
inputRef.value?.blur();
},
});
return () => {
const {
prefixCls = 'rc-input-number',
min,
max,
step = 1,
defaultValue,
value,
disabled,
readonly,
keyboard,
controls = true,
autofocus,
stringMode,
parser,
formatter,
precision,
decimalSeparator,
onChange,
onInput,
onPressEnter,
onStep,
class: className,
style,
...inputProps
} = { ...(attrs as HTMLAttributes), ...props };
const { upHandler, downHandler } = slots;
const inputClassName = `${prefixCls}-input`;
return (
<div
class={classNames(prefixCls, className, {
[`${prefixCls}-focused`]: focus.value,
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-readonly`]: readonly,
[`${prefixCls}-not-a-number`]: decimalValue.value.isNaN(),
[`${prefixCls}-out-of-range`]:
!decimalValue.value.isInvalidate() && !isInRange(decimalValue.value),
})}
style={style}
onKeydown={onKeyDown}
onKeyup={onKeyUp}
>
{controls && (
<StepHandler
prefixCls={prefixCls}
upDisabled={upDisabled.value}
downDisabled={downDisabled.value}
onStep={onInternalStep}
v-slots={{ upNode: upHandler, downNode: downHandler }}
/>
)}
<div class={`${inputClassName}-wrap`}>
<input
autofocus={autofocus}
autocomplete="off"
role="spinbutton"
aria-valuemin={min as any}
aria-valuemax={max as any}
aria-valuenow={
decimalValue.value.isInvalidate() ? null : (decimalValue.value.toString() as any)
}
step={step}
{...inputProps}
ref={inputRef}
class={inputClassName}
value={inputValue.value}
disabled={disabled}
readonly={readonly}
onFocus={() => {
focus.value = true;
emit('focus');
}}
onInput={onInternalInput}
onBlur={onBlur}
onCompositionstart={onCompositionStart}
onCompositionend={onCompositionEnd}
/>
</div>
</div>
);
};
},
});

View File

@ -0,0 +1,103 @@
import isMobile from '../../vc-util/isMobile';
import type { PropType } from 'vue';
import { onBeforeUnmount, ref, defineComponent } from 'vue';
import classNames from '../../_util/classNames';
/**
* When click and hold on a button - the speed of auto changing the value.
*/
const STEP_INTERVAL = 200;
/**
* When click and hold on a button - the delay before auto changing the value.
*/
const STEP_DELAY = 600;
export default defineComponent({
name: 'StepHandler',
inheritAttrs: false,
props: {
prefixCls: String,
upDisabled: Boolean,
downDisabled: Boolean,
onStep: { type: Function as PropType<(up: boolean) => void> },
},
slots: ['upNode', 'downNode'],
setup(props, { slots, emit }) {
const stepTimeoutRef = ref();
// We will interval update step when hold mouse down
const onStepMouseDown = (e: MouseEvent, up: boolean) => {
e.preventDefault();
emit('step', up);
// Loop step for interval
function loopStep() {
emit('step', up);
stepTimeoutRef.value = setTimeout(loopStep, STEP_INTERVAL);
}
// First time press will wait some time to trigger loop step update
stepTimeoutRef.value = setTimeout(loopStep, STEP_DELAY);
};
const onStopStep = () => {
clearTimeout(stepTimeoutRef.value);
};
onBeforeUnmount(() => {
onStopStep();
});
return () => {
if (isMobile()) {
return null;
}
const { prefixCls, upDisabled, downDisabled } = props;
const handlerClassName = `${prefixCls}-handler`;
const upClassName = classNames(handlerClassName, `${handlerClassName}-up`, {
[`${handlerClassName}-up-disabled`]: upDisabled,
});
const downClassName = classNames(handlerClassName, `${handlerClassName}-down`, {
[`${handlerClassName}-down-disabled`]: downDisabled,
});
const sharedHandlerProps = {
unselectable: 'on' as const,
role: 'button',
onMouseup: onStopStep,
onMouseleave: onStopStep,
};
const { upNode, downNode } = slots;
return (
<div class={`${handlerClassName}-wrap`}>
<span
{...sharedHandlerProps}
onMousedown={e => {
onStepMouseDown(e, true);
}}
aria-label="Increase Value"
aria-disabled={upDisabled}
class={upClassName}
>
{upNode?.() || <span unselectable="on" class={`${prefixCls}-handler-up-inner`} />}
</span>
<span
{...sharedHandlerProps}
onMousedown={e => {
onStepMouseDown(e, false);
}}
aria-label="Decrease Value"
aria-disabled={downDisabled}
class={downClassName}
>
{downNode?.() || <span unselectable="on" class={`${prefixCls}-handler-down-inner`} />}
</span>
</div>
);
};
},
});

View File

@ -0,0 +1,78 @@
import { warning } from '../../../vc-util/warning';
import type { Ref } from 'vue';
import { ref } from 'vue';
/**
* Keep input cursor in the correct position if possible.
* Is this necessary since we have `formatter` which may mass the content?
*/
export default function useCursor(
inputRef: Ref<HTMLInputElement>,
focused: Ref<boolean>,
): [() => void, () => void] {
const selectionRef = ref<{
start?: number;
end?: number;
value?: string;
beforeTxt?: string;
afterTxt?: string;
}>(null);
function recordCursor() {
// Record position
try {
const { selectionStart: start, selectionEnd: end, value } = inputRef.value;
const beforeTxt = value.substring(0, start);
const afterTxt = value.substring(end);
selectionRef.value = {
start,
end,
value,
beforeTxt,
afterTxt,
};
} catch (e) {
// Fix error in Chrome:
// Failed to read the 'selectionStart' property from 'HTMLInputElement'
// http://stackoverflow.com/q/21177489/3040605
}
}
/**
* Restore logic:
* 1. back string same
* 2. start string same
*/
function restoreCursor() {
if (inputRef.value && selectionRef.value && focused.value) {
try {
const { value } = inputRef.value;
const { beforeTxt, afterTxt, start } = selectionRef.value;
let startPos = value.length;
if (value.endsWith(afterTxt)) {
startPos = value.length - selectionRef.value.afterTxt.length;
} else if (value.startsWith(beforeTxt)) {
startPos = beforeTxt.length;
} else {
const beforeLastChar = beforeTxt[start - 1];
const newIndex = value.indexOf(beforeLastChar, start - 1);
if (newIndex !== -1) {
startPos = newIndex + 1;
}
}
inputRef.value.setSelectionRange(startPos, startPos);
} catch (e) {
warning(
false,
`Something warning of cursor restore. Please fire issue about this: ${e.message}`,
);
}
}
}
return [recordCursor, restoreCursor];
}

View File

@ -0,0 +1,25 @@
import raf from '../../../_util/raf';
import { onBeforeUnmount, ref } from 'vue';
/**
* Always trigger latest once when call multiple time
*/
export default () => {
const idRef = ref(0);
const cleanUp = () => {
raf.cancel(idRef.value);
};
onBeforeUnmount(() => {
cleanUp();
});
return (callback: () => void) => {
cleanUp();
idRef.value = raf(() => {
callback();
});
};
};

View File

@ -0,0 +1,327 @@
/* eslint-disable max-classes-per-file */
import { getNumberPrecision, isE, num2str, trimNumber, validateNumber } from './numberUtil';
import { supportBigInt } from './supportUtil';
export type ValueType = string | number;
export interface DecimalClass {
add: (value: ValueType) => DecimalClass;
isEmpty: () => boolean;
isNaN: () => boolean;
isInvalidate: () => boolean;
toNumber: () => number;
/**
* Parse value as string. Will return empty string if `isInvalidate`.
* You can set `safe=false` to get origin string content.
*/
toString: (safe?: boolean) => string;
equals: (target: DecimalClass) => boolean;
lessEquals: (target: DecimalClass) => boolean;
negate: () => DecimalClass;
}
/**
* We can remove this when IE not support anymore
*/
export class NumberDecimal implements DecimalClass {
origin = '';
number: number;
empty: boolean;
constructor(value: ValueType) {
if ((!value && value !== 0) || !String(value).trim()) {
this.empty = true;
return;
}
this.origin = String(value);
this.number = Number(value);
}
negate() {
return new NumberDecimal(-this.toNumber());
}
add(value: ValueType) {
if (this.isInvalidate()) {
return new NumberDecimal(value);
}
const target = Number(value);
if (Number.isNaN(target)) {
return this;
}
const number = this.number + target;
// [Legacy] Back to safe integer
if (number > Number.MAX_SAFE_INTEGER) {
return new NumberDecimal(Number.MAX_SAFE_INTEGER);
}
if (number < Number.MIN_SAFE_INTEGER) {
return new NumberDecimal(Number.MIN_SAFE_INTEGER);
}
const maxPrecision = Math.max(getNumberPrecision(this.number), getNumberPrecision(target));
return new NumberDecimal(number.toFixed(maxPrecision));
}
isEmpty() {
return this.empty;
}
isNaN() {
return Number.isNaN(this.number);
}
isInvalidate() {
return this.isEmpty() || this.isNaN();
}
equals(target: DecimalClass) {
return this.toNumber() === target?.toNumber();
}
lessEquals(target: DecimalClass) {
return this.add(target.negate().toString()).toNumber() <= 0;
}
toNumber() {
return this.number;
}
toString(safe = true) {
if (!safe) {
return this.origin;
}
if (this.isInvalidate()) {
return '';
}
return num2str(this.number);
}
}
export class BigIntDecimal implements DecimalClass {
origin = '';
negative: boolean;
integer: bigint;
decimal: bigint;
/** BigInt will convert `0009` to `9`. We need record the len of decimal */
decimalLen: number;
empty: boolean;
nan: boolean;
constructor(value: string | number) {
if ((!value && value !== 0) || !String(value).trim()) {
this.empty = true;
return;
}
this.origin = String(value);
// Act like Number convert
if (value === '-') {
this.nan = true;
return;
}
let mergedValue = value;
// We need convert back to Number since it require `toFixed` to handle this
if (isE(mergedValue)) {
mergedValue = Number(mergedValue);
}
mergedValue = typeof mergedValue === 'string' ? mergedValue : num2str(mergedValue);
if (validateNumber(mergedValue)) {
const trimRet = trimNumber(mergedValue);
this.negative = trimRet.negative;
const numbers = trimRet.trimStr.split('.');
this.integer = BigInt(numbers[0]);
const decimalStr = numbers[1] || '0';
this.decimal = BigInt(decimalStr);
this.decimalLen = decimalStr.length;
} else {
this.nan = true;
}
}
private getMark() {
return this.negative ? '-' : '';
}
private getIntegerStr() {
return this.integer.toString();
}
private getDecimalStr() {
return this.decimal.toString().padStart(this.decimalLen, '0');
}
/**
* Align BigIntDecimal with same decimal length. e.g. 12.3 + 5 = 1230000
* This is used for add function only.
*/
private alignDecimal(decimalLength: number): bigint {
const str = `${this.getMark()}${this.getIntegerStr()}${this.getDecimalStr().padEnd(
decimalLength,
'0',
)}`;
return BigInt(str);
}
negate() {
const clone = new BigIntDecimal(this.toString());
clone.negative = !clone.negative;
return clone;
}
add(value: ValueType): BigIntDecimal {
if (this.isInvalidate()) {
return new BigIntDecimal(value);
}
const offset = new BigIntDecimal(value);
if (offset.isInvalidate()) {
return this;
}
const maxDecimalLength = Math.max(this.getDecimalStr().length, offset.getDecimalStr().length);
const myAlignedDecimal = this.alignDecimal(maxDecimalLength);
const offsetAlignedDecimal = offset.alignDecimal(maxDecimalLength);
const valueStr = (myAlignedDecimal + offsetAlignedDecimal).toString();
// We need fill string length back to `maxDecimalLength` to avoid parser failed
const { negativeStr, trimStr } = trimNumber(valueStr);
const hydrateValueStr = `${negativeStr}${trimStr.padStart(maxDecimalLength + 1, '0')}`;
return new BigIntDecimal(
`${hydrateValueStr.slice(0, -maxDecimalLength)}.${hydrateValueStr.slice(-maxDecimalLength)}`,
);
}
isEmpty() {
return this.empty;
}
isNaN() {
return this.nan;
}
isInvalidate() {
return this.isEmpty() || this.isNaN();
}
equals(target: DecimalClass) {
return this.toString() === target?.toString();
}
lessEquals(target: DecimalClass) {
return this.add(target.negate().toString()).toNumber() <= 0;
}
toNumber() {
if (this.isNaN()) {
return NaN;
}
return Number(this.toString());
}
toString(safe = true) {
if (!safe) {
return this.origin;
}
if (this.isInvalidate()) {
return '';
}
return trimNumber(`${this.getMark()}${this.getIntegerStr()}.${this.getDecimalStr()}`).fullStr;
}
}
export default function getMiniDecimal(value: ValueType): DecimalClass {
// We use BigInt here.
// Will fallback to Number if not support.
if (supportBigInt()) {
return new BigIntDecimal(value);
}
return new NumberDecimal(value);
}
/**
* round up an unsigned number str, like: 1.4 -> 2, 1.5 -> 2
*/
export function roundUpUnsignedDecimal(numStr: string, precision: number) {
const { integerStr, decimalStr } = trimNumber(numStr);
const advancedDecimal = getMiniDecimal(integerStr + '.' + decimalStr).add(
`0.${'0'.repeat(precision)}${5}`,
);
return toFixed(advancedDecimal.toString(), '.', precision);
}
/**
* round up an unsigned number str, like: 1.4 -> 1, 1.5 -> 1
*/
export function roundDownUnsignedDecimal(numStr: string, precision: number) {
const { negativeStr, integerStr, decimalStr } = trimNumber(numStr);
const numberWithoutDecimal = `${negativeStr}${integerStr}`;
if (precision === 0) {
return integerStr;
}
return `${numberWithoutDecimal}.${decimalStr.padEnd(precision, '0').slice(0, precision)}`;
}
/**
* Align the logic of toFixed to around like 1.5 => 2
*/
export function toFixed(numStr: string, separatorStr: string, precision?: number) {
if (numStr === '') {
return '';
}
const { negativeStr, integerStr, decimalStr } = trimNumber(numStr);
const precisionDecimalStr = `${separatorStr}${decimalStr}`;
const numberWithoutDecimal = `${negativeStr}${integerStr}`;
if (precision >= 0) {
// We will get last + 1 number to check if need advanced number
const advancedNum = Number(decimalStr[precision]);
if (advancedNum >= 5) {
const advancedDecimal = getMiniDecimal(numStr).add(
`${negativeStr}0.${'0'.repeat(precision)}${10 - advancedNum}`,
);
return toFixed(advancedDecimal.toString(), separatorStr, precision);
}
if (precision === 0) {
return numberWithoutDecimal;
}
return `${numberWithoutDecimal}${separatorStr}${decimalStr
.padEnd(precision, '0')
.slice(0, precision)}`;
}
if (precisionDecimalStr === '.0') {
return numberWithoutDecimal;
}
return `${numberWithoutDecimal}${precisionDecimalStr}`;
}

View File

@ -0,0 +1,115 @@
import { supportBigInt } from './supportUtil';
/**
* Format string number to readable number
*/
export function trimNumber(numStr: string) {
let str = numStr.trim();
let negative = str.startsWith('-');
if (negative) {
str = str.slice(1);
}
str = str
// Remove decimal 0. `1.000` => `1.`, `1.100` => `1.1`
.replace(/(\.\d*[^0])0*$/, '$1')
// Remove useless decimal. `1.` => `1`
.replace(/\.0*$/, '')
// Remove integer 0. `0001` => `1`, 000.1' => `.1`
.replace(/^0+/, '');
if (str.startsWith('.')) {
str = `0${str}`;
}
const trimStr = str || '0';
const splitNumber = trimStr.split('.');
const integerStr = splitNumber[0] || '0';
const decimalStr = splitNumber[1] || '0';
if (integerStr === '0' && decimalStr === '0') {
negative = false;
}
const negativeStr = negative ? '-' : '';
return {
negative,
negativeStr,
trimStr,
integerStr,
decimalStr,
fullStr: `${negativeStr}${trimStr}`,
};
}
export function isE(number: string | number) {
const str = String(number);
return !Number.isNaN(Number(str)) && str.includes('e');
}
/**
* [Legacy] Convert 1e-9 to 0.000000001.
* This may lose some precision if user really want 1e-9.
*/
export function getNumberPrecision(number: string | number) {
const numStr = String(number);
if (isE(number)) {
let precision = Number(numStr.slice(numStr.indexOf('e-') + 2));
const decimalMatch = numStr.match(/\.(\d+)/);
if (decimalMatch?.[1]) {
precision += decimalMatch[1].length;
}
return precision;
}
return numStr.includes('.') && validateNumber(numStr)
? numStr.length - numStr.indexOf('.') - 1
: 0;
}
/**
* Convert number (includes scientific notation) to -xxx.yyy format
*/
export function num2str(number: number): string {
let numStr = String(number);
if (isE(number)) {
if (number > Number.MAX_SAFE_INTEGER) {
return String(supportBigInt() ? BigInt(number).toString() : Number.MAX_SAFE_INTEGER);
}
if (number < Number.MIN_SAFE_INTEGER) {
return String(supportBigInt() ? BigInt(number).toString() : Number.MIN_SAFE_INTEGER);
}
numStr = number.toFixed(getNumberPrecision(numStr));
}
return trimNumber(numStr).fullStr;
}
export function validateNumber(num: string | number) {
if (typeof num === 'number') {
return !Number.isNaN(num);
}
// Empty
if (!num) {
return false;
}
return (
// Normal type: 11.28
/^\s*-?\d+(\.\d+)?\s*$/.test(num) ||
// Pre-number: 1.
/^\s*-?\d+\.\s*$/.test(num) ||
// Post-number: .1
/^\s*-?\.\d+\s*$/.test(num)
);
}

View File

@ -0,0 +1,3 @@
export function supportBigInt() {
return typeof BigInt === 'function';
}

View File

@ -3,11 +3,24 @@
@import '../../input/style/mixin';
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
@form-item-prefix-cls: ~'@{ant-prefix}-form-item';
.@{input-number-prefix-cls} {
.reset-component();
.input();
//== Style for input-group: input with label, with button or dropdown...
&-group {
.reset-component();
.input-group(~'@{input-number-prefix-cls}');
&-wrapper {
display: inline-block;
text-align: start;
vertical-align: top; // https://github.com/ant-design/ant-design/issues/6403
}
}
display: inline-block;
width: 90px;
margin: 0;
@ -25,10 +38,13 @@
font-weight: bold;
line-height: 0;
text-align: center;
border-left: @border-width-base @border-style-base @input-number-handler-border-color;
transition: all 0.1s linear;
&:active {
background: @input-number-handler-active-bg;
}
&:hover &-up-inner,
&:hover &-down-inner {
color: @input-number-handler-hover-bg;
@ -51,6 +67,10 @@
&:hover {
.hover(@input-number-hover-border-color);
& + .@{form-item-prefix-cls}-children-icon {
opacity: 0;
transition: opacity 0.24s linear 0.24s;
}
}
&-focused {
@ -67,6 +87,12 @@
}
}
&-readonly {
.@{input-number-prefix-cls}-handler-wrap {
display: none;
}
}
&-input {
width: 100%;
height: @input-height-base - 2px;
@ -77,13 +103,15 @@
border-radius: @border-radius-base;
outline: 0;
transition: all 0.3s linear;
-moz-appearance: textfield !important;
appearance: textfield !important;
.placeholder();
&[type='number']::-webkit-inner-spin-button,
&[type='number']::-webkit-outer-spin-button {
margin: 0;
/* stylelint-disable-next-line property-no-vendor-prefix */
-webkit-appearance: none;
appearance: none;
}
}
@ -112,7 +140,6 @@
width: 22px;
height: 100%;
background: @input-number-handler-bg;
border-left: @border-width-base @border-style-base @input-number-handler-border-color;
border-radius: 0 @border-radius-base @border-radius-base 0;
opacity: 0;
transition: opacity 0.24s linear 0.1s;
@ -123,30 +150,39 @@
.@{input-number-prefix-cls}-handler {
.@{input-number-prefix-cls}-handler-up-inner,
.@{input-number-prefix-cls}-handler-down-inner {
.iconfont-size-under-12px(7px);
display: flex;
align-items: center;
justify-content: center;
min-width: auto;
margin-right: 0;
font-size: 7px;
}
}
.@{input-number-prefix-cls}-borderless & {
border-left-width: 0;
}
}
&-handler-wrap:hover &-handler {
height: 40%;
}
&:hover &-handler-wrap {
&:hover &-handler-wrap,
&-focused &-handler-wrap {
opacity: 1;
}
&-handler-up {
border-top-right-radius: @border-radius-base;
cursor: pointer;
&-inner {
top: 50%;
margin-top: -5px;
text-align: center;
}
&:hover {
height: 60% !important;
}
@ -157,14 +193,19 @@
border-top: @border-width-base @border-style-base @border-color-base;
border-bottom-right-radius: @border-radius-base;
cursor: pointer;
&-inner {
top: 50%;
margin-top: -6px;
text-align: center;
transform: translateY(-50%);
}
&:hover {
height: 60% !important;
}
.@{input-number-prefix-cls}-borderless & {
border-top-width: 0;
}
}
&-handler-up-disabled,
@ -176,4 +217,17 @@
&-handler-down-disabled:hover &-handler-down-inner {
color: @disabled-color;
}
&-borderless {
box-shadow: none;
}
// ===================== Out Of Range =====================
&-out-of-range {
input {
color: @error-color;
}
}
}
@import './rtl';

View File

@ -0,0 +1,48 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
.@{input-number-prefix-cls} {
&-rtl {
direction: rtl;
}
&-handler {
.@{input-number-prefix-cls}-rtl & {
border-right: @border-width-base @border-style-base @input-number-handler-border-color;
border-left: 0;
}
&-wrap {
.@{input-number-prefix-cls}-rtl & {
right: auto;
left: 0;
}
.@{input-number-prefix-cls}-rtl.@{input-number-prefix-cls}-borderless & {
border-right-width: 0;
}
}
&-up {
.@{input-number-prefix-cls}-rtl & {
border-top-right-radius: 0;
}
}
&-down {
.@{input-number-prefix-cls}-rtl & {
border-bottom-right-radius: 0;
}
}
}
&-input {
.@{input-number-prefix-cls}-rtl & {
direction: ltr;
text-align: right;
}
}
}

View File

@ -1,120 +0,0 @@
@inputNumberPrefixCls: rc-input-number;
.@{inputNumberPrefixCls} {
margin: 0;
padding: 0;
line-height: 26px;
font-size: 12px;
height: 26px;
display: inline-block;
vertical-align: middle;
border: 1px solid #d9d9d9;
border-radius: 5px;
box-sizing: content-box;
&-handler {
text-align: center;
line-height: 12px;
height: 12px;
overflow: hidden;
display: block;
touch-action: none;
&-active {
background: #ddd;
}
}
&-handler-up-inner,
&-handler-down-inner {
color: #666666;
user-select: none;
-webkit-user-select: none;
}
&:hover {
border-color: #23c0fa;
.@{inputNumberPrefixCls}-handler-up,
.@{inputNumberPrefixCls}-handler-wrap {
border-color: #23c0fa;
}
}
&-disabled:hover {
border-color: #d9d9d9;
.@{inputNumberPrefixCls}-handler-up,
.@{inputNumberPrefixCls}-handler-wrap {
border-color: #d9d9d9;
}
}
&-input-wrap {
overflow: hidden;
height: 26px;
}
&-input {
width: 100%;
text-align: center;
outline: 0;
-moz-appearance: textfield;
line-height: 26px;
height: 26px;
transition: all 0.3s ease;
color: #666666;
border: 0;
border-radius: 5px;
padding: 0;
}
&-handler-wrap {
float: right;
border-left: 1px solid #d9d9d9;
width: 20px;
height: 26px;
}
&-handler-up {
border-bottom: 1px solid #d9d9d9;
padding-top: 1px;
&-inner {
&:after {
content: '+';
}
}
}
&-handler-down {
&-inner {
&:after {
content: '-';
}
}
}
.handler-disabled() {
opacity: 0.72;
&:hover {
color: #999;
border-color: #d9d9d9;
}
}
&-handler-down-disabled,
&-handler-up-disabled {
.handler-disabled();
}
&-disabled {
.@{inputNumberPrefixCls}-input {
opacity: 0.72;
cursor: not-allowed;
background-color: #f3f3f3;
}
.@{inputNumberPrefixCls}-handler {
.handler-disabled();
}
}
}

View File

@ -1,26 +0,0 @@
import PropTypes from '../../_util/vue-types';
import Touchable from '../../vc-m-feedback';
import { getSlot } from '../../_util/props-util';
const InputHandler = {
name: 'InputHandler',
inheritAttrs: false,
props: {
prefixCls: PropTypes.string,
disabled: PropTypes.looseBool,
},
render() {
const { prefixCls, disabled } = this.$props;
const touchableProps = {
disabled,
activeClassName: `${prefixCls}-handler-active`,
};
return (
<Touchable {...touchableProps}>
<span {...this.$attrs}>{getSlot(this)}</span>
</Touchable>
);
},
};
export default InputHandler;

View File

@ -1,816 +0,0 @@
// based on rc-input-number 4.5.5
import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin';
import { initDefaultProps, hasProp, getOptionProps } from '../../_util/props-util';
import classNames from '../../_util/classNames';
import KeyCode from '../../_util/KeyCode';
import InputHandler from './InputHandler';
import { defineComponent } from 'vue';
import supportsPassive from '../../_util/supportsPassive';
function preventDefault(e) {
e.preventDefault();
}
function defaultParser(input) {
return input.replace(/[^\w\.-]+/g, '');
}
/**
* When click and hold on a button - the speed of auto changin the value.
*/
const SPEED = 200;
/**
* When click and hold on a button - the delay before auto changin the value.
*/
const DELAY = 600;
/**
* Max Safe Integer -- on IE this is not available, so manually set the number in that case.
* The reason this is used, instead of Infinity is because numbers above the MSI are unstable
*/
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1;
const isValidProps = value => value !== undefined && value !== null;
const isEqual = (oldValue, newValue) =>
newValue === oldValue ||
(typeof newValue === 'number' &&
typeof oldValue === 'number' &&
isNaN(newValue) &&
isNaN(oldValue));
const inputNumberProps = {
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
focusOnUpDown: PropTypes.looseBool,
autofocus: PropTypes.looseBool,
// onChange: PropTypes.func,
// onKeyDown: PropTypes.func,
// onKeyUp: PropTypes.func,
prefixCls: PropTypes.string,
tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
placeholder: PropTypes.string,
disabled: PropTypes.looseBool,
// onFocus: PropTypes.func,
// onBlur: PropTypes.func,
readonly: PropTypes.looseBool,
max: PropTypes.number,
min: PropTypes.number,
step: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
upHandler: PropTypes.any,
downHandler: PropTypes.any,
useTouch: PropTypes.looseBool,
formatter: PropTypes.func,
parser: PropTypes.func,
// onMouseEnter: PropTypes.func,
// onMouseLeave: PropTypes.func,
// onMouseOver: PropTypes.func,
// onMouseOut: PropTypes.func,
precision: PropTypes.number,
required: PropTypes.looseBool,
pattern: PropTypes.string,
decimalSeparator: PropTypes.string,
autocomplete: PropTypes.string,
title: PropTypes.string,
name: PropTypes.string,
id: PropTypes.string,
type: PropTypes.string,
maxlength: PropTypes.any,
};
export default defineComponent({
name: 'VCInputNumber',
mixins: [BaseMixin],
inheritAttrs: false,
// model: {
// prop: 'value',
// event: 'change',
// },
props: initDefaultProps(inputNumberProps, {
focusOnUpDown: true,
useTouch: false,
prefixCls: 'rc-input-number',
min: -MAX_SAFE_INTEGER,
step: 1,
parser: defaultParser,
required: false,
autocomplete: 'off',
}),
data() {
const props = getOptionProps(this);
this.prevProps = { ...props };
let value;
if ('value' in props) {
value = this.value;
} else {
value = this.defaultValue;
}
const validValue = this.getValidValue(this.toNumber(value));
return {
inputValue: this.toPrecisionAsStep(validValue),
sValue: validValue,
focused: this.autofocus,
};
},
mounted() {
this.$nextTick(() => {
this.updatedFunc();
});
},
updated() {
const { value, max, min } = this.$props;
const { focused } = this.$data;
const { prevProps } = this;
const props = getOptionProps(this);
// Don't trigger in componentDidMount
if (prevProps) {
if (
!isEqual(prevProps.value, value) ||
!isEqual(prevProps.max, max) ||
!isEqual(prevProps.min, min)
) {
const validValue = focused ? value : this.getValidValue(value);
let nextInputValue;
if (this.pressingUpOrDown) {
nextInputValue = validValue;
} else if (this.inputting) {
nextInputValue = this.rawInput;
} else {
nextInputValue = this.toPrecisionAsStep(validValue);
}
this.setState({
// eslint-disable-line
sValue: validValue,
inputValue: nextInputValue,
});
}
// Trigger onChange when max or min change
// https://github.com/ant-design/ant-design/issues/11574
const nextValue = 'value' in props ? value : this.$data.sValue;
// ref: null < 20 === true
// https://github.com/ant-design/ant-design/issues/14277
if (
'max' in props &&
prevProps.max !== max &&
typeof nextValue === 'number' &&
nextValue > max
) {
this.__emit('change', max);
}
if (
'min' in props &&
prevProps.min !== min &&
typeof nextValue === 'number' &&
nextValue < min
) {
this.__emit('change', min);
}
}
this.prevProps = { ...props };
this.$nextTick(() => {
this.updatedFunc();
});
},
beforeUnmount() {
this.stop();
},
methods: {
updatedFunc() {
const inputElem = this.inputRef;
// Restore cursor
try {
// Firefox set the input cursor after it get focused.
// This caused that if an input didn't init with the selection,
// set will cause cursor not correct when first focus.
// Safari will focus input if set selection. We need skip this.
if (this.cursorStart !== undefined && this.$data.focused) {
// In most cases, the string after cursor is stable.
// We can move the cursor before it
if (
// If not match full str, try to match part of str
!this.partRestoreByAfter(this.cursorAfter) &&
this.$data.sValue !== this.value
) {
// If not match any of then, let's just keep the position
// TODO: Logic should not reach here, need check if happens
let pos = this.cursorStart + 1;
// If not have last string, just position to the end
if (!this.cursorAfter) {
pos = inputElem.value.length;
} else if (this.lastKeyCode === KeyCode.BACKSPACE) {
pos = this.cursorStart - 1;
} else if (this.lastKeyCode === KeyCode.DELETE) {
pos = this.cursorStart;
}
this.fixCaret(pos, pos);
} else if (this.currentValue === inputElem.value) {
// Handle some special key code
switch (this.lastKeyCode) {
case KeyCode.BACKSPACE:
this.fixCaret(this.cursorStart - 1, this.cursorStart - 1);
break;
case KeyCode.DELETE:
this.fixCaret(this.cursorStart + 1, this.cursorStart + 1);
break;
default:
// Do nothing
}
}
}
} catch (e) {
// Do nothing
}
// Reset last key
this.lastKeyCode = null;
// pressingUpOrDown is true means that someone just click up or down button
if (!this.pressingUpOrDown) {
return;
}
if (this.focusOnUpDown && this.$data.focused) {
if (document.activeElement !== inputElem) {
this.focus();
}
}
this.pressingUpOrDown = false;
},
onKeyDown(e, ...args) {
if (e.keyCode === KeyCode.UP) {
const ratio = this.getRatio(e);
this.up(e, ratio);
this.stop();
} else if (e.keyCode === KeyCode.DOWN) {
const ratio = this.getRatio(e);
this.down(e, ratio);
this.stop();
} else if (e.keyCode === KeyCode.ENTER) {
this.__emit('pressEnter', e);
}
// Trigger user key down
this.recordCursorPosition();
this.lastKeyCode = e.keyCode;
this.__emit('keydown', e, ...args);
},
onKeyUp(e, ...args) {
this.stop();
this.recordCursorPosition();
this.__emit('keyup', e, ...args);
},
onTrigger(e) {
if (e.target.composing) return false;
this.onChange(e);
},
onChange(e) {
if (this.$data.focused) {
this.inputting = true;
}
this.rawInput = this.parser(this.getValueFromEvent(e));
this.setState({ inputValue: this.rawInput });
const num = this.toNumber(this.rawInput); // valid number or invalid string
this.__emit('change', num);
},
onFocus(...args) {
this.setState({
focused: true,
});
this.__emit('focus', ...args);
},
onBlur(...args) {
this.inputting = false;
this.setState({
focused: false,
});
const value = this.getCurrentValidValue(this.$data.inputValue);
const newValue = this.setValue(value);
if (this.$attrs.onBlur && this.inputRef) {
const originValue = this.inputRef.value;
const inputValue = this.getInputDisplayValue({ focused: false, sValue: newValue });
this.inputRef.value = inputValue;
this.__emit('blur', ...args);
this.inputRef.value = originValue;
}
},
getCurrentValidValue(value) {
let val = value;
if (val === '') {
val = '';
} else if (!this.isNotCompleteNumber(parseFloat(val, 10))) {
val = this.getValidValue(val);
} else {
val = this.$data.sValue;
}
return this.toNumber(val);
},
getRatio(e) {
let ratio = 1;
if (e.metaKey || e.ctrlKey) {
ratio = 0.1;
} else if (e.shiftKey) {
ratio = 10;
}
return ratio;
},
getValueFromEvent(e) {
// optimize for chinese input expierence
// https://github.com/ant-design/ant-design/issues/8196
let value = e.target.value.trim().replace(/。/g, '.');
if (isValidProps(this.decimalSeparator)) {
value = value.replace(this.decimalSeparator, '.');
}
return value;
},
getValidValue(value, min = this.min, max = this.max) {
let val = parseFloat(value, 10);
// https://github.com/ant-design/ant-design/issues/7358
if (isNaN(val)) {
return value;
}
if (val < min) {
val = min;
}
if (val > max) {
val = max;
}
return val;
},
setValue(v, callback) {
// trigger onChange
const { precision } = this.$props;
const newValue = this.isNotCompleteNumber(parseFloat(v, 10)) ? null : parseFloat(v, 10);
const { sValue: value = null, inputValue = null } = this.$data;
// https://github.com/ant-design/ant-design/issues/7363
// https://github.com/ant-design/ant-design/issues/16622
const newValueInString =
typeof newValue === 'number' ? newValue.toFixed(precision) : `${newValue}`;
const changed = newValue !== value || newValueInString !== `${inputValue}`;
if (!hasProp(this, 'value')) {
this.setState(
{
sValue: newValue,
inputValue: this.toPrecisionAsStep(v),
},
callback,
);
} else {
// always set input value same as value
this.setState(
{
inputValue: this.toPrecisionAsStep(this.$data.sValue),
},
callback,
);
}
if (changed) {
this.__emit('change', newValue);
}
return newValue;
},
getPrecision(value) {
if (isValidProps(this.precision)) {
return this.precision;
}
const valueString = value.toString();
if (valueString.indexOf('e-') >= 0) {
return parseInt(valueString.slice(valueString.indexOf('e-') + 2), 10);
}
let precision = 0;
if (valueString.indexOf('.') >= 0) {
precision = valueString.length - valueString.indexOf('.') - 1;
}
return precision;
},
// step={1.0} value={1.51}
// press +
// then value should be 2.51, rather than 2.5
// if this.$props.precision is undefined
// https://github.com/react-component/input-number/issues/39
getMaxPrecision(currentValue, ratio = 1) {
if (isValidProps(this.precision)) {
return this.precision;
}
const { step } = this;
const ratioPrecision = this.getPrecision(ratio);
const stepPrecision = this.getPrecision(step);
const currentValuePrecision = this.getPrecision(currentValue);
if (!currentValue) {
return ratioPrecision + stepPrecision;
}
return Math.max(currentValuePrecision, ratioPrecision + stepPrecision);
},
getPrecisionFactor(currentValue, ratio = 1) {
const precision = this.getMaxPrecision(currentValue, ratio);
return Math.pow(10, precision);
},
getInputDisplayValue(state) {
const { focused, inputValue, sValue } = state || this.$data;
let inputDisplayValue;
if (focused) {
inputDisplayValue = inputValue;
} else {
inputDisplayValue = this.toPrecisionAsStep(sValue);
}
if (inputDisplayValue === undefined || inputDisplayValue === null) {
inputDisplayValue = '';
}
let inputDisplayValueFormat = this.formatWrapper(inputDisplayValue);
if (isValidProps(this.$props.decimalSeparator)) {
inputDisplayValueFormat = inputDisplayValueFormat
.toString()
.replace('.', this.$props.decimalSeparator);
}
return inputDisplayValueFormat;
},
recordCursorPosition() {
// Record position
try {
const inputElem = this.inputRef;
this.cursorStart = inputElem.selectionStart;
this.cursorEnd = inputElem.selectionEnd;
this.currentValue = inputElem.value;
this.cursorBefore = inputElem.value.substring(0, this.cursorStart);
this.cursorAfter = inputElem.value.substring(this.cursorEnd);
} catch (e) {
// Fix error in Chrome:
// Failed to read the 'selectionStart' property from 'HTMLInputElement'
// http://stackoverflow.com/q/21177489/3040605
}
},
fixCaret(start, end) {
if (start === undefined || end === undefined || !this.inputRef || !this.inputRef.value) {
return;
}
try {
const inputElem = this.inputRef;
const currentStart = inputElem.selectionStart;
const currentEnd = inputElem.selectionEnd;
if (start !== currentStart || end !== currentEnd) {
inputElem.setSelectionRange(start, end);
}
} catch (e) {
// Fix error in Chrome:
// Failed to read the 'selectionStart' property from 'HTMLInputElement'
// http://stackoverflow.com/q/21177489/3040605
}
},
restoreByAfter(str) {
if (str === undefined) return false;
const fullStr = this.inputRef.value;
const index = fullStr.lastIndexOf(str);
if (index === -1) return false;
const prevCursorPos = this.cursorBefore.length;
if (
this.lastKeyCode === KeyCode.DELETE &&
this.cursorBefore.charAt(prevCursorPos - 1) === str[0]
) {
this.fixCaret(prevCursorPos, prevCursorPos);
return true;
}
if (index + str.length === fullStr.length) {
this.fixCaret(index, index);
return true;
}
return false;
},
partRestoreByAfter(str) {
if (str === undefined) return false;
// For loop from full str to the str with last char to map. e.g. 123
// -> 123
// -> 23
// -> 3
return Array.prototype.some.call(str, (_, start) => {
const partStr = str.substring(start);
return this.restoreByAfter(partStr);
});
},
focus() {
this.inputRef.focus();
this.recordCursorPosition();
},
blur() {
this.inputRef.blur();
},
formatWrapper(num) {
// http://2ality.com/2012/03/signedzero.html
// https://github.com/ant-design/ant-design/issues/9439
if (this.formatter) {
return this.formatter(num);
}
return num;
},
toPrecisionAsStep(num) {
if (this.isNotCompleteNumber(num) || num === '') {
return num;
}
const precision = Math.abs(this.getMaxPrecision(num));
if (!isNaN(precision)) {
return Number(num).toFixed(precision);
}
return num.toString();
},
// '1.' '1x' 'xx' '' => are not complete numbers
isNotCompleteNumber(num) {
return (
isNaN(num) ||
num === '' ||
num === null ||
(num && num.toString().indexOf('.') === num.toString().length - 1)
);
},
toNumber(num) {
const { precision, autofocus } = this.$props;
const { focused = autofocus } = this.$data;
// num.length > 16 => This is to prevent input of large numbers
const numberIsTooLarge = num && num.length > 16 && focused;
if (this.isNotCompleteNumber(num) || numberIsTooLarge) {
return num;
}
if (isValidProps(precision)) {
return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
}
return Number(num);
},
upStep(val, rat) {
const { step } = this;
const precisionFactor = this.getPrecisionFactor(val, rat);
const precision = Math.abs(this.getMaxPrecision(val, rat));
const result = (
(precisionFactor * val + precisionFactor * step * rat) /
precisionFactor
).toFixed(precision);
return this.toNumber(result);
},
downStep(val, rat) {
const { step } = this;
const precisionFactor = this.getPrecisionFactor(val, rat);
const precision = Math.abs(this.getMaxPrecision(val, rat));
const result = (
(precisionFactor * val - precisionFactor * step * rat) /
precisionFactor
).toFixed(precision);
return this.toNumber(result);
},
stepFn(type, e, ratio = 1, recursive) {
this.stop();
if (e) {
// e.persist()
e.preventDefault();
}
if (this.disabled) {
return;
}
const { max, min } = this;
const value = this.getCurrentValidValue(this.$data.inputValue) || 0;
if (this.isNotCompleteNumber(value)) {
return;
}
let val = this[`${type}Step`](value, ratio);
const outOfRange = val > max || val < min;
if (val > max) {
val = max;
} else if (val < min) {
val = min;
}
this.setValue(val);
this.setState({
focused: true,
});
if (outOfRange) {
return;
}
this.autoStepTimer = setTimeout(
() => {
this[type](e, ratio, true);
},
recursive ? SPEED : DELAY,
);
},
stop() {
if (this.autoStepTimer) {
clearTimeout(this.autoStepTimer);
}
},
down(e, ratio, recursive) {
this.pressingUpOrDown = true;
this.stepFn('down', e, ratio, recursive);
},
up(e, ratio, recursive) {
this.pressingUpOrDown = true;
this.stepFn('up', e, ratio, recursive);
},
handleInputClick() {
this.__emit('click');
},
saveUp(node) {
this.upHandlerRef = node;
},
saveDown(node) {
this.downHandlerRef = node;
},
saveInput(node) {
this.inputRef = node;
},
onCompositionstart(e) {
e.target.composing = true;
},
onCompositionend(e) {
this.onChange(e);
e.target.composing = false;
},
},
render() {
const props = { ...this.$props, ...this.$attrs };
const {
prefixCls,
disabled,
readonly,
useTouch,
autocomplete,
upHandler,
downHandler,
class: className,
} = props;
const classes = classNames({
[className]: className,
[prefixCls]: true,
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-focused`]: this.$data.focused,
});
let upDisabledClass = '';
let downDisabledClass = '';
const { sValue } = this.$data;
if (sValue || sValue === 0) {
if (!isNaN(sValue)) {
const val = Number(sValue);
if (val >= this.max) {
upDisabledClass = `${prefixCls}-handler-up-disabled`;
}
if (val <= this.min) {
downDisabledClass = `${prefixCls}-handler-down-disabled`;
}
} else {
upDisabledClass = `${prefixCls}-handler-up-disabled`;
downDisabledClass = `${prefixCls}-handler-down-disabled`;
}
}
const dataOrAriaAttributeProps = {};
for (const key in props) {
if (
props.hasOwnProperty(key) &&
(key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-' || key === 'role')
) {
dataOrAriaAttributeProps[key] = props[key];
}
}
const editable = !this.readonly && !this.disabled;
// focus state, show input value
// unfocus state, show valid value
const inputDisplayValue = this.getInputDisplayValue();
let upEvents;
let downEvents;
if (useTouch) {
upEvents = {
[supportsPassive ? 'onTouchstartPassive' : 'onTouchstart']:
editable && !upDisabledClass && this.up,
onTouchend: this.stop,
};
downEvents = {
[supportsPassive ? 'onTouchstartPassive' : 'onTouchstart']:
editable && !downDisabledClass && this.down,
onTouchend: this.stop,
};
} else {
upEvents = {
onMousedown: editable && !upDisabledClass && this.up,
onMouseup: this.stop,
onMouseleave: this.stop,
};
downEvents = {
onMousedown: editable && !downDisabledClass && this.down,
onMouseup: this.stop,
onMouseleave: this.stop,
};
}
const isUpDisabled = !!upDisabledClass || disabled || readonly;
const isDownDisabled = !!downDisabledClass || disabled || readonly;
const upHandlerProps = {
disabled: isUpDisabled,
prefixCls,
unselectable: 'unselectable',
role: 'button',
'aria-label': 'Increase Value',
'aria-disabled': !!isUpDisabled,
class: `${prefixCls}-handler ${prefixCls}-handler-up ${upDisabledClass}`,
...upEvents,
ref: this.saveUp,
};
const downHandlerProps = {
disabled: isDownDisabled,
prefixCls,
unselectable: 'unselectable',
role: 'button',
'aria-label': 'Decrease Value',
'aria-disabled': !!isDownDisabled,
class: `${prefixCls}-handler ${prefixCls}-handler-down ${downDisabledClass}`,
...downEvents,
ref: this.saveDown,
};
return (
<div
class={classes}
style={props.style}
title={props.title}
onMouseenter={props.onMouseenter}
onMouseleave={props.onMouseleave}
onMouseover={props.onMouseover}
onMouseout={props.onMouseout}
>
<div class={`${prefixCls}-handler-wrap`}>
<span>
<InputHandler {...upHandlerProps} key="upHandler">
{upHandler || (
<span
unselectable="unselectable"
class={`${prefixCls}-handler-up-inner`}
onClick={preventDefault}
/>
)}
</InputHandler>
</span>
<InputHandler {...downHandlerProps} key="downHandler">
{downHandler || (
<span
unselectable="unselectable"
class={`${prefixCls}-handler-down-inner`}
onClick={preventDefault}
/>
)}
</InputHandler>
</div>
<div class={`${prefixCls}-input-wrap`}>
<input
role="spinbutton"
aria-valuemin={this.min}
aria-valuemax={this.max}
aria-valuenow={sValue}
required={this.required}
type={props.type}
placeholder={this.placeholder}
onClick={this.handleInputClick}
class={`${prefixCls}-input`}
tabindex={this.tabindex}
autocomplete={autocomplete}
onFocus={this.onFocus}
onBlur={this.onBlur}
onKeydown={editable && this.onKeyDown}
onKeyup={editable && this.onKeyUp}
autofocus={this.autofocus}
maxlength={this.maxlength}
readonly={this.readonly}
disabled={this.disabled}
max={this.max}
min={this.min}
step={this.step}
name={this.name}
title={this.title}
id={this.id}
onInput={this.onTrigger}
onCompositionstart={this.onCompositionstart}
onCompositionend={this.onCompositionend}
ref={this.saveInput}
value={inputDisplayValue}
pattern={this.pattern}
{...dataOrAriaAttributeProps}
/>
</div>
</div>
);
},
});