Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(input-number): input-number #1687

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mount } from '@vue/test-utils';
import { nextTick, ref } from 'vue';
import DInputNumber from '../src/input-number';
import { useNamespace } from '../../shared/hooks/use-namespace';
import { useNamespace } from '@devui/shared/utils';
import { Form as DForm, FormItem as DFormItem } from '../../form';

const ns = useNamespace('input-number', true);
Expand Down
3 changes: 2 additions & 1 deletion packages/devui-vue/devui/input-number/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { App } from 'vue';
import InputNumber from './src/input-number';
export * from './src/input-number-types';

export { InputNumber };

Expand All @@ -9,5 +10,5 @@ export default {
status: '50%',
install(app: App): void {
app.component(InputNumber.name, InputNumber);
}
},
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useNamespace } from '../../shared/hooks/use-namespace';
import { useNamespace } from '@devui/shared/utils';

const ns = useNamespace('input-number');

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { PropType, ExtractPropTypes, ComputedRef, Ref, CSSProperties, InputHTMLAttributes } from 'vue';
import type { PropType, ExtractPropTypes, ComputedRef, Ref, CSSProperties, InputHTMLAttributes, Prop } from 'vue';

export type ISize = 'lg' | 'md' | 'sm';

Expand All @@ -23,7 +23,7 @@ export const inputNumberProps = {
default: -Infinity,
},
size: {
type: String as PropType<ISize>
type: String as PropType<ISize>,
},
modelValue: {
type: Number,
Expand All @@ -35,6 +35,9 @@ export const inputNumberProps = {
type: [RegExp, String] as PropType<RegExp | string>,
default: '',
},
formatter: {
type: Function as PropType<(val: number | string) => number | string>,
},
} as const;

export type InputNumberProps = ExtractPropTypes<typeof inputNumberProps>;
Expand Down
106 changes: 71 additions & 35 deletions packages/devui-vue/devui/input-number/src/input-number.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@
display: flex;
border-color: $devui-form-control-line-hover;
}

.#{$devui-prefix}-input-number__input-box--error:not(.disabled) {
border-color: $devui-danger-line;
}

.#{$devui-prefix}-input-number__control-buttons--error:not(.disabled) {
border-color: $devui-danger-line;
border-left-color: $devui-form-control-line-hover;

span {
background-color: $devui-danger-bg;
}
}
}

&:focus-within {
Expand All @@ -27,13 +40,22 @@
display: flex;
border-color: $devui-form-control-line-active;
}

.#{$devui-prefix}-input-number__input-box--error:not(.disabled) {
border-color: $devui-danger-line;
}

.#{$devui-prefix}-input-number__control-buttons--error:not(.disabled) {
border-color: $devui-danger-line;
border-left-color: $devui-form-control-line-hover;
}
}

.#{$devui-prefix}-input-number__input-box {
box-sizing: border-box;
width: 100%;
height: 32px;
line-height: $devui-line-height-base;
height: 28px;
line-height: 20px;
padding: 4px 8px;
display: block;
font-size: $devui-font-size;
Expand Down Expand Up @@ -120,51 +142,65 @@
}
}

.#{$devui-prefix}-input-number__input-wrap {
height: 100%;
}
.#{$devui-prefix}-input-number--lg {
& > .#{$devui-prefix}-input-number__input-box {
height: 46px;
font-size: $devui-font-size-lg;
line-height: 24px;
}

.disabled {
cursor: not-allowed;
.#{$devui-prefix}-input-number__control-buttons .control-button .#{$devui-prefix}-input-number__icon-arrow {
width: 16px;
height: 16px;
}
}
}

.#{$devui-prefix}-input-number--lg {
height: 40px;
.#{$devui-prefix}-input-number__input-box {
font-size: $devui-font-size-lg;
height: 100%;
.#{$devui-prefix}-input-number--md {
& > .#{$devui-prefix}-input-number__input-box {
font-size: $devui-font-size;
height: 32px;
line-height: 20px;
}
}

.#{$devui-prefix}-input-number__control-buttons .control-button .#{$devui-prefix}-input-number__icon-arrow {
width: 20px;
height: 20px;
}
}
.#{$devui-prefix}-input-number--sm {
& > .#{$devui-prefix}-input-number__input-box {
font-size: $devui-font-size-sm;
line-height: 18px;
height: 26px;
}

.#{$devui-prefix}-input-number--md {
height: 32px;
.#{$devui-prefix}-input-number__input-box {
font-size: $devui-font-size;
height: 100%;
&.#{$devui-prefix}-input-number__control-buttons .control-button {
&:first-child .#{$devui-prefix}-input-number__icon-arrow {
width: 14px;
height: 14px;
}
&:last-child .#{$devui-prefix}-input-number__icon-arrow {
width: 14px;
height: 14px;
}
}
}

.#{$devui-prefix}-input-number__control-buttons .control-button .#{$devui-prefix}-input-number__icon-arrow {
width: 18px;
height: 18px;
.#{$devui-prefix}-input-number__input-wrap {
line-height: 100%;
}
}

.#{$devui-prefix}-input-number--sm {
height: 24px;
.disabled {
cursor: not-allowed;
}

.#{$devui-prefix}-input-number__input-box {
font-size: $devui-font-size-sm;
height: 100%;
.#{$devui-prefix}-input-number__input-box--error:not(.disabled) {
border-color: $devui-danger-line;
background-color: $devui-danger-bg;
}

