vuetify
Version:
Vue Material Component Framework
246 lines (244 loc) • 8.61 kB
JavaScript
import { mergeProps as _mergeProps, Fragment as _Fragment, createVNode as _createVNode } from "vue";
// Styles
import "./VNumberInput.css";
// Components
import { VBtn } from "../../components/VBtn/index.mjs";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/index.mjs";
import { VDivider } from "../../components/VDivider/index.mjs";
import { makeVTextFieldProps, VTextField } from "../../components/VTextField/VTextField.mjs"; // Composables
import { useProxiedModel } from "../../composables/proxiedModel.mjs"; // Utilities
import { computed, watchEffect } from 'vue';
import { clamp, genericComponent, getDecimals, omit, propsFactory, useRender } from "../../util/index.mjs"; // Types
const makeVNumberInputProps = propsFactory({
controlVariant: {
type: String,
default: 'default'
},
inset: Boolean,
hideInput: Boolean,
min: {
type: Number,
default: -Infinity
},
max: {
type: Number,
default: Infinity
},
step: {
type: Number,
default: 1
},
...omit(makeVTextFieldProps(), ['appendInnerIcon', 'prependInnerIcon'])
}, 'VNumberInput');
export const VNumberInput = genericComponent()({
name: 'VNumberInput',
inheritAttrs: false,
props: {
...makeVNumberInputProps()
},
emits: {
'update:modelValue': val => true
},
setup(props, _ref) {
let {
attrs,
emit,
slots
} = _ref;
const model = useProxiedModel(props, 'modelValue');
const stepDecimals = computed(() => getDecimals(props.step));
const modelDecimals = computed(() => model.value != null ? getDecimals(model.value) : 0);
const canIncrease = computed(() => {
if (model.value == null) return true;
return model.value + props.step <= props.max;
});
const canDecrease = computed(() => {
if (model.value == null) return true;
return model.value - props.step >= props.min;
});
watchEffect(() => {
if (model.value != null && (model.value < props.min || model.value > props.max)) {
model.value = clamp(model.value, props.min, props.max);
}
});
const controlVariant = computed(() => {
return props.hideInput ? 'stacked' : props.controlVariant;
});
const incrementSlotProps = computed(() => ({
click: onClickUp
}));
const decrementSlotProps = computed(() => ({
click: onClickDown
}));
function toggleUpDown() {
let increment = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
if (model.value == null) {
model.value = 0;
return;
}
const decimals = Math.max(modelDecimals.value, stepDecimals.value);
if (increment) {
if (canIncrease.value) model.value = +(model.value + props.step).toFixed(decimals);
} else {
if (canDecrease.value) model.value = +(model.value - props.step).toFixed(decimals);
}
}
function onClickUp(e) {
e.stopPropagation();
toggleUpDown();
}
function onClickDown(e) {
e.stopPropagation();
toggleUpDown(false);
}
function onKeydown(e) {
if (['Enter', 'ArrowLeft', 'ArrowRight', 'Backspace', 'Tab'].includes(e.key) || e.ctrlKey) return;
if (['ArrowDown'].includes(e.key)) {
e.preventDefault();
toggleUpDown(false);
return;
}
if (['ArrowUp'].includes(e.key)) {
e.preventDefault();
toggleUpDown();
return;
}
// Only numbers, +, - & . are allowed
if (!/^[0-9\-+.]+$/.test(e.key)) {
e.preventDefault();
}
}
function onModelUpdate(v) {
model.value = v ? +v : undefined;
}
function onControlMousedown(e) {
e.stopPropagation();
}
useRender(() => {
const {
modelValue: _,
...textFieldProps
} = VTextField.filterProps(props);
function controlNode() {
const defaultHeight = controlVariant.value === 'stacked' ? 'auto' : '100%';
return _createVNode("div", {
"class": "v-number-input__control"
}, [!slots.decrement ? _createVNode(VBtn, {
"disabled": !canDecrease.value,
"flat": true,
"key": "decrement-btn",
"height": defaultHeight,
"name": "decrement-btn",
"icon": "$expand",
"size": "small",
"tabindex": "-1",
"onClick": onClickDown,
"onMousedown": onControlMousedown
}, null) : _createVNode(VDefaultsProvider, {
"key": "decrement-defaults",
"defaults": {
VBtn: {
disabled: !canDecrease.value,
flat: true,
height: defaultHeight,
size: 'small',
icon: '$expand'
}
}
}, {
default: () => [slots.decrement(decrementSlotProps.value)]
}), _createVNode(VDivider, {
"vertical": controlVariant.value !== 'stacked'
}, null), !slots.increment ? _createVNode(VBtn, {
"disabled": !canIncrease.value,
"flat": true,
"key": "increment-btn",
"height": defaultHeight,
"name": "increment-btn",
"icon": "$collapse",
"onClick": onClickUp,
"onMousedown": onControlMousedown,
"size": "small",
"tabindex": "-1"
}, null) : _createVNode(VDefaultsProvider, {
"key": "increment-defaults",
"defaults": {
VBtn: {
disabled: !canIncrease.value,
flat: true,
height: defaultHeight,
size: 'small',
icon: '$collapse'
}
}
}, {
default: () => [slots.increment(incrementSlotProps.value)]
})]);
}
function dividerNode() {
return !props.hideInput && !props.inset ? _createVNode(VDivider, {
"vertical": true
}, null) : undefined;
}
const appendInnerControl = controlVariant.value === 'split' ? _createVNode("div", {
"class": "v-number-input__control"
}, [_createVNode(VDivider, {
"vertical": true
}, null), _createVNode(VBtn, {
"flat": true,
"height": "100%",
"icon": "$plus",
"tile": true,
"tabindex": "-1",
"onClick": onClickUp,
"onMousedown": onControlMousedown
}, null)]) : !props.reverse ? _createVNode(_Fragment, null, [dividerNode(), controlNode()]) : undefined;
const hasAppendInner = slots['append-inner'] || appendInnerControl;
const prependInnerControl = controlVariant.value === 'split' ? _createVNode("div", {
"class": "v-number-input__control"
}, [_createVNode(VBtn, {
"flat": true,
"height": "100%",
"icon": "$minus",
"tile": true,
"tabindex": "-1",
"onClick": onClickDown,
"onMousedown": onControlMousedown
}, null), _createVNode(VDivider, {
"vertical": true
}, null)]) : props.reverse ? _createVNode(_Fragment, null, [controlNode(), dividerNode()]) : undefined;
const hasPrependInner = slots['prepend-inner'] || prependInnerControl;
return _createVNode(VTextField, _mergeProps({
"modelValue": model.value,
"onUpdate:modelValue": onModelUpdate,
"onKeydown": onKeydown,
"class": ['v-number-input', {
'v-number-input--default': controlVariant.value === 'default',
'v-number-input--hide-input': props.hideInput,
'v-number-input--inset': props.inset,
'v-number-input--reverse': props.reverse,
'v-number-input--split': controlVariant.value === 'split',
'v-number-input--stacked': controlVariant.value === 'stacked'
}, props.class]
}, textFieldProps, {
"style": props.style,
"inputmode": "decimal"
}), {
...slots,
'append-inner': hasAppendInner ? function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _createVNode(_Fragment, null, [slots['append-inner']?.(...args), appendInnerControl]);
} : undefined,
'prepend-inner': hasPrependInner ? function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _createVNode(_Fragment, null, [prependInnerControl, slots['prepend-inner']?.(...args)]);
} : undefined
});
});
}
});
//# sourceMappingURL=VNumberInput.mjs.map