ChatInput.vue 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. <script setup lang="ts">
  2. import {ArrowUpIcon, Loader2} from "lucide-vue-next"
  3. import {
  4. InputGroup,
  5. InputGroupAddon,
  6. InputGroupButton,
  7. InputGroupTextarea
  8. } from "@/Packages/Shadcn/Components/ui/input-group"
  9. import {computed, nextTick} from "vue";
  10. const emit = defineEmits(['submit'])
  11. const { disabled } = defineProps({
  12. loading: {
  13. type: Boolean,
  14. default: false
  15. },
  16. disabled: {
  17. type: Boolean,
  18. default: false
  19. },
  20. })
  21. const value = defineModel({required: true, default: String})
  22. const isEmpty = computed(() => String(value.value ?? '').trim().length === 0)
  23. const submit = () => {
  24. if (disabled || isEmpty.value) return
  25. emit('submit', value.value)
  26. }
  27. const handleEnter = e => {
  28. if (e.metaKey || e.ctrlKey || e.shiftKey) {
  29. const {selectionStart, selectionEnd, value} = e.target
  30. value.value = value.slice(0, selectionStart) + '\n' + value.slice(selectionEnd)
  31. nextTick(() => {
  32. const pos = selectionStart + 1
  33. e.target.setSelectionRange(pos, pos)
  34. })
  35. } else submit()
  36. }
  37. </script>
  38. <template>
  39. <InputGroup class="!bg-card">
  40. <InputGroupTextarea placeholder="Ask, Search or Chat..." v-model="value"
  41. @keydown.enter.prevent="handleEnter"/>
  42. <InputGroupAddon align="block-end">
  43. <InputGroupButton class="ml-auto cursor-pointer" variant="default" :disabled="disabled || isEmpty"
  44. @click="submit">
  45. <template v-if="!loading">
  46. <ArrowUpIcon class="size-5"/>
  47. <span>Отправить</span>
  48. </template>
  49. <template v-else>
  50. <Loader2 class="w-4 h-4 animate-spin"/>
  51. <span>Отправка</span>
  52. </template>
  53. </InputGroupButton>
  54. </InputGroupAddon>
  55. </InputGroup>
  56. </template>
  57. <style scoped>
  58. </style>