@v-c/input-number 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { ValueType } from '@v-c/mini-decimal';
2
2
  import { InputFocusOptions } from '@v-c/util/dist/Dom/focus';
3
3
  export type { ValueType };
4
- type SemanticName = 'root' | 'actions' | 'input' | 'action' | 'prefix' | 'suffix';
4
+ type SemanticName = 'root' | 'actions' | 'input' | 'action' | 'prefix' | 'suffix' | 'clear';
5
5
  export interface InputNumberProps<T extends ValueType = ValueType> {
6
6
  mode?: 'input' | 'spinner';
7
7
  prefixCls?: string;
@@ -19,6 +19,11 @@ export interface InputNumberProps<T extends ValueType = ValueType> {
19
19
  readOnly?: boolean;
20
20
  prefix?: any;
21
21
  suffix?: any;
22
+ allowClear?: boolean | {
23
+ clearIcon?: any;
24
+ disabled?: boolean;
25
+ label?: string;
26
+ };
22
27
  upHandler?: any;
23
28
  downHandler?: any;
24
29
  keyboard?: boolean;
@@ -33,6 +38,7 @@ export interface InputNumberProps<T extends ValueType = ValueType> {
33
38
  decimalSeparator?: string;
34
39
  onInput?: (text: string) => void;
35
40
  onChange?: (value: T | null) => void;
41
+ onClear?: () => void;
36
42
  onPressEnter?: (e: KeyboardEvent) => void;
37
43
  onStep?: (value: T, info: {
38
44
  offset: ValueType;
@@ -4,7 +4,7 @@ import StepHandler_default from "./StepHandler.js";
4
4
  import { getDecupleSteps } from "./utils/numberUtil.js";
5
5
  import { computed, createVNode, defineComponent, isVNode, mergeDefaults, mergeProps, shallowRef, watch, watchEffect } from "vue";
6
6
  import getMiniDecimal, { getNumberPrecision, num2str, toFixed, validateNumber } from "@v-c/mini-decimal";
7
- import { clsx } from "@v-c/util";
7
+ import { clsx, isVueRenderable } from "@v-c/util";
8
8
  import { triggerFocus } from "@v-c/util/dist/Dom/focus";
9
9
  import { KeyCodeStr } from "@v-c/util/dist/KeyCode";
10
10
  import omit from "@v-c/util/dist/omit";
@@ -12,6 +12,20 @@ import omit from "@v-c/util/dist/omit";
12
12
  function _isSlot(s) {
13
13
  return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
14
14
  }
15
+ /** Accumulated wheel distance that adds up to a single step. */
16
+ var WHEEL_STEP_DISTANCE = 100;
17
+ var WHEEL_LINE_HEIGHT = 40;
18
+ var WHEEL_PAGE_HEIGHT = 800;
19
+ /** Idle gap after which a new wheel gesture starts from scratch. */
20
+ var WHEEL_DELTA_RESET_INTERVAL = 200;
21
+ /** Normalize `deltaY` to pixels — browsers may report it in lines or pages. */
22
+ function getWheelDeltaY(event) {
23
+ switch (event.deltaMode) {
24
+ case 1: return event.deltaY * WHEEL_LINE_HEIGHT;
25
+ case 2: return event.deltaY * WHEEL_PAGE_HEIGHT;
26
+ default: return event.deltaY;
27
+ }
28
+ }
15
29
  var defaults = {
16
30
  prefixCls: "vc-input-number",
17
31
  step: 1,
@@ -63,6 +77,7 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
63
77
  };
64
78
  const inputValue = shallowRef("");
65
79
  const inputValueRef = shallowRef("");
80
+ let inputValueUpdateId = 0;
66
81
  const mergedFormatter = (number, userTyping) => {
67
82
  if (props.formatter) return props.formatter(number, {
68
83
  userTyping,
@@ -145,6 +160,8 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
145
160
  };
146
161
  const onNextPromise = useFrame_default();
147
162
  const collectInputValue = (inputStr) => {
163
+ inputValueUpdateId += 1;
164
+ const currentUpdateId = inputValueUpdateId;
148
165
  recordCursor();
149
166
  inputValueRef.value = inputStr;
150
167
  inputValue.value = inputStr;
@@ -154,6 +171,7 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
154
171
  }
155
172
  props.onInput?.(inputStr);
156
173
  onNextPromise(() => {
174
+ if (currentUpdateId !== inputValueUpdateId) return;
157
175
  let nextInputStr = inputStr;
158
176
  if (!props.parser) nextInputStr = inputStr.replace(/。/g, ".");
159
177
  if (nextInputStr !== inputStr) collectInputValue(nextInputStr);
@@ -227,20 +245,44 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
227
245
  shiftKeyRef.value = false;
228
246
  props.onKeyUp?.(event);
229
247
  };
248
+ let wheelDelta = 0;
249
+ let wheelTimestamp = 0;
250
+ const resetWheel = () => {
251
+ wheelDelta = 0;
252
+ wheelTimestamp = 0;
253
+ };
254
+ const onInternalWheel = (event) => {
255
+ const delta = getWheelDeltaY(event);
256
+ if (!delta) return;
257
+ const eventTimestamp = event.timeStamp || Date.now();
258
+ if (eventTimestamp - wheelTimestamp > WHEEL_DELTA_RESET_INTERVAL) wheelDelta = 0;
259
+ wheelTimestamp = eventTimestamp;
260
+ if (wheelDelta && Math.sign(wheelDelta) !== Math.sign(delta)) wheelDelta = 0;
261
+ wheelDelta += delta;
262
+ if (Math.abs(wheelDelta) >= WHEEL_STEP_DISTANCE) {
263
+ onInternalStep(wheelDelta < 0, "wheel");
264
+ wheelDelta -= Math.sign(wheelDelta) * WHEEL_STEP_DISTANCE;
265
+ }
266
+ };
230
267
  watchEffect((onCleanup) => {
231
268
  if (props.changeOnWheel && focus.value && inputRef.value) {
232
269
  const onWheel = (event) => {
233
- onInternalStep(event.deltaY < 0, "wheel");
270
+ onInternalWheel(event);
234
271
  event.preventDefault();
235
272
  };
236
273
  inputRef.value.addEventListener("wheel", onWheel, { passive: false });
237
- onCleanup(() => inputRef.value?.removeEventListener("wheel", onWheel));
274
+ onCleanup(() => {
275
+ inputRef.value?.removeEventListener("wheel", onWheel);
276
+ resetWheel();
277
+ });
238
278
  }
239
279
  });
240
280
  const onBlur = (e) => {
281
+ if (e.relatedTarget instanceof Node && rootRef.value?.contains(e.relatedTarget)) return;
241
282
  if (props.changeOnBlur ?? true) flushInputValue(false);
242
283
  focus.value = false;
243
284
  userTypingRef.value = false;
285
+ resetWheel();
244
286
  props.onBlur?.(e);
245
287
  };
246
288
  const onFocus = (e) => {
@@ -254,6 +296,26 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
254
296
  }
255
297
  props.onMouseDown?.(event);
256
298
  };
299
+ const onClearKeyDown = (event) => {
300
+ const isStepKey = [
301
+ "Up",
302
+ "ArrowUp",
303
+ "Down",
304
+ "ArrowDown"
305
+ ].includes(event.key);
306
+ if (event.key === KeyCodeStr.Enter || props.keyboard !== false && isStepKey) event.stopPropagation();
307
+ };
308
+ const onClearClick = () => {
309
+ userTypingRef.value = false;
310
+ inputValueRef.value = "";
311
+ inputValueUpdateId += 1;
312
+ const emptyValue = getMiniDecimal(null);
313
+ if (props.value !== void 0) setInputValue(decimalValue.value, false);
314
+ else if (decimalValue.value.isEmpty()) setInputValue(emptyValue, false);
315
+ inputRef.value?.focus();
316
+ triggerValueUpdate(emptyValue, false);
317
+ props.onClear?.();
318
+ };
257
319
  watch([
258
320
  () => props.precision,
259
321
  () => props.formatter,
@@ -271,7 +333,7 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
271
333
  if (props.formatter) restoreCursor();
272
334
  }, { flush: "post" });
273
335
  return () => {
274
- const { prefixCls = defaults.prefixCls, classNames, styles, step = defaults.step, disabled, readOnly, controls = defaults.controls, mode = defaults.mode, placeholder } = props;
336
+ const { prefixCls = defaults.prefixCls, classNames, styles, step = defaults.step, disabled, readOnly, controls = defaults.controls, mode = defaults.mode, placeholder, allowClear } = props;
275
337
  const mergedPrefixCls = prefixCls || defaults.prefixCls;
276
338
  const { class: className, style, ...restAttrs } = attrs;
277
339
  const mergedClassName = props.className || className;
@@ -284,6 +346,23 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
284
346
  const suffixNode = slots.suffix?.() ?? props.suffix;
285
347
  const upNode = slots.upHandler?.() ?? props.upHandler;
286
348
  const downNode = slots.downHandler?.() ?? props.downHandler;
349
+ const clearConfig = allowClear && typeof allowClear === "object" ? allowClear : { disabled: allowClear !== true };
350
+ const showClear = !disabled && !readOnly && clearConfig.disabled !== true && String(inputValue.value).length > 0;
351
+ const hasSuffix = isVueRenderable(suffixNode);
352
+ const clearIconCls = `${mergedPrefixCls}-clear-icon`;
353
+ const clearNode = allowClear && createVNode("button", {
354
+ "type": "button",
355
+ "aria-label": clearConfig.label ?? "Clear",
356
+ "disabled": !showClear,
357
+ "class": clsx(clearIconCls, {
358
+ [`${clearIconCls}-hidden`]: !showClear,
359
+ [`${clearIconCls}-has-suffix`]: hasSuffix
360
+ }, classNames?.clear),
361
+ "style": styles?.clear,
362
+ "onMousedown": (event) => event.preventDefault(),
363
+ "onKeydown": onClearKeyDown,
364
+ "onClick": onClearClick
365
+ }, [slots.clearIcon?.() ?? clearConfig.clearIcon ?? "✖"]);
287
366
  const upHandlerNode = createVNode(StepHandler_default, {
288
367
  "prefixCls": mergedPrefixCls,
289
368
  "action": "up",
@@ -308,6 +387,7 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
308
387
  "value",
309
388
  "prefix",
310
389
  "suffix",
390
+ "allowClear",
311
391
  "upHandler",
312
392
  "downHandler",
313
393
  "keyboard",
@@ -319,6 +399,7 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
319
399
  "precision",
320
400
  "decimalSeparator",
321
401
  "onChange",
402
+ "onClear",
322
403
  "onInput",
323
404
  "onPressEnter",
324
405
  "onStep",
@@ -371,7 +452,7 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
371
452
  }
372
453
  }, [
373
454
  mode === "spinner" && controls && downHandlerNode,
374
- !!prefixNode && createVNode("div", {
455
+ isVueRenderable(prefixNode) && createVNode("div", {
375
456
  "class": clsx(`${mergedPrefixCls}-prefix`, classNames?.prefix),
376
457
  "style": styles?.prefix
377
458
  }, [prefixNode]),
@@ -398,10 +479,10 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
398
479
  "onCompositionend": onCompositionEnd,
399
480
  "onBeforeinput": onBeforeInput
400
481
  }, inputAttrs), null),
401
- !!suffixNode && createVNode("div", {
482
+ (allowClear || hasSuffix) && createVNode("div", {
402
483
  "class": clsx(`${mergedPrefixCls}-suffix`, classNames?.suffix),
403
484
  "style": styles?.suffix
404
- }, [suffixNode]),
485
+ }, [clearNode, suffixNode]),
405
486
  mode === "spinner" && controls && upHandlerNode,
406
487
  mode === "input" && controls && createVNode("div", {
407
488
  "class": clsx(`${mergedPrefixCls}-actions`, classNames?.actions),
@@ -482,6 +563,11 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
482
563
  required: false,
483
564
  default: void 0
484
565
  },
566
+ allowClear: {
567
+ type: [Boolean, Object],
568
+ required: false,
569
+ default: void 0
570
+ },
485
571
  upHandler: {
486
572
  required: false,
487
573
  default: void 0
@@ -535,6 +621,11 @@ var InputNumber = /* @__PURE__ */ defineComponent((props, { attrs, slots, expose
535
621
  required: false,
536
622
  default: void 0
537
623
  },
624
+ onClear: {
625
+ type: Function,
626
+ required: false,
627
+ default: void 0
628
+ },
538
629
  onPressEnter: {
539
630
  type: Function,
540
631
  required: false,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@v-c/input-number",
3
3
  "type": "module",
4
- "version": "1.0.6",
4
+ "version": "1.1.0",
5
5
  "description": "",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/antdv-next/vue-components/tree/next/packages/input-number",
@@ -30,9 +30,9 @@
30
30
  "vue": "^3.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "@v-c/input": "^1.1.1",
34
- "@v-c/util": "^1.0.21",
35
- "@v-c/mini-decimal": "^1.0.1"
33
+ "@v-c/input": "^1.1.2",
34
+ "@v-c/mini-decimal": "^1.0.1",
35
+ "@v-c/util": "^1.2.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "vite build",