MemPrepMate / src /lib /components /MessageInput.svelte
Christian Kniep
new webapp
1fff71f
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import type { MessageMode } from '$lib/types/enums';
import ModeSelector from './ModeSelector.svelte';
import { validateMessageContent } from '$lib/utils/validators';
export let disabled = false;
export let loading = false;
const dispatch = createEventDispatcher<{
send: { content: string; mode: MessageMode };
}>();
let content = '';
let selectedMode: MessageMode = 'chat';
let error: string | null = null;
function handleKeyDown(event: KeyboardEvent) {
// Cmd/Ctrl + Enter to send
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
event.preventDefault();
handleSubmit();
}
}
function handleSubmit() {
error = null;
// Validate content
const validationError = validateMessageContent(content);
if (validationError) {
error = validationError;
return;
}
// Dispatch send event
dispatch('send', {
content: content.trim(),
mode: selectedMode
});
// Clear input
content = '';
}
$: canSend = content.trim().length > 0 && !loading && !disabled;
</script>
<div class="message-input border-top bg-white p-2 p-md-3">
{#if error}
<div class="alert alert-danger alert-sm mb-2" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
{error}
</div>
{/if}
<div class="input-container">
<!-- Mode Selector -->
<div class="mb-2">
<ModeSelector bind:selectedMode disabled={loading || disabled} />
</div>
<!-- Message Input -->
<div class="d-flex gap-2">
<textarea
class="form-control"
placeholder="Type your message..."
bind:value={content}
on:keydown={handleKeyDown}
{disabled}
rows="2"
maxlength="10000"
></textarea>
<button
type="button"
class="btn btn-primary send-btn"
on:click={handleSubmit}
disabled={!canSend}
title="Send message"
>
{#if loading}
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
{:else}
<i class="bi bi-send-fill"></i>
{/if}
</button>
</div>
<!-- Character Counter -->
<div class="d-flex justify-content-between align-items-center mt-2">
<small class="text-muted d-none d-sm-block">
<i class="bi bi-info-circle me-1"></i>
<span class="d-none d-md-inline">Press Cmd/Ctrl+Enter to send</span>
<span class="d-inline d-md-none">Cmd/Ctrl+Enter</span>
</small>
<small class="text-muted ms-auto">
{content.length} / 10k
</small>
</div>
</div>
</div>
<style>
.message-input {
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}
.input-container {
max-width: 100%;
}
textarea {
resize: vertical;
min-height: 60px;
max-height: 200px;
font-size: 1rem;
}
.send-btn {
min-width: 48px;
height: auto;
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
}
.alert-sm {
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
}
/* Mobile optimizations */
@media (max-width: 576px) {
textarea {
min-height: 50px;
font-size: 16px; /* Prevents zoom on iOS */
}
.send-btn {
min-width: 44px;
padding: 0.5rem;
}
.message-input {
padding: 0.5rem !important;
}
}
/* Tablet and up */
@media (min-width: 768px) {
textarea {
min-height: 80px;
}
}
</style>