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
|
||||
|
||||
exports[`renders ./components/input-number/demo/addon.vue correctly 1`] = `
|
||||
<div class="ant-space ant-space-vertical">
|
||||
<div class="ant-space-item" style="margin-bottom: 8px;">
|
||||
<div class="ant-input-number-group-wrapper">
|
||||
<div class="ant-input-number-wrapper ant-input-number-group">
|
||||
<div class="ant-input-number-group-addon">+</div>
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
<div class="ant-input-number-group-addon">$</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<div class="ant-space-item" style="margin-bottom: 8px;">
|
||||
<div class="ant-input-number-group-wrapper">
|
||||
<div class="ant-input-number-wrapper ant-input-number-group">
|
||||
<div class="ant-input-number-group-addon">
|
||||
<div style="width: 60px;" class="ant-select ant-select-single ant-select-show-arrow">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"></span><span class="ant-select-selection-item">+</span>
|
||||
<!---->
|
||||
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
<div class="ant-input-number-group-addon">
|
||||
<div style="width: 60px;" class="ant-select ant-select-single ant-select-show-arrow">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="ant-select-selector"><span class="ant-select-selection-search"><input id="rc_select_TEST_OR_SSR" autocomplete="off" class="ant-select-selection-search-input" style="opacity: 0;" role="combobox" aria-haspopup="listbox" aria-owns="rc_select_TEST_OR_SSR_list" aria-autocomplete="list" aria-controls="rc_select_TEST_OR_SSR_list" aria-activedescendant="rc_select_TEST_OR_SSR_list_0" readonly="" unselectable="on" type="search"></span><span class="ant-select-selection-item">$</span>
|
||||
<!---->
|
||||
</div><span class="ant-select-arrow" style="user-select: none;" unselectable="on" aria-hidden="true"><span role="img" aria-label="down" class="anticon anticon-down ant-select-suffix"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<div class="ant-space-item" style="margin-bottom: 8px;">
|
||||
<div class="ant-input-number-group-wrapper">
|
||||
<div class="ant-input-number-wrapper ant-input-number-group">
|
||||
<!---->
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
<div class="ant-input-number-group-addon"><span role="img" aria-label="setting" class="anticon anticon-setting"><svg focusable="false" class="" data-icon="setting" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"></path></svg></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<div class="ant-space-item">
|
||||
<div class="ant-input-number-group-wrapper">
|
||||
<div class="ant-input-number-wrapper ant-input-number-group">
|
||||
<!---->
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
<div class="ant-input-number-group-addon">
|
||||
<!----><span class="ant-cascader-picker" style="width: 150px;" tabindex="0"><span class="ant-cascader-picker-label"></span><input placeholder="cascader" autocomplete="off" type="text" readonly="" class="ant-input ant-cascader-input">
|
||||
<!----><span role="img" aria-label="down" class="anticon anticon-down ant-cascader-picker-arrow"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/basic.vue correctly 1`] = `
|
||||
<div>
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="10" min="1" step="1" id="inputNumber"></div>
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" id="inputNumber" class="ant-input-number-input"></div>
|
||||
</div> 当前值:3
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/borderless.vue correctly 1`] = `
|
||||
<div>
|
||||
<div class="ant-input-number ant-input-number-borderless">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" id="inputNumber" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/digit.vue correctly 1`] = `
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="0" aria-valuemax="10" class="ant-input-number-input" autocomplete="off" max="10" min="0" step="0.1"></div>
|
||||
<div class="ant-input-number" style="width: 200px;">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="0" aria-valuemax="10" aria-valuenow="1" step="1e-14" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/disabled.vue correctly 1`] = `
|
||||
<div>
|
||||
<div class="ant-input-number ant-input-number-disabled">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" disabled="" max="10" min="1" step="1"></div>
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" class="ant-input-number-input" disabled=""></div>
|
||||
</div>
|
||||
<div style="margin-top: 20px;"><button class="ant-btn ant-btn-primary" type="button">
|
||||
<!----><span>Toggle disabled</span>
|
||||
|
@ -31,29 +118,59 @@ exports[`renders ./components/input-number/demo/disabled.vue correctly 1`] = `
|
|||
exports[`renders ./components/input-number/demo/formatter.vue correctly 1`] = `
|
||||
<div>
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="-9007199254740991" aria-valuenow="1000" class="ant-input-number-input" autocomplete="off" min="-9007199254740991" step="1"></div>
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuenow="1000" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" class="ant-input-number-input" autocomplete="off" max="100" min="0" step="1"></div>
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/keyboard.vue correctly 1`] = `
|
||||
<div class="ant-space ant-space-horizontal ant-space-align-center">
|
||||
<div class="ant-space-item" style="margin-right: 8px;">
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<div class="ant-space-item"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span>Toggle keyboard</span></label></div>
|
||||
<!---->
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/out-of-range.vue correctly 1`] = `
|
||||
<div class="ant-space ant-space-horizontal ant-space-align-center">
|
||||
<div class="ant-space-item" style="margin-right: 8px;">
|
||||
<div class="ant-input-number ant-input-number-out-of-range">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="true" class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="10" aria-valuenow="99" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<div class="ant-space-item"><button class="ant-btn ant-btn-primary" type="button">
|
||||
<!----><span>Reset</span>
|
||||
</button></div>
|
||||
<!---->
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/size.vue correctly 1`] = `
|
||||
<div>
|
||||
<div class="ant-input-number-lg ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="100000" min="1" step="1"></div>
|
||||
<div class="ant-input-number ant-input-number-lg">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
<div class="ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="100000" min="1" step="1"></div>
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
<div class="ant-input-number-sm ant-input-number">
|
||||
<div class="ant-input-number-handler-wrap"><span><span unselectable="unselectable" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span></span><span unselectable="unselectable" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" class="ant-input-number-input" autocomplete="off" max="100000" min="1" step="1"></div>
|
||||
<div class="ant-input-number ant-input-number-sm">
|
||||
<div class="ant-input-number-handler-wrap"><span unselectable="on" role="button" aria-label="Increase Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-up"><span role="img" aria-label="up" class="anticon anticon-up ant-input-number-handler-up-inner"><svg focusable="false" class="" data-icon="up" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path></svg></span></span><span unselectable="on" role="button" aria-label="Decrease Value" aria-disabled="false" class="ant-input-number-handler ant-input-number-handler-down"><span role="img" aria-label="down" class="anticon anticon-down ant-input-number-handler-down-inner"><svg focusable="false" class="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></span></span></div>
|
||||
<div class="ant-input-number-input-wrap"><input autocomplete="off" role="spinbutton" aria-valuemin="1" aria-valuemax="100000" aria-valuenow="3" step="1" class="ant-input-number-input"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -22,8 +22,6 @@ describe('InputNumber', () => {
|
|||
);
|
||||
wrapper.find('input').element.value = '';
|
||||
wrapper.find('input').trigger('input');
|
||||
expect(onChange).toHaveBeenLastCalledWith('');
|
||||
wrapper.find('input').trigger('blur');
|
||||
expect(onChange).toHaveBeenLastCalledWith(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
---
|
||||
order: 0
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 小数
|
||||
en-US: Decimals
|
||||
zh-CN: 高精度小数
|
||||
en-US: High precision decimals
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
和原生的数字输入框一样,value 的精度由 step 的小数位数决定。
|
||||
通过 `stringMode` 开启高精度小数支持,`change` 事件将返回 string 类型。
|
||||
对于旧版浏览器,你需要 BigInt polyfill。
|
||||
|
||||
## en-US
|
||||
|
||||
A numeric-only input box whose values can be increased or decreased using a decimal step. The number of decimals (also known as precision) is determined by the step prop.
|
||||
Use `stringMode` to support high precision decimals support.
|
||||
`change` will return string value instead. You need polyfill of BigInt if browser not support.
|
||||
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<a-input-number v-model:value="value" :min="0" :max="10" :step="0.1" />
|
||||
<a-input-number
|
||||
v-model:value="value"
|
||||
style="width: 200px"
|
||||
:min="0"
|
||||
:max="10"
|
||||
:step="0.00000000000001"
|
||||
string-mode
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const value = ref<number>();
|
||||
const value = ref<string>('1');
|
||||
|
||||
return {
|
||||
value,
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<template>
|
||||
<demo-sort>
|
||||
<basic />
|
||||
<addonVue />
|
||||
<size />
|
||||
<disabled />
|
||||
<digit />
|
||||
<formatter />
|
||||
<keyboardVue />
|
||||
<outOfRangeVue />
|
||||
<borderlessVue />
|
||||
</demo-sort>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
|
@ -13,7 +17,10 @@ import Disabled from './disabled.vue';
|
|||
import Digit from './digit.vue';
|
||||
import Formatter from './formatter.vue';
|
||||
import Size from './size.vue';
|
||||
|
||||
import addonVue from './addon.vue';
|
||||
import borderlessVue from './borderless.vue';
|
||||
import keyboardVue from './keyboard.vue';
|
||||
import outOfRangeVue from './out-of-range.vue';
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
import { defineComponent } from 'vue';
|
||||
|
@ -27,6 +34,10 @@ export default defineComponent({
|
|||
Digit,
|
||||
Formatter,
|
||||
Size,
|
||||
addonVue,
|
||||
borderlessVue,
|
||||
keyboardVue,
|
||||
outOfRangeVue,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -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 { defineComponent, inject, nextTick, onMounted, ref } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { getOptionProps } from '../_util/props-util';
|
||||
import type { PropType, ExtractPropTypes, HTMLAttributes, App } from 'vue';
|
||||
import { watch, defineComponent, nextTick, onMounted, ref } from 'vue';
|
||||
import classNames from '../_util/classNames';
|
||||
import UpOutlined from '@ant-design/icons-vue/UpOutlined';
|
||||
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
|
||||
import VcInputNumber from '../vc-input-number/src';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { tuple, withInstall } from '../_util/type';
|
||||
import type { EventHandler } from '../_util/EventInterface';
|
||||
import VcInputNumber, { inputNumberProps as baseInputNumberProps } from './src/InputNumber';
|
||||
import type { SizeType } from '../config-provider';
|
||||
import { useInjectFormItemContext } from '../form/FormItemContext';
|
||||
|
||||
const inputNumberProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
min: PropTypes.number,
|
||||
max: PropTypes.number,
|
||||
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
step: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).def(1),
|
||||
defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
tabindex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
disabled: PropTypes.looseBool,
|
||||
size: PropTypes.oneOf(tuple('large', 'small', 'default')),
|
||||
formatter: PropTypes.func,
|
||||
parser: PropTypes.func,
|
||||
decimalSeparator: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
precision: PropTypes.number,
|
||||
autofocus: PropTypes.looseBool,
|
||||
onPressEnter: {
|
||||
type: Function as PropType<EventHandler>,
|
||||
},
|
||||
onChange: Function as PropType<(num: number) => void>,
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import omit from '../_util/omit';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import isValidValue from '../_util/isValidValue';
|
||||
export const inputNumberProps = {
|
||||
...baseInputNumberProps,
|
||||
size: { type: String as PropType<SizeType> },
|
||||
bordered: { type: Boolean, default: true },
|
||||
placeholder: String,
|
||||
name: String,
|
||||
id: String,
|
||||
type: String,
|
||||
addonBefore: PropTypes.any,
|
||||
addonAfter: PropTypes.any,
|
||||
'update:value': baseInputNumberProps.onChange,
|
||||
};
|
||||
|
||||
export type InputNumberProps = Partial<ExtractPropTypes<typeof inputNumberProps>>;
|
||||
|
@ -41,17 +30,33 @@ const InputNumber = defineComponent({
|
|||
name: 'AInputNumber',
|
||||
inheritAttrs: false,
|
||||
props: inputNumberProps,
|
||||
emits: ['input', 'change', 'blur', 'update:value'],
|
||||
setup(props, { emit }) {
|
||||
emits: ['focus', 'blur', 'change', 'input', 'update:value'],
|
||||
slots: ['addonBefore', 'addonAfter'],
|
||||
setup(props, { emit, expose, attrs, slots }) {
|
||||
const formItemContext = useInjectFormItemContext();
|
||||
const { prefixCls, size, direction } = useConfigInject('input-number', props);
|
||||
const mergedValue = ref(props.value === undefined ? props.defaultValue : props.value);
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
mergedValue.value = props.value;
|
||||
},
|
||||
);
|
||||
const inputNumberRef = ref(null);
|
||||
const focus = () => {
|
||||
inputNumberRef.value.focus();
|
||||
inputNumberRef.value?.focus();
|
||||
};
|
||||
const blur = () => {
|
||||
inputNumberRef.value.blur();
|
||||
inputNumberRef.value?.blur();
|
||||
};
|
||||
expose({
|
||||
focus,
|
||||
blur,
|
||||
});
|
||||
const handleChange = (val: number) => {
|
||||
if (props.value === undefined) {
|
||||
mergedValue.value = val;
|
||||
}
|
||||
emit('update:value', val);
|
||||
emit('change', val);
|
||||
formItemContext.onFieldChange();
|
||||
|
@ -60,6 +65,9 @@ const InputNumber = defineComponent({
|
|||
emit('blur');
|
||||
formItemContext.onFieldBlur();
|
||||
};
|
||||
const handleFocus = () => {
|
||||
emit('focus');
|
||||
};
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
|
@ -69,53 +77,88 @@ const InputNumber = defineComponent({
|
|||
}
|
||||
});
|
||||
});
|
||||
return {
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
inputNumberRef,
|
||||
focus,
|
||||
blur,
|
||||
formItemContext,
|
||||
handleBlur,
|
||||
handleChange,
|
||||
return () => {
|
||||
const {
|
||||
class: className,
|
||||
bordered,
|
||||
readonly,
|
||||
style,
|
||||
addonBefore = slots.addonBefore?.(),
|
||||
addonAfter = slots.addonAfter?.(),
|
||||
...others
|
||||
} = { ...(attrs as HTMLAttributes), ...props };
|
||||
|
||||
const preCls = prefixCls.value;
|
||||
|
||||
const mergeSize = size.value;
|
||||
const inputNumberClass = classNames(
|
||||
{
|
||||
[`${preCls}-lg`]: mergeSize === 'large',
|
||||
[`${preCls}-sm`]: mergeSize === 'small',
|
||||
[`${preCls}-rtl`]: direction.value === 'rtl',
|
||||
[`${preCls}-readonly`]: readonly,
|
||||
[`${preCls}-borderless`]: !bordered,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
const element = (
|
||||
<VcInputNumber
|
||||
{...omit(others, ['size', 'defaultValue'])}
|
||||
ref={inputNumberRef}
|
||||
value={mergedValue.value}
|
||||
class={inputNumberClass}
|
||||
prefixCls={preCls}
|
||||
readonly={readonly}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
v-slots={{
|
||||
upHandler: () => <UpOutlined class={`${preCls}-handler-up-inner`} />,
|
||||
downHandler: () => <DownOutlined class={`${preCls}-handler-down-inner`} />,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
if (isValidValue(addonBefore) || isValidValue(addonAfter)) {
|
||||
const wrapperClassName = `${preCls}-group`;
|
||||
const addonClassName = `${wrapperClassName}-addon`;
|
||||
const addonBeforeNode = addonBefore ? (
|
||||
<div class={addonClassName}>{addonBefore}</div>
|
||||
) : null;
|
||||
const addonAfterNode = addonAfter ? <div class={addonClassName}>{addonAfter}</div> : null;
|
||||
|
||||
const mergedWrapperClassName = classNames(`${preCls}-wrapper`, wrapperClassName, {
|
||||
[`${wrapperClassName}-rtl`]: direction.value === 'rtl',
|
||||
});
|
||||
|
||||
const mergedGroupClassName = classNames(
|
||||
`${preCls}-group-wrapper`,
|
||||
{
|
||||
[`${preCls}-group-wrapper-sm`]: mergeSize === 'small',
|
||||
[`${preCls}-group-wrapper-lg`]: mergeSize === 'large',
|
||||
[`${preCls}-group-wrapper-rtl`]: direction.value === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
return (
|
||||
<div class={mergedGroupClassName} style={style}>
|
||||
<div class={mergedWrapperClassName}>
|
||||
{addonBeforeNode}
|
||||
{element}
|
||||
{addonAfterNode}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return cloneElement(element, { style });
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
size,
|
||||
class: className,
|
||||
id = this.formItemContext.id.value,
|
||||
...others
|
||||
} = {
|
||||
...getOptionProps(this),
|
||||
...this.$attrs,
|
||||
} as any;
|
||||
const { getPrefixCls } = this.configProvider;
|
||||
const prefixCls = getPrefixCls('input-number', customizePrefixCls);
|
||||
|
||||
const inputNumberClass = classNames(
|
||||
{
|
||||
[`${prefixCls}-lg`]: size === 'large',
|
||||
[`${prefixCls}-sm`]: size === 'small',
|
||||
},
|
||||
className,
|
||||
);
|
||||
const upIcon = <UpOutlined class={`${prefixCls}-handler-up-inner`} />;
|
||||
const downIcon = <DownOutlined class={`${prefixCls}-handler-down-inner`} />;
|
||||
|
||||
const vcInputNumberProps = {
|
||||
prefixCls,
|
||||
upHandler: upIcon,
|
||||
downHandler: downIcon,
|
||||
...others,
|
||||
class: inputNumberClass,
|
||||
onChange: this.handleChange,
|
||||
onBlur: this.handleBlur,
|
||||
id,
|
||||
};
|
||||
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';
|
||||
|
||||
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
|
||||
@form-item-prefix-cls: ~'@{ant-prefix}-form-item';
|
||||
|
||||
.@{input-number-prefix-cls} {
|
||||
.reset-component();
|
||||
.input();
|
||||
|
||||
//== Style for input-group: input with label, with button or dropdown...
|
||||
&-group {
|
||||
.reset-component();
|
||||
.input-group(~'@{input-number-prefix-cls}');
|
||||
|
||||
&-wrapper {
|
||||
display: inline-block;
|
||||
text-align: start;
|
||||
vertical-align: top; // https://github.com/ant-design/ant-design/issues/6403
|
||||
}
|
||||
}
|
||||
|
||||
display: inline-block;
|
||||
width: 90px;
|
||||
margin: 0;
|
||||
|
@ -25,10 +38,13 @@
|
|||
font-weight: bold;
|
||||
line-height: 0;
|
||||
text-align: center;
|
||||
border-left: @border-width-base @border-style-base @input-number-handler-border-color;
|
||||
transition: all 0.1s linear;
|
||||
|
||||
&:active {
|
||||
background: @input-number-handler-active-bg;
|
||||
}
|
||||
|
||||
&:hover &-up-inner,
|
||||
&:hover &-down-inner {
|
||||
color: @input-number-handler-hover-bg;
|
||||
|
@ -51,6 +67,10 @@
|
|||
|
||||
&:hover {
|
||||
.hover(@input-number-hover-border-color);
|
||||
& + .@{form-item-prefix-cls}-children-icon {
|
||||
opacity: 0;
|
||||
transition: opacity 0.24s linear 0.24s;
|
||||
}
|
||||
}
|
||||
|
||||
&-focused {
|
||||
|
@ -67,6 +87,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-readonly {
|
||||
.@{input-number-prefix-cls}-handler-wrap {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
width: 100%;
|
||||
height: @input-height-base - 2px;
|
||||
|
@ -77,13 +103,15 @@
|
|||
border-radius: @border-radius-base;
|
||||
outline: 0;
|
||||
transition: all 0.3s linear;
|
||||
-moz-appearance: textfield !important;
|
||||
appearance: textfield !important;
|
||||
.placeholder();
|
||||
|
||||
&[type='number']::-webkit-inner-spin-button,
|
||||
&[type='number']::-webkit-outer-spin-button {
|
||||
margin: 0;
|
||||
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +140,6 @@
|
|||
width: 22px;
|
||||
height: 100%;
|
||||
background: @input-number-handler-bg;
|
||||
border-left: @border-width-base @border-style-base @input-number-handler-border-color;
|
||||
border-radius: 0 @border-radius-base @border-radius-base 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.24s linear 0.1s;
|
||||
|
@ -123,30 +150,39 @@
|
|||
.@{input-number-prefix-cls}-handler {
|
||||
.@{input-number-prefix-cls}-handler-up-inner,
|
||||
.@{input-number-prefix-cls}-handler-down-inner {
|
||||
.iconfont-size-under-12px(7px);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: auto;
|
||||
margin-right: 0;
|
||||
font-size: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{input-number-prefix-cls}-borderless & {
|
||||
border-left-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-handler-wrap:hover &-handler {
|
||||
height: 40%;
|
||||
}
|
||||
|
||||
&:hover &-handler-wrap {
|
||||
&:hover &-handler-wrap,
|
||||
&-focused &-handler-wrap {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&-handler-up {
|
||||
border-top-right-radius: @border-radius-base;
|
||||
cursor: pointer;
|
||||
|
||||
&-inner {
|
||||
top: 50%;
|
||||
margin-top: -5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
height: 60% !important;
|
||||
}
|
||||
|
@ -157,14 +193,19 @@
|
|||
border-top: @border-width-base @border-style-base @border-color-base;
|
||||
border-bottom-right-radius: @border-radius-base;
|
||||
cursor: pointer;
|
||||
|
||||
&-inner {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
text-align: center;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
height: 60% !important;
|
||||
}
|
||||
.@{input-number-prefix-cls}-borderless & {
|
||||
border-top-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-handler-up-disabled,
|
||||
|
@ -176,4 +217,17 @@
|
|||
&-handler-down-disabled:hover &-handler-down-inner {
|
||||
color: @disabled-color;
|
||||
}
|
||||
|
||||
&-borderless {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
// ===================== Out Of Range =====================
|
||||
&-out-of-range {
|
||||
input {
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl';
|
||||
|
|
|
@ -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