Textarea.vue 2.1 KB
<script setup>
/**
 * Textarea —— 受控多行输入框(v-model)。用于 JSON params、长文本消息等。
 */

defineProps({
  /** 受控值。 */
  modelValue: { type: String, default: '' },
  /** 可见行数。 */
  rows: { type: Number, default: 4 },
  /** 占位文案。 */
  placeholder: { type: String, default: '' },
  /** 禁用态。 */
  disabled: { type: Boolean, default: false },
  /** 非法态:描边转 danger 色,并置 aria-invalid。 */
  invalid: { type: Boolean, default: false },
})

const emit = defineEmits(['update:modelValue'])

/**
 * 转发输入值。
 * @param {Event} e
 * @returns {void}
 */
function onInput(e) {
  emit('update:modelValue', /** @type {HTMLTextAreaElement} */ (e.target).value)
}
</script>

<template>
  <textarea
    class="drama-textarea"
    :class="{ 'is-invalid': invalid }"
    :rows="rows"
    :value="modelValue"
    :placeholder="placeholder"
    :disabled="disabled"
    :aria-invalid="invalid ? 'true' : 'false'"
    @input="onInput"
  />
</template>

<style scoped>
.drama-textarea {
  box-sizing: border-box;
  width: 100%;
  padding: var(--drama-space-md);
  border: 1px solid var(--drama-color-border);
  border-radius: var(--drama-radius-sm);
  background-color: var(--drama-color-surface);
  color: var(--drama-color-text);
  font-family: var(--drama-font-family-body);
  font-size: var(--drama-font-size-sm);
  line-height: 1.5;
  resize: vertical;
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}

.drama-textarea::placeholder {
  color: var(--drama-color-text-muted);
}

.drama-textarea:hover:not(:disabled):not(:focus) {
  border-color: var(--drama-color-border-strong);
}

.drama-textarea:focus {
  border-color: var(--drama-color-primary);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--drama-color-primary) 18%, transparent);
}

.drama-textarea:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.drama-textarea.is-invalid {
  border-color: var(--drama-color-danger);
}

.drama-textarea.is-invalid:focus {
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--drama-color-danger) 18%, transparent);
}
</style>