.#{$devui-prefix}-input-number__control-buttons .control-button .#{$devui-prefix}-input-number__icon-arrow {
width: 16px;
height: 16px;
.#{$devui-prefix}-input-number__control-buttons--error:not(.disabled) {
border-color: $devui-danger-line;
border-left-color: $devui-form-control-line-hover;

span {
background-color: $devui-danger-bg;
}
}
}
12 changes: 11 additions & 1 deletion packages/devui-vue/devui/input-number/src/input-number.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { defineComponent, toRefs } from 'vue';
import { defineComponent, toRefs, watch, inject } from 'vue';
import type { SetupContext } from 'vue';
import { inputNumberProps, InputNumberProps } from './input-number-types';
import { IncIcon, DecIcon } from './input-number-icons';
import { useRender, useEvent, useExpose } from './use-input-number';
import './input-number.scss';
import { FORM_ITEM_TOKEN, FormItemContext } from '@devui/shared/components/form';

export default defineComponent({
name: 'DInputNumber',
Expand All @@ -14,6 +15,14 @@ export default defineComponent({
const { wrapClass, customStyle, otherAttrs, controlButtonsClass, inputWrapClass, inputInnerClass } = useRender(props, ctx);
const { inputRef } = useExpose(ctx);
const { inputVal, minDisabled, maxDisabled, onAdd, onSubtract, onInput, onChange } = useEvent(props, ctx, inputRef);
const formItemContext = inject(FORM_ITEM_TOKEN, undefined) as FormItemContext;

watch(
() => props.modelValue,
() => {
formItemContext?.validate('change').catch(() => {});
}
);

return () => (
<div class={wrapClass.value} {...customStyle}>
Expand All @@ -35,6 +44,7 @@ export default defineComponent({
{...otherAttrs}
onInput={onInput}
onChange={onChange}
onBlur={() => formItemContext?.validate('blur').catch(() => {})}
/>
</div>
</div>
Expand Down
34 changes: 25 additions & 9 deletions packages/devui-vue/devui/input-number/src/use-input-number.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { computed, reactive, toRefs, watch, ref, inject } from 'vue';
import type { SetupContext, Ref, CSSProperties } from 'vue';
import { InputNumberProps, UseEvent, UseRender, IState, UseExpose } from './input-number-types';
import { useNamespace } from '../../shared/hooks/use-namespace';
import { isNumber, isUndefined } from '../../shared/utils';
import { FORM_TOKEN } from '../../form';
import { isNumber, isUndefined, useNamespace } from '@devui/shared/utils';
import { FORM_ITEM_TOKEN, FORM_TOKEN, FormItemContext } from '@devui/shared/components/form';

const ns = useNamespace('input-number');

Expand All @@ -12,27 +11,33 @@ export function useRender(props: InputNumberProps, ctx: SetupContext): UseRender
const { style, class: customClass, ...otherAttrs } = ctx.attrs;
const customStyle = { style: style as CSSProperties };

const formItemContext = inject(FORM_ITEM_TOKEN, undefined) as FormItemContext;
const isValidateError = computed(() => formItemContext?.validateState === 'error');

const inputNumberSize = computed(() => props.size || formContext?.size || 'md');

const wrapClass = computed(() => [
{
[ns.b()]: true,
[ns.m(inputNumberSize.value)]: true,
},
customClass,
]);

const controlButtonsClass = computed(() => ({
[ns.e('control-buttons')]: true,
[ns.em('control-buttons', 'error')]: isValidateError.value,
disabled: props.disabled,
[ns.m(inputNumberSize.value)]: true,
}));

const inputWrapClass = computed(() => ({
[ns.e('input-wrap')]: true,
[ns.m(inputNumberSize.value)]: true,
}));

const inputInnerClass = computed(() => ({
[ns.e('input-box')]: true,
[ns.em('input-box', 'error')]: isValidateError.value,
disabled: props.disabled,
}));

Expand Down Expand Up @@ -85,7 +90,7 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R

const inputVal = computed(() => {
if (!isUndefined(state.userInputValue)) {
return state.userInputValue;
return props.formatter ? props.formatter(state.userInputValue ?? 0) : state.userInputValue;
}
let currentValue = state.currentValue;
if (currentValue === '' || isUndefined(currentValue) || Number.isNaN(currentValue)) {
Expand All @@ -95,7 +100,7 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R
// todo 小数精度 确认是否应该以正则处理
currentValue = currentValue.toFixed(numPrecision.value);
}
return currentValue;
return props.formatter ? props.formatter(currentValue ?? 0) : currentValue;
});

const toPrecision = (num: number) => {
Expand Down Expand Up @@ -187,11 +192,22 @@ export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: R
);

const onInput = (event: Event) => {
state.userInputValue = (event.target as HTMLInputElement).value;
const value = (event.target as HTMLInputElement).value;
if (value[0] === '-') {
state.userInputValue = '-' + value.substring(1).replace(/[^0-9.]/g, '');
} else {
state.userInputValue = value.replace(/[^0-9.]/g, '');
}
inputRef.value.value = props.formatter ? props.formatter(state.userInputValue) : state.userInputValue;
};

const onChange = (event: Event) => {
setCurrentValue((event.target as HTMLInputElement).value);
const onChange = () => {
const value = state.userInputValue;
const newVal = value !== '' ? Number(value) : '';
if ((isNumber(newVal) && !Number.isNaN(newVal)) || value === '') {
setCurrentValue(newVal);
}
state.userInputValue = undefined;
};

return { inputVal, minDisabled, maxDisabled, onAdd, onSubtract, onInput, onChange };
Expand Down
Loading
Loading