diff --git a/packages/devui-vue/devui/textarea/__tests__/textarea.spec.ts b/packages/devui-vue/devui/textarea/__tests__/textarea.spec.ts
index a0d3544405..538cbdec0d 100644
--- a/packages/devui-vue/devui/textarea/__tests__/textarea.spec.ts
+++ b/packages/devui-vue/devui/textarea/__tests__/textarea.spec.ts
@@ -1,9 +1,9 @@
import { mount } from '@vue/test-utils';
import { ref, nextTick } from 'vue';
import DTextarea from '../src/textarea';
-import { useNamespace } from '../../shared/hooks/use-namespace';
+import { useNamespace } from '@devui/shared/utils';
-const ns = useNamespace('textarea', true);
+const ns = useNamespace('textarea');
describe('textarea test', () => {
it('d-textarea render work', async () => {
@@ -20,7 +20,7 @@ describe('textarea test', () => {
},
});
const textarea = wrapper.find('textarea');
- expect(textarea.classes()).toContain('devui-textarea');
+ expect(textarea.classes()).toContain(ns.b());
expect(textarea.element.value).toBe('abc');
await textarea.setValue('def');
@@ -100,12 +100,12 @@ describe('textarea test', () => {
error: false,
},
});
- expect(wrapper.find('textarea').classes()).not.toContain('devui-textarea--error');
+ expect(wrapper.find('textarea').classes()).not.toContain(ns.m('error'));
await wrapper.setProps({
error: true,
});
- expect(wrapper.find('textarea').classes()).toContain('devui-textarea--error');
+ expect(wrapper.find('textarea').classes()).toContain(ns.m('error'));
});
it('d-textarea autosize work', async () => {
@@ -138,7 +138,7 @@ describe('textarea test', () => {
template: `
`,
- setup () {
+ setup() {
return {
value,
};
diff --git a/packages/devui-vue/devui/textarea/src/composables/use-textarea-event.ts b/packages/devui-vue/devui/textarea/src/composables/use-textarea-event.ts
index c29ce27f64..c65182549d 100644
--- a/packages/devui-vue/devui/textarea/src/composables/use-textarea-event.ts
+++ b/packages/devui-vue/devui/textarea/src/composables/use-textarea-event.ts
@@ -1,9 +1,10 @@
-import { inject, Ref, SetupContext } from 'vue';
+import { inject, Ref, SetupContext, ref } from 'vue';
import { FormItemContext, FORM_ITEM_TOKEN } from '../../../form';
import { TextareaProps, UseTextareaEvent } from '../textarea-types';
-export function useTextareaEvent(isFocus: Ref, props: TextareaProps, ctx: SetupContext): UseTextareaEvent {
+export function useTextareaEvent(isFocus: Ref, props: TextareaProps, ctx: SetupContext) {
const formItemContext = inject(FORM_ITEM_TOKEN, undefined) as FormItemContext;
+ const isComposition = ref(false);
const onFocus = (e: FocusEvent) => {
isFocus.value = true;
ctx.emit('focus', e);
@@ -18,6 +19,9 @@ export function useTextareaEvent(isFocus: Ref, props: TextareaProps, ct
};
const onInput = (e: Event) => {
+ if (isComposition.value) {
+ return;
+ }
ctx.emit('update:modelValue', (e.target as HTMLInputElement).value);
ctx.emit('update', (e.target as HTMLInputElement).value);
};
@@ -30,5 +34,22 @@ export function useTextareaEvent(isFocus: Ref, props: TextareaProps, ct
ctx.emit('keydown', e);
};
- return { onFocus, onBlur, onInput, onChange, onKeydown };
+ const onCompositionStart = () => {
+ isComposition.value = true;
+ };
+
+ const onCompositionUpdate = (e: CompositionEvent) => {
+ const text = (e.target as HTMLInputElement)?.value;
+ const lastCharacter = text[text.length - 1] || '';
+ isComposition.value = !/([(\uAC00-\uD7AF)|(\u3130-\u318F)])+/gi.test(lastCharacter);
+ };
+
+ const onCompositionEnd = (e: CompositionEvent) => {
+ if (isComposition.value) {
+ isComposition.value = false;
+ onInput(e);
+ }
+ };
+
+ return { onFocus, onBlur, onInput, onChange, onKeydown, onCompositionStart, onCompositionUpdate, onCompositionEnd };
}
diff --git a/packages/devui-vue/devui/textarea/src/composables/use-textarea-render.ts b/packages/devui-vue/devui/textarea/src/composables/use-textarea-render.ts
index e017d78efc..19549ec8e2 100644
--- a/packages/devui-vue/devui/textarea/src/composables/use-textarea-render.ts
+++ b/packages/devui-vue/devui/textarea/src/composables/use-textarea-render.ts
@@ -1,7 +1,7 @@
import { computed, toRefs, ref, inject } from 'vue';
-import { FORM_TOKEN, FormContext, FORM_ITEM_TOKEN, FormItemContext } from '../../../form';
+import { FORM_TOKEN, FormContext, FORM_ITEM_TOKEN, FormItemContext, STYLE_TOKEN } from '../../../form';
import { TextareaProps, UseTextareaRender } from '../textarea-types';
-import { useNamespace } from '../../../shared/hooks/use-namespace';
+import { useNamespace } from '@devui/shared/utils';
export function useTextareaRender(props: TextareaProps): UseTextareaRender {
const formContext = inject(FORM_TOKEN, undefined) as FormContext;
@@ -12,12 +12,15 @@ export function useTextareaRender(props: TextareaProps): UseTextareaRender {
const { error, disabled } = toRefs(props);
const textareaDisabled = computed(() => disabled.value || formContext?.disabled);
+ const styleType = inject(STYLE_TOKEN, undefined);
+
const wrapClasses = computed(() => ({
[ns.b()]: true,
[ns.m('focus')]: isFocus.value,
[ns.m('disabled')]: textareaDisabled.value,
[ns.m('error')]: error.value || isValidateError.value,
[ns.m('feedback')]: Boolean(formItemContext?.validateState) && formItemContext?.showFeedback,
+ [ns.m('gary-style')]: styleType === 'gray',
}));
return { isFocus, textareaDisabled, wrapClasses };
diff --git a/packages/devui-vue/devui/textarea/src/textarea.scss b/packages/devui-vue/devui/textarea/src/textarea.scss
index 214663495b..23d08f4172 100644
--- a/packages/devui-vue/devui/textarea/src/textarea.scss
+++ b/packages/devui-vue/devui/textarea/src/textarea.scss
@@ -4,6 +4,8 @@
width: 100%;
box-sizing: border-box;
padding: 4px 8px;
+ font-size: $devui-font-size;
+ font-family: inherit;
color: var(--devui-text, #252b3a);
vertical-align: middle;
outline: none;
@@ -12,6 +14,10 @@
background: $devui-form-control-bg;
transition: border-color 0.3s $devui-animation-ease-in-out-smooth;
+ &::placeholder {
+ color: $devui-placeholder;
+ }
+
&:not(.#{$devui-prefix}-textarea--error):not(.#{$devui-prefix}-textarea--disabled):not(.#{$devui-prefix}-textarea--focus):hover {
border-color: $devui-form-control-line-hover;
}
@@ -52,4 +58,14 @@
&--feedback {
padding-right: 28px;
}
+
+ &--gray-style:not(.#{$devui-prefix}-textarea--disabled):not(.#{$devui-prefix}-textarea--error) {
+ background: $devui-gray-5;
+ border-color: $devui-gray-5;
+
+ &:hover {
+ background: $devui-gray-10;
+ border-color: $devui-gray-10 !important;
+ }
+ }
}
diff --git a/packages/devui-vue/devui/textarea/src/textarea.tsx b/packages/devui-vue/devui/textarea/src/textarea.tsx
index 5c7152f00f..2c2eeeef3c 100644
--- a/packages/devui-vue/devui/textarea/src/textarea.tsx
+++ b/packages/devui-vue/devui/textarea/src/textarea.tsx
@@ -1,7 +1,7 @@
import { defineComponent, inject, nextTick, onMounted, SetupContext, shallowRef, toRefs, watch } from 'vue';
import { FORM_ITEM_TOKEN, FormItemContext } from '../../form';
import { textareaProps, TextareaProps } from './textarea-types';
-import { useNamespace } from '../../shared/hooks/use-namespace';
+import { useNamespace } from '@devui/shared/utils';
import { useTextareaRender } from './composables/use-textarea-render';
import { useTextareaEvent } from './composables/use-textarea-event';
import { useTextareaAutosize } from './composables/use-textarea-autosize';
@@ -18,7 +18,11 @@ export default defineComponent({
const textarea = shallowRef();
const ns = useNamespace('textarea');
const { isFocus, textareaDisabled, wrapClasses } = useTextareaRender(props);
- const { onFocus, onBlur, onInput, onChange, onKeydown } = useTextareaEvent(isFocus, props, ctx);
+ const { onFocus, onBlur, onInput, onChange, onKeydown, onCompositionStart, onCompositionUpdate, onCompositionEnd } = useTextareaEvent(
+ isFocus,
+ props,
+ ctx
+ );
const { textareaStyle, updateTextareaStyle } = useTextareaAutosize(props, textarea);
watch(
@@ -46,6 +50,9 @@ export default defineComponent({
disabled={textareaDisabled.value}
style={textareaStyle.value}
class={wrapClasses.value}
+ onCompositionstart={onCompositionStart}
+ onCompositionupdate={onCompositionUpdate}
+ onCompositionend={onCompositionEnd}
onInput={onInput}
onFocus={onFocus}
onBlur={onBlur}