refactor: input-number
parent
384ea35ca7
commit
2a47de6c57
|
@ -0,0 +1,3 @@
|
||||||
|
export default function (val: any) {
|
||||||
|
return val !== undefined && val !== null;
|
||||||
|
}
|
|
@ -1,26 +1,113 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// 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`] = `
|
exports[`renders ./components/input-number/demo/basic.vue correctly 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div class="ant-input-number">
|
<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-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 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-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> 当前值:3
|
||||||
</div>
|
</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`] = `
|
exports[`renders ./components/input-number/demo/digit.vue correctly 1`] = `
|
||||||
<div class="ant-input-number">
|
<div class="ant-input-number" style="width: 200px;">
|
||||||
<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-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 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-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>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`renders ./components/input-number/demo/disabled.vue correctly 1`] = `
|
exports[`renders ./components/input-number/demo/disabled.vue correctly 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div class="ant-input-number ant-input-number-disabled">
|
<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-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 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-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>
|
||||||
<div style="margin-top: 20px;"><button class="ant-btn ant-btn-primary" type="button">
|
<div style="margin-top: 20px;"><button class="ant-btn ant-btn-primary" type="button">
|
||||||
<!----><span>Toggle disabled</span>
|
<!----><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`] = `
|
exports[`renders ./components/input-number/demo/formatter.vue correctly 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div class="ant-input-number">
|
<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-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 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-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="1000" step="1" class="ant-input-number-input"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ant-input-number">
|
<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-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 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-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>
|
||||||
</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`] = `
|
exports[`renders ./components/input-number/demo/size.vue correctly 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div class="ant-input-number-lg ant-input-number">
|
<div class="ant-input-number ant-input-number-lg">
|
||||||
<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-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 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-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>
|
||||||
<div class="ant-input-number">
|
<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-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 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-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>
|
||||||
<div class="ant-input-number-sm ant-input-number">
|
<div class="ant-input-number ant-input-number-sm">
|
||||||
<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-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 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-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>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -22,8 +22,6 @@ describe('InputNumber', () => {
|
||||||
);
|
);
|
||||||
wrapper.find('input').element.value = '';
|
wrapper.find('input').element.value = '';
|
||||||
wrapper.find('input').trigger('input');
|
wrapper.find('input').trigger('input');
|
||||||
expect(onChange).toHaveBeenLastCalledWith('');
|
|
||||||
wrapper.find('input').trigger('blur');
|
|
||||||
expect(onChange).toHaveBeenLastCalledWith(null);
|
expect(onChange).toHaveBeenLastCalledWith(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,29 +1,38 @@
|
||||||
<docs>
|
<docs>
|
||||||
---
|
---
|
||||||
order: 0
|
order: 3
|
||||||
title:
|
title:
|
||||||
zh-CN: 小数
|
zh-CN: 高精度小数
|
||||||
en-US: Decimals
|
en-US: High precision decimals
|
||||||
---
|
---
|
||||||
|
|
||||||
## zh-CN
|
## zh-CN
|
||||||
|
|
||||||
和原生的数字输入框一样,value 的精度由 step 的小数位数决定。
|
通过 `stringMode` 开启高精度小数支持,`change` 事件将返回 string 类型。
|
||||||
|
对于旧版浏览器,你需要 BigInt polyfill。
|
||||||
|
|
||||||
## en-US
|
## 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>
|
</docs>
|
||||||
|
|
||||||
<template>
|
<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>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const value = ref<number>();
|
const value = ref<string>('1');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<demo-sort>
|
<demo-sort>
|
||||||
<basic />
|
<basic />
|
||||||
|
<addonVue />
|
||||||
<size />
|
<size />
|
||||||
<disabled />
|
<disabled />
|
||||||
<digit />
|
<digit />
|
||||||
<formatter />
|
<formatter />
|
||||||
|
<keyboardVue />
|
||||||
|
<outOfRangeVue />
|
||||||
|
<borderlessVue />
|
||||||
</demo-sort>
|
</demo-sort>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -13,7 +17,10 @@ import Disabled from './disabled.vue';
|
||||||
import Digit from './digit.vue';
|
import Digit from './digit.vue';
|
||||||
import Formatter from './formatter.vue';
|
import Formatter from './formatter.vue';
|
||||||
import Size from './size.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 CN from '../index.zh-CN.md';
|
||||||
import US from '../index.en-US.md';
|
import US from '../index.en-US.md';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
@ -27,6 +34,10 @@ export default defineComponent({
|
||||||
Digit,
|
Digit,
|
||||||
Formatter,
|
Formatter,
|
||||||
Size,
|
Size,
|
||||||
|
addonVue,
|
||||||
|
borderlessVue,
|
||||||
|
keyboardVue,
|
||||||
|
outOfRangeVue,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,38 +1,27 @@
|
||||||
import type { PropType, ExtractPropTypes } from 'vue';
|
import type { PropType, ExtractPropTypes, HTMLAttributes, App } from 'vue';
|
||||||
import { defineComponent, inject, nextTick, onMounted, ref } from 'vue';
|
import { watch, defineComponent, nextTick, onMounted, ref } from 'vue';
|
||||||
import PropTypes from '../_util/vue-types';
|
|
||||||
import { getOptionProps } from '../_util/props-util';
|
|
||||||
import classNames from '../_util/classNames';
|
import classNames from '../_util/classNames';
|
||||||
import UpOutlined from '@ant-design/icons-vue/UpOutlined';
|
import UpOutlined from '@ant-design/icons-vue/UpOutlined';
|
||||||
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
|
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
|
||||||
import VcInputNumber from '../vc-input-number/src';
|
import VcInputNumber, { inputNumberProps as baseInputNumberProps } from './src/InputNumber';
|
||||||
import { defaultConfigProvider } from '../config-provider';
|
import type { SizeType } from '../config-provider';
|
||||||
import { tuple, withInstall } from '../_util/type';
|
|
||||||
import type { EventHandler } from '../_util/EventInterface';
|
|
||||||
import { useInjectFormItemContext } from '../form/FormItemContext';
|
import { useInjectFormItemContext } from '../form/FormItemContext';
|
||||||
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
const inputNumberProps = {
|
import { cloneElement } from '../_util/vnode';
|
||||||
prefixCls: PropTypes.string,
|
import omit from '../_util/omit';
|
||||||
min: PropTypes.number,
|
import PropTypes from '../_util/vue-types';
|
||||||
max: PropTypes.number,
|
import isValidValue from '../_util/isValidValue';
|
||||||
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
export const inputNumberProps = {
|
||||||
step: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).def(1),
|
...baseInputNumberProps,
|
||||||
defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
size: { type: String as PropType<SizeType> },
|
||||||
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
bordered: { type: Boolean, default: true },
|
||||||
disabled: PropTypes.looseBool,
|
placeholder: String,
|
||||||
size: PropTypes.oneOf(tuple('large', 'small', 'default')),
|
name: String,
|
||||||
formatter: PropTypes.func,
|
id: String,
|
||||||
parser: PropTypes.func,
|
type: String,
|
||||||
decimalSeparator: PropTypes.string,
|
addonBefore: PropTypes.any,
|
||||||
placeholder: PropTypes.string,
|
addonAfter: PropTypes.any,
|
||||||
name: PropTypes.string,
|
'update:value': baseInputNumberProps.onChange,
|
||||||
id: PropTypes.string,
|
|
||||||
precision: PropTypes.number,
|
|
||||||
autofocus: PropTypes.looseBool,
|
|
||||||
onPressEnter: {
|
|
||||||
type: Function as PropType<EventHandler>,
|
|
||||||
},
|
|
||||||
onChange: Function as PropType<(num: number) => void>,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InputNumberProps = Partial<ExtractPropTypes<typeof inputNumberProps>>;
|
export type InputNumberProps = Partial<ExtractPropTypes<typeof inputNumberProps>>;
|
||||||
|
@ -41,17 +30,33 @@ const InputNumber = defineComponent({
|
||||||
name: 'AInputNumber',
|
name: 'AInputNumber',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: inputNumberProps,
|
props: inputNumberProps,
|
||||||
emits: ['input', 'change', 'blur', 'update:value'],
|
emits: ['focus', 'blur', 'change', 'input', 'update:value'],
|
||||||
setup(props, { emit }) {
|
slots: ['addonBefore', 'addonAfter'],
|
||||||
|
setup(props, { emit, expose, attrs, slots }) {
|
||||||
const formItemContext = useInjectFormItemContext();
|
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 inputNumberRef = ref(null);
|
||||||
const focus = () => {
|
const focus = () => {
|
||||||
inputNumberRef.value.focus();
|
inputNumberRef.value?.focus();
|
||||||
};
|
};
|
||||||
const blur = () => {
|
const blur = () => {
|
||||||
inputNumberRef.value.blur();
|
inputNumberRef.value?.blur();
|
||||||
};
|
};
|
||||||
|
expose({
|
||||||
|
focus,
|
||||||
|
blur,
|
||||||
|
});
|
||||||
const handleChange = (val: number) => {
|
const handleChange = (val: number) => {
|
||||||
|
if (props.value === undefined) {
|
||||||
|
mergedValue.value = val;
|
||||||
|
}
|
||||||
emit('update:value', val);
|
emit('update:value', val);
|
||||||
emit('change', val);
|
emit('change', val);
|
||||||
formItemContext.onFieldChange();
|
formItemContext.onFieldChange();
|
||||||
|
@ -60,6 +65,9 @@ const InputNumber = defineComponent({
|
||||||
emit('blur');
|
emit('blur');
|
||||||
formItemContext.onFieldBlur();
|
formItemContext.onFieldBlur();
|
||||||
};
|
};
|
||||||
|
const handleFocus = () => {
|
||||||
|
emit('focus');
|
||||||
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (process.env.NODE_ENV === 'test') {
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
@ -69,53 +77,88 @@ const InputNumber = defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return {
|
return () => {
|
||||||
configProvider: inject('configProvider', defaultConfigProvider),
|
|
||||||
inputNumberRef,
|
|
||||||
focus,
|
|
||||||
blur,
|
|
||||||
formItemContext,
|
|
||||||
handleBlur,
|
|
||||||
handleChange,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
|
||||||
size,
|
|
||||||
class: className,
|
class: className,
|
||||||
id = this.formItemContext.id.value,
|
bordered,
|
||||||
|
readonly,
|
||||||
|
style,
|
||||||
|
addonBefore = slots.addonBefore?.(),
|
||||||
|
addonAfter = slots.addonAfter?.(),
|
||||||
...others
|
...others
|
||||||
} = {
|
} = { ...(attrs as HTMLAttributes), ...props };
|
||||||
...getOptionProps(this),
|
|
||||||
...this.$attrs,
|
|
||||||
} as any;
|
|
||||||
const { getPrefixCls } = this.configProvider;
|
|
||||||
const prefixCls = getPrefixCls('input-number', customizePrefixCls);
|
|
||||||
|
|
||||||
|
const preCls = prefixCls.value;
|
||||||
|
|
||||||
|
const mergeSize = size.value;
|
||||||
const inputNumberClass = classNames(
|
const inputNumberClass = classNames(
|
||||||
{
|
{
|
||||||
[`${prefixCls}-lg`]: size === 'large',
|
[`${preCls}-lg`]: mergeSize === 'large',
|
||||||
[`${prefixCls}-sm`]: size === 'small',
|
[`${preCls}-sm`]: mergeSize === 'small',
|
||||||
|
[`${preCls}-rtl`]: direction.value === 'rtl',
|
||||||
|
[`${preCls}-readonly`]: readonly,
|
||||||
|
[`${preCls}-borderless`]: !bordered,
|
||||||
},
|
},
|
||||||
className,
|
className,
|
||||||
);
|
);
|
||||||
const upIcon = <UpOutlined class={`${prefixCls}-handler-up-inner`} />;
|
|
||||||
const downIcon = <DownOutlined class={`${prefixCls}-handler-down-inner`} />;
|
|
||||||
|
|
||||||
const vcInputNumberProps = {
|
const element = (
|
||||||
prefixCls,
|
<VcInputNumber
|
||||||
upHandler: upIcon,
|
{...omit(others, ['size', 'defaultValue'])}
|
||||||
downHandler: downIcon,
|
ref={inputNumberRef}
|
||||||
...others,
|
value={mergedValue.value}
|
||||||
class: inputNumberClass,
|
class={inputNumberClass}
|
||||||
onChange: this.handleChange,
|
prefixCls={preCls}
|
||||||
onBlur: this.handleBlur,
|
readonly={readonly}
|
||||||
id,
|
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;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
|
@ -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];
|
||||||
|
}
|
|
@ -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();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -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}`;
|
||||||
|
}
|
|
@ -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)
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function supportBigInt() {
|
||||||
|
return typeof BigInt === 'function';
|
||||||
|
}
|
|
@ -3,11 +3,24 @@
|
||||||
@import '../../input/style/mixin';
|
@import '../../input/style/mixin';
|
||||||
|
|
||||||
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
|
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
|
||||||
|
@form-item-prefix-cls: ~'@{ant-prefix}-form-item';
|
||||||
|
|
||||||
.@{input-number-prefix-cls} {
|
.@{input-number-prefix-cls} {
|
||||||
.reset-component();
|
.reset-component();
|
||||||
.input();
|
.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;
|
display: inline-block;
|
||||||
width: 90px;
|
width: 90px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -25,10 +38,13 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
border-left: @border-width-base @border-style-base @input-number-handler-border-color;
|
||||||
transition: all 0.1s linear;
|
transition: all 0.1s linear;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: @input-number-handler-active-bg;
|
background: @input-number-handler-active-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover &-up-inner,
|
&:hover &-up-inner,
|
||||||
&:hover &-down-inner {
|
&:hover &-down-inner {
|
||||||
color: @input-number-handler-hover-bg;
|
color: @input-number-handler-hover-bg;
|
||||||
|
@ -51,6 +67,10 @@
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.hover(@input-number-hover-border-color);
|
.hover(@input-number-hover-border-color);
|
||||||
|
& + .@{form-item-prefix-cls}-children-icon {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.24s linear 0.24s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-focused {
|
&-focused {
|
||||||
|
@ -67,6 +87,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-readonly {
|
||||||
|
.@{input-number-prefix-cls}-handler-wrap {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-input {
|
&-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: @input-height-base - 2px;
|
height: @input-height-base - 2px;
|
||||||
|
@ -77,13 +103,15 @@
|
||||||
border-radius: @border-radius-base;
|
border-radius: @border-radius-base;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
transition: all 0.3s linear;
|
transition: all 0.3s linear;
|
||||||
-moz-appearance: textfield !important;
|
appearance: textfield !important;
|
||||||
.placeholder();
|
.placeholder();
|
||||||
|
|
||||||
&[type='number']::-webkit-inner-spin-button,
|
&[type='number']::-webkit-inner-spin-button,
|
||||||
&[type='number']::-webkit-outer-spin-button {
|
&[type='number']::-webkit-outer-spin-button {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +140,6 @@
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: @input-number-handler-bg;
|
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;
|
border-radius: 0 @border-radius-base @border-radius-base 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.24s linear 0.1s;
|
transition: opacity 0.24s linear 0.1s;
|
||||||
|
@ -123,30 +150,39 @@
|
||||||
.@{input-number-prefix-cls}-handler {
|
.@{input-number-prefix-cls}-handler {
|
||||||
.@{input-number-prefix-cls}-handler-up-inner,
|
.@{input-number-prefix-cls}-handler-up-inner,
|
||||||
.@{input-number-prefix-cls}-handler-down-inner {
|
.@{input-number-prefix-cls}-handler-down-inner {
|
||||||
.iconfont-size-under-12px(7px);
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
font-size: 7px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.@{input-number-prefix-cls}-borderless & {
|
||||||
|
border-left-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-handler-wrap:hover &-handler {
|
&-handler-wrap:hover &-handler {
|
||||||
height: 40%;
|
height: 40%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover &-handler-wrap {
|
&:hover &-handler-wrap,
|
||||||
|
&-focused &-handler-wrap {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-handler-up {
|
&-handler-up {
|
||||||
border-top-right-radius: @border-radius-base;
|
border-top-right-radius: @border-radius-base;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&-inner {
|
&-inner {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
height: 60% !important;
|
height: 60% !important;
|
||||||
}
|
}
|
||||||
|
@ -157,14 +193,19 @@
|
||||||
border-top: @border-width-base @border-style-base @border-color-base;
|
border-top: @border-width-base @border-style-base @border-color-base;
|
||||||
border-bottom-right-radius: @border-radius-base;
|
border-bottom-right-radius: @border-radius-base;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&-inner {
|
&-inner {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -6px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
height: 60% !important;
|
height: 60% !important;
|
||||||
}
|
}
|
||||||
|
.@{input-number-prefix-cls}-borderless & {
|
||||||
|
border-top-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-handler-up-disabled,
|
&-handler-up-disabled,
|
||||||
|
@ -176,4 +217,17 @@
|
||||||
&-handler-down-disabled:hover &-handler-down-inner {
|
&-handler-down-disabled:hover &-handler-down-inner {
|
||||||
color: @disabled-color;
|
color: @disabled-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-borderless {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== Out Of Range =====================
|
||||||
|
&-out-of-range {
|
||||||
|
input {
|
||||||
|
color: @error-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import './rtl';
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -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>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
Loading…
Reference in New Issue