Files
kontextwerk/frontend/src/lib/components/chatbotDemo/InputRow.svelte
2025-10-06 15:26:28 +00:00

219 lines
6.4 KiB
Svelte

<script lang="ts">
import { mdiSendVariantOutline } from "@mdi/js"
import Icon from "../widgets/Icon.svelte"
import { tick } from "svelte"
let {
onSend,
disabled = false,
quickMessages = [
{
title: "Erzähl mir was über",
highlight: "Kontextwerk",
message: "Erzähl mir bitte mehr über Kontextwerk.",
},
{
title: "Ich möchte euch",
highlight: "kontaktieren",
message: "Ich möchte euch kontaktieren. Welche Möglichkeiten gibt es?",
},
],
}: {
onSend: (message: string) => void
disabled?: boolean
quickMessages?: Array<{ title: string; highlight: string; message: string }>
} = $props()
let value = $state("")
let textareaEl: HTMLTextAreaElement | null = $state(null)
function autoResize() {
if (textareaEl) {
textareaEl.style.height = "auto"
textareaEl.style.height = textareaEl.scrollHeight + "px"
}
}
let submittedOnce = $state(false)
const submit = async () => {
submittedOnce = true
const text = value.trim()
if (!text || disabled) return
onSend(text)
value = ""
await tick()
autoResize()
}
$effect(() => {
void tick().then(autoResize)
})
</script>
<div class="input-row">
{#if quickMessages.length && !submittedOnce}
<div class="quick-actions">
{#each quickMessages as quick, index (index)}
<button
class="quick-chip"
type="button"
{disabled}
onclick={() => {
if (!disabled) {
onSend(quick.message)
}
}}
>
<span class="title">{quick.title}</span>
<span class="highlight">{quick.highlight}</span>
</button>
{/each}
</div>
{/if}
<textarea
placeholder="Schreibe eine Nachricht..."
bind:value
rows="1"
bind:this={textareaEl}
oninput={autoResize}
{disabled}
onkeydown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
void submit()
}
}}
></textarea>
<button
class="btn"
onclick={() => void submit()}
disabled={disabled || !value.trim()}
aria-label="Nachricht senden"
>
<Icon
path={mdiSendVariantOutline}
size="1.4rem"
/>
</button>
</div>
<style lang="less">
@import "../../assets/css/variables.less";
.input-row {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-end;
position: relative;
gap: 0.75rem;
.quick-actions {
width: 100%;
display: grid;
gap: 0.9rem;
padding: 0 0.6rem;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
.quick-chip {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
padding: 0.9rem 1.15rem;
border-radius: 4px;
position: relative;
overflow: hidden;
background:
radial-gradient(140% 160% at 15% -10%, rgba(173, 81, 76, 0.38) 0%, rgba(13, 12, 12, 0.88) 55%),
rgba(13, 12, 12, 0.82);
color: var(--neutral-white);
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow:
0 18px 36px rgba(0, 0, 0, 0.4),
inset 0 0 0 1px rgba(255, 255, 255, 0.04);
backdrop-filter: blur(12px);
cursor: pointer;
gap: 0.35rem;
transition:
transform 0.3s ease,
box-shadow 0.3s ease,
border-color 0.3s ease;
&::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: linear-gradient(130deg, rgba(255, 255, 255, 0.08), transparent 60%);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
&:hover:not(:disabled) {
transform: translateY(-4px);
box-shadow:
0 24px 48px rgba(0, 0, 0, 0.5),
inset 0 0 0 1px rgba(255, 255, 255, 0.08);
border-color: rgba(173, 81, 76, 0.55);
&::after {
opacity: 1;
}
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.title {
font-size: 0.78rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.7);
}
.highlight {
font-size: 1rem;
font-weight: 600;
color: var(--text-200);
text-transform: capitalize;
}
}
}
textarea {
width: 100%;
min-height: 3.6rem;
padding: 0.6rem;
border-radius: 1.2rem;
padding-bottom: 2.4rem;
max-height: 12rem;
line-height: 1.4rem;
resize: none;
overflow: hidden;
color: white;
border-top: 1px solid white;
transition: border-top 0.3s ease;
&:focus {
outline: none;
border-top: 1px solid var(--primary-100);
}
}
.btn {
position: absolute;
bottom: 0.6rem;
right: 0.6rem;
height: 2rem;
width: 2.4rem;
min-width: unset;
background-color: var(--primary-100);
border-radius: 4px;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
}
</style>