UNPKG

naive-ui

Version:

A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast

591 lines (590 loc) 26.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.inputNumberProps = void 0; const evtd_1 = require("evtd"); const seemly_1 = require("seemly"); const vooks_1 = require("vooks"); const vue_1 = require("vue"); const _internal_1 = require("../../_internal"); const icons_1 = require("../../_internal/icons"); const _mixins_1 = require("../../_mixins"); const use_rtl_1 = require("../../_mixins/use-rtl"); const _utils_1 = require("../../_utils"); const button_1 = require("../../button"); const input_1 = require("../../input"); const styles_1 = require("../styles"); const input_number_cssr_1 = __importDefault(require("./styles/input-number.cssr")); const utils_1 = require("./utils"); const HOLDING_CHANGE_THRESHOLD = 800; const HOLDING_CHANGE_INTERVAL = 100; exports.inputNumberProps = Object.assign(Object.assign({}, _mixins_1.useTheme.props), { autofocus: Boolean, loading: { type: Boolean, default: undefined }, placeholder: String, defaultValue: { type: Number, default: null }, value: Number, step: { type: [Number, String], default: 1 }, min: [Number, String], max: [Number, String], size: String, disabled: { type: Boolean, default: undefined }, validator: Function, bordered: { type: Boolean, default: undefined }, showButton: { type: Boolean, default: true }, buttonPlacement: { type: String, default: 'right' }, inputProps: Object, readonly: Boolean, clearable: Boolean, keyboard: { type: Object, default: {} }, updateValueOnInput: { type: Boolean, default: true }, round: { type: Boolean, default: undefined }, parse: Function, format: Function, precision: Number, status: String, 'onUpdate:value': [Function, Array], onUpdateValue: [Function, Array], onFocus: [Function, Array], onBlur: [Function, Array], onClear: [Function, Array], // deprecated onChange: [Function, Array] }); exports.default = (0, vue_1.defineComponent)({ name: 'InputNumber', props: exports.inputNumberProps, slots: Object, setup(props) { if (process.env.NODE_ENV !== 'production') { (0, vue_1.watchEffect)(() => { if (props.onChange !== undefined) { (0, _utils_1.warnOnce)('input-number', '`on-change` is deprecated, please use `on-update:value` instead'); } }); } const { mergedBorderedRef, mergedClsPrefixRef, mergedRtlRef } = (0, _mixins_1.useConfig)(props); const themeRef = (0, _mixins_1.useTheme)('InputNumber', '-input-number', input_number_cssr_1.default, styles_1.inputNumberLight, props, mergedClsPrefixRef); const { localeRef } = (0, _mixins_1.useLocale)('InputNumber'); const formItem = (0, _mixins_1.useFormItem)(props); const { mergedSizeRef, mergedDisabledRef, mergedStatusRef } = formItem; // dom ref const inputInstRef = (0, vue_1.ref)(null); const minusButtonInstRef = (0, vue_1.ref)(null); const addButtonInstRef = (0, vue_1.ref)(null); // value const uncontrolledValueRef = (0, vue_1.ref)(props.defaultValue); const controlledValueRef = (0, vue_1.toRef)(props, 'value'); const mergedValueRef = (0, vooks_1.useMergedState)(controlledValueRef, uncontrolledValueRef); const displayedValueRef = (0, vue_1.ref)(''); const getPrecision = (value) => { const fraction = String(value).split('.')[1]; return fraction ? fraction.length : 0; }; const getMaxPrecision = (currentValue) => { const precisions = [props.min, props.max, props.step, currentValue].map((value) => { if (value === undefined) return 0; return getPrecision(value); }); return Math.max(...precisions); }; const mergedPlaceholderRef = (0, vooks_1.useMemo)(() => { const { placeholder } = props; if (placeholder !== undefined) return placeholder; return localeRef.value.placeholder; }); const mergedStepRef = (0, vooks_1.useMemo)(() => { const parsedNumber = (0, utils_1.parseNumber)(props.step); if (parsedNumber !== null) { return parsedNumber === 0 ? 1 : Math.abs(parsedNumber); } return 1; }); const mergedMinRef = (0, vooks_1.useMemo)(() => { const parsedNumber = (0, utils_1.parseNumber)(props.min); if (parsedNumber !== null) return parsedNumber; else return null; }); const mergedMaxRef = (0, vooks_1.useMemo)(() => { const parsedNumber = (0, utils_1.parseNumber)(props.max); if (parsedNumber !== null) return parsedNumber; else return null; }); const deriveDisplayedValueFromValue = () => { const { value: mergedValue } = mergedValueRef; if ((0, utils_1.validator)(mergedValue)) { const { format: formatProp, precision } = props; if (formatProp) { displayedValueRef.value = formatProp(mergedValue); } else { if (mergedValue === null || precision === undefined // precision overflow || getPrecision(mergedValue) > precision) { displayedValueRef.value = (0, utils_1.format)(mergedValue, undefined); } else { displayedValueRef.value = (0, utils_1.format)(mergedValue, precision); } } } else { // null can pass the validator check // so mergedValue is a number displayedValueRef.value = String(mergedValue); } }; deriveDisplayedValueFromValue(); const doUpdateValue = (value) => { const { value: mergedValue } = mergedValueRef; if (value === mergedValue) { deriveDisplayedValueFromValue(); return; } const { 'onUpdate:value': _onUpdateValue, onUpdateValue, onChange } = props; const { nTriggerFormInput, nTriggerFormChange } = formItem; if (onChange) (0, _utils_1.call)(onChange, value); if (onUpdateValue) (0, _utils_1.call)(onUpdateValue, value); if (_onUpdateValue) (0, _utils_1.call)(_onUpdateValue, value); uncontrolledValueRef.value = value; nTriggerFormInput(); nTriggerFormChange(); }; const deriveValueFromDisplayedValue = ({ offset, doUpdateIfValid, fixPrecision, isInputing }) => { const { value: displayedValue } = displayedValueRef; if (isInputing && (0, utils_1.isWipValue)(displayedValue)) { return false; } const parsedValue = (props.parse || utils_1.parse)(displayedValue); if (parsedValue === null) { if (doUpdateIfValid) doUpdateValue(null); return null; } if ((0, utils_1.validator)(parsedValue)) { const currentPrecision = getPrecision(parsedValue); const { precision } = props; if (precision !== undefined && precision < currentPrecision && !fixPrecision) { return false; } let nextValue = Number.parseFloat((parsedValue + offset).toFixed(precision !== null && precision !== void 0 ? precision : getMaxPrecision(parsedValue))); if ((0, utils_1.validator)(nextValue)) { const { value: mergedMax } = mergedMaxRef; const { value: mergedMin } = mergedMinRef; if (mergedMax !== null && nextValue > mergedMax) { if (!doUpdateIfValid || isInputing) return false; // if doUpdateIfValid=true, we try to make it a valid value nextValue = mergedMax; } if (mergedMin !== null && nextValue < mergedMin) { if (!doUpdateIfValid || isInputing) return false; // if doUpdateIfValid=true, we try to make it a valid value nextValue = mergedMin; } if (props.validator && !props.validator(nextValue)) return false; if (doUpdateIfValid) doUpdateValue(nextValue); return nextValue; } } return false; }; const displayedValueInvalidRef = (0, vooks_1.useMemo)(() => { const derivedValue = deriveValueFromDisplayedValue({ offset: 0, doUpdateIfValid: false, isInputing: false, fixPrecision: false }); return derivedValue === false; }); const minusableRef = (0, vooks_1.useMemo)(() => { const { value: mergedValue } = mergedValueRef; if (props.validator && mergedValue === null) { return false; } const { value: mergedStep } = mergedStepRef; const derivedNextValue = deriveValueFromDisplayedValue({ offset: -mergedStep, doUpdateIfValid: false, isInputing: false, fixPrecision: false }); return derivedNextValue !== false; }); const addableRef = (0, vooks_1.useMemo)(() => { const { value: mergedValue } = mergedValueRef; if (props.validator && mergedValue === null) { return false; } const { value: mergedStep } = mergedStepRef; const derivedNextValue = deriveValueFromDisplayedValue({ offset: +mergedStep, doUpdateIfValid: false, isInputing: false, fixPrecision: false }); return derivedNextValue !== false; }); function doFocus(e) { const { onFocus } = props; const { nTriggerFormFocus } = formItem; if (onFocus) (0, _utils_1.call)(onFocus, e); nTriggerFormFocus(); } function doBlur(e) { var _a, _b; if (e.target === ((_a = inputInstRef.value) === null || _a === void 0 ? void 0 : _a.wrapperElRef)) { // hit input wrapper // which means not activated return; } const value = deriveValueFromDisplayedValue({ offset: 0, doUpdateIfValid: true, isInputing: false, fixPrecision: true }); // If valid, update event has been emitted // make sure e.target.value is correct in blur callback if (value !== false) { const inputElRef = (_b = inputInstRef.value) === null || _b === void 0 ? void 0 : _b.inputElRef; if (inputElRef) { inputElRef.value = String(value || ''); } // If value is not changed, the displayed value may be greater than or // less than the current value. The derived value is reformatted so the // value is not changed. We can simply derive a new displayed value if (mergedValueRef.value === value) { deriveDisplayedValueFromValue(); } } else { // If not valid, nothing will be emitted, so derive displayed value from // origin value deriveDisplayedValueFromValue(); } const { onBlur } = props; const { nTriggerFormBlur } = formItem; if (onBlur) (0, _utils_1.call)(onBlur, e); nTriggerFormBlur(); // User may change value in blur callback, we make sure it will be // displayed. Sometimes mergedValue won't be viewed as changed void (0, vue_1.nextTick)(() => { deriveDisplayedValueFromValue(); }); } function doClear(e) { const { onClear } = props; if (onClear) (0, _utils_1.call)(onClear, e); } function doAdd() { const { value: addable } = addableRef; if (!addable) { clearAddHoldTimeout(); return; } const { value: mergedValue } = mergedValueRef; if (mergedValue === null) { if (!props.validator) { doUpdateValue(createValidValue()); } } else { const { value: mergedStep } = mergedStepRef; deriveValueFromDisplayedValue({ offset: mergedStep, doUpdateIfValid: true, isInputing: false, fixPrecision: true }); } } function doMinus() { const { value: minusable } = minusableRef; if (!minusable) { clearMinusHoldTimeout(); return; } const { value: mergedValue } = mergedValueRef; if (mergedValue === null) { if (!props.validator) { doUpdateValue(createValidValue()); } } else { const { value: mergedStep } = mergedStepRef; deriveValueFromDisplayedValue({ offset: -mergedStep, doUpdateIfValid: true, isInputing: false, fixPrecision: true }); } } const handleFocus = doFocus; const handleBlur = doBlur; function createValidValue() { if (props.validator) return null; const { value: mergedMin } = mergedMinRef; const { value: mergedMax } = mergedMaxRef; if (mergedMin !== null) { return Math.max(0, mergedMin); } else if (mergedMax !== null) { return Math.min(0, mergedMax); } else { return 0; } } function handleClear(e) { doClear(e); doUpdateValue(null); } function handleMouseDown(e) { var _a, _b, _c; if ((_a = addButtonInstRef.value) === null || _a === void 0 ? void 0 : _a.$el.contains(e.target)) { e.preventDefault(); } if ((_b = minusButtonInstRef.value) === null || _b === void 0 ? void 0 : _b.$el.contains(e.target)) { e.preventDefault(); } (_c = inputInstRef.value) === null || _c === void 0 ? void 0 : _c.activate(); } let minusHoldStateIntervalId = null; let addHoldStateIntervalId = null; let firstMinusMousedownId = null; function clearMinusHoldTimeout() { if (firstMinusMousedownId) { window.clearTimeout(firstMinusMousedownId); firstMinusMousedownId = null; } if (minusHoldStateIntervalId) { window.clearInterval(minusHoldStateIntervalId); minusHoldStateIntervalId = null; } } let firstAddMousedownId = null; function clearAddHoldTimeout() { if (firstAddMousedownId) { window.clearTimeout(firstAddMousedownId); firstAddMousedownId = null; } if (addHoldStateIntervalId) { window.clearInterval(addHoldStateIntervalId); addHoldStateIntervalId = null; } } function handleMinusMousedown() { clearMinusHoldTimeout(); firstMinusMousedownId = window.setTimeout(() => { minusHoldStateIntervalId = window.setInterval(() => { doMinus(); }, HOLDING_CHANGE_INTERVAL); }, HOLDING_CHANGE_THRESHOLD); (0, evtd_1.on)('mouseup', document, clearMinusHoldTimeout, { once: true }); } function handleAddMousedown() { clearAddHoldTimeout(); firstAddMousedownId = window.setTimeout(() => { addHoldStateIntervalId = window.setInterval(() => { doAdd(); }, HOLDING_CHANGE_INTERVAL); }, HOLDING_CHANGE_THRESHOLD); (0, evtd_1.on)('mouseup', document, clearAddHoldTimeout, { once: true }); } const handleAddClick = () => { if (addHoldStateIntervalId) return; doAdd(); }; const handleMinusClick = () => { if (minusHoldStateIntervalId) return; doMinus(); }; function handleKeyDown(e) { var _a, _b; if (e.key === 'Enter') { if (e.target === ((_a = inputInstRef.value) === null || _a === void 0 ? void 0 : _a.wrapperElRef)) { // hit input wrapper // which means not activated return; } const value = deriveValueFromDisplayedValue({ offset: 0, doUpdateIfValid: true, isInputing: false, fixPrecision: true }); if (value !== false) { (_b = inputInstRef.value) === null || _b === void 0 ? void 0 : _b.deactivate(); } } else if (e.key === 'ArrowUp') { if (!addableRef.value) return; if (props.keyboard.ArrowUp === false) return; e.preventDefault(); const value = deriveValueFromDisplayedValue({ offset: 0, doUpdateIfValid: true, isInputing: false, fixPrecision: true }); if (value !== false) { doAdd(); } } else if (e.key === 'ArrowDown') { if (!minusableRef.value) return; if (props.keyboard.ArrowDown === false) return; e.preventDefault(); const value = deriveValueFromDisplayedValue({ offset: 0, doUpdateIfValid: true, isInputing: false, fixPrecision: true }); if (value !== false) { doMinus(); } } } function handleUpdateDisplayedValue(value) { displayedValueRef.value = value; if (props.updateValueOnInput && !props.format && !props.parse && props.precision === undefined) { deriveValueFromDisplayedValue({ offset: 0, doUpdateIfValid: true, isInputing: true, fixPrecision: false }); } } (0, vue_1.watch)(mergedValueRef, () => { deriveDisplayedValueFromValue(); }); const exposedMethods = { focus: () => { var _a; return (_a = inputInstRef.value) === null || _a === void 0 ? void 0 : _a.focus(); }, blur: () => { var _a; return (_a = inputInstRef.value) === null || _a === void 0 ? void 0 : _a.blur(); }, select: () => { var _a; return (_a = inputInstRef.value) === null || _a === void 0 ? void 0 : _a.select(); } }; const rtlEnabledRef = (0, use_rtl_1.useRtl)('InputNumber', mergedRtlRef, mergedClsPrefixRef); return Object.assign(Object.assign({}, exposedMethods), { rtlEnabled: rtlEnabledRef, inputInstRef, minusButtonInstRef, addButtonInstRef, mergedClsPrefix: mergedClsPrefixRef, mergedBordered: mergedBorderedRef, uncontrolledValue: uncontrolledValueRef, mergedValue: mergedValueRef, mergedPlaceholder: mergedPlaceholderRef, displayedValueInvalid: displayedValueInvalidRef, mergedSize: mergedSizeRef, mergedDisabled: mergedDisabledRef, displayedValue: displayedValueRef, addable: addableRef, minusable: minusableRef, mergedStatus: mergedStatusRef, handleFocus, handleBlur, handleClear, handleMouseDown, handleAddClick, handleMinusClick, handleAddMousedown, handleMinusMousedown, handleKeyDown, handleUpdateDisplayedValue, // theme mergedTheme: themeRef, inputThemeOverrides: { paddingSmall: '0 8px 0 10px', paddingMedium: '0 8px 0 12px', paddingLarge: '0 8px 0 14px' }, buttonThemeOverrides: (0, vue_1.computed)(() => { const { self: { iconColorDisabled } } = themeRef.value; const [r, g, b, a] = (0, seemly_1.rgba)(iconColorDisabled); return { textColorTextDisabled: `rgb(${r}, ${g}, ${b})`, opacityDisabled: `${a}` }; }) }); }, render() { const { mergedClsPrefix, $slots } = this; const renderMinusButton = () => { return ((0, vue_1.h)(button_1.NxButton, { text: true, disabled: !this.minusable || this.mergedDisabled || this.readonly, focusable: false, theme: this.mergedTheme.peers.Button, themeOverrides: this.mergedTheme.peerOverrides.Button, builtinThemeOverrides: this.buttonThemeOverrides, onClick: this.handleMinusClick, onMousedown: this.handleMinusMousedown, ref: "minusButtonInstRef" }, { icon: () => (0, _utils_1.resolveSlot)($slots['minus-icon'], () => [ (0, vue_1.h)(_internal_1.NBaseIcon, { clsPrefix: mergedClsPrefix }, { default: () => (0, vue_1.h)(icons_1.RemoveIcon, null) }) ]) })); }; const renderAddButton = () => { return ((0, vue_1.h)(button_1.NxButton, { text: true, disabled: !this.addable || this.mergedDisabled || this.readonly, focusable: false, theme: this.mergedTheme.peers.Button, themeOverrides: this.mergedTheme.peerOverrides.Button, builtinThemeOverrides: this.buttonThemeOverrides, onClick: this.handleAddClick, onMousedown: this.handleAddMousedown, ref: "addButtonInstRef" }, { icon: () => (0, _utils_1.resolveSlot)($slots['add-icon'], () => [ (0, vue_1.h)(_internal_1.NBaseIcon, { clsPrefix: mergedClsPrefix }, { default: () => (0, vue_1.h)(icons_1.AddIcon, null) }) ]) })); }; return ((0, vue_1.h)("div", { class: [ `${mergedClsPrefix}-input-number`, this.rtlEnabled && `${mergedClsPrefix}-input-number--rtl` ] }, (0, vue_1.h)(input_1.NInput, { ref: "inputInstRef", autofocus: this.autofocus, status: this.mergedStatus, bordered: this.mergedBordered, loading: this.loading, value: this.displayedValue, onUpdateValue: this.handleUpdateDisplayedValue, theme: this.mergedTheme.peers.Input, themeOverrides: this.mergedTheme.peerOverrides.Input, builtinThemeOverrides: this.inputThemeOverrides, size: this.mergedSize, placeholder: this.mergedPlaceholder, disabled: this.mergedDisabled, readonly: this.readonly, round: this.round, textDecoration: this.displayedValueInvalid ? 'line-through' : undefined, onFocus: this.handleFocus, onBlur: this.handleBlur, onKeydown: this.handleKeyDown, onMousedown: this.handleMouseDown, onClear: this.handleClear, clearable: this.clearable, inputProps: this.inputProps, internalLoadingBeforeSuffix: true }, { prefix: () => { var _a; return this.showButton && this.buttonPlacement === 'both' ? [ renderMinusButton(), (0, _utils_1.resolveWrappedSlot)($slots.prefix, (children) => { if (children) { return ((0, vue_1.h)("span", { class: `${mergedClsPrefix}-input-number-prefix` }, children)); } return null; }) ] : (_a = $slots.prefix) === null || _a === void 0 ? void 0 : _a.call($slots); }, suffix: () => { var _a; return this.showButton ? [ (0, _utils_1.resolveWrappedSlot)($slots.suffix, (children) => { if (children) { return ((0, vue_1.h)("span", { class: `${mergedClsPrefix}-input-number-suffix` }, children)); } return null; }), this.buttonPlacement === 'right' ? renderMinusButton() : null, renderAddButton() ] : (_a = $slots.suffix) === null || _a === void 0 ? void 0 : _a.call($slots); } }))); } });