185 lines
4.5 KiB
TypeScript
185 lines
4.5 KiB
TypeScript
import { LitElement, html, css } from './plugins.js';
|
|
import type { CSSResult, TemplateResult } from './plugins.js';
|
|
|
|
export class SmartchatInput extends LitElement {
|
|
declare disabled: boolean;
|
|
declare placeholder: string;
|
|
private value = '';
|
|
|
|
static properties = {
|
|
disabled: { type: Boolean },
|
|
placeholder: { type: String },
|
|
};
|
|
|
|
static styles: CSSResult = css`
|
|
:host {
|
|
display: block;
|
|
}
|
|
|
|
.input-wrap {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 8px;
|
|
padding: 10px 12px;
|
|
background: rgba(255, 255, 255, 0.04);
|
|
border-radius: 16px;
|
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
|
|
.input-wrap:focus-within {
|
|
border-color: rgba(99, 102, 241, 0.45);
|
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.08);
|
|
}
|
|
|
|
.input-wrap.disabled {
|
|
opacity: 0.45;
|
|
pointer-events: none;
|
|
}
|
|
|
|
textarea {
|
|
flex: 1;
|
|
background: transparent;
|
|
border: none;
|
|
outline: none;
|
|
color: #e4e4e7;
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
line-height: 1.5;
|
|
resize: none;
|
|
min-height: 22px;
|
|
max-height: 110px;
|
|
padding: 2px 0;
|
|
}
|
|
|
|
textarea::placeholder {
|
|
color: rgba(255, 255, 255, 0.22);
|
|
}
|
|
|
|
.send-btn {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
background: linear-gradient(135deg, #6366f1, #7c3aed);
|
|
color: #fff;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
transition: transform 0.12s ease, opacity 0.12s ease, box-shadow 0.12s ease;
|
|
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
|
|
}
|
|
|
|
.send-btn:hover:not(:disabled) {
|
|
transform: scale(1.08);
|
|
box-shadow: 0 3px 12px rgba(99, 102, 241, 0.45);
|
|
}
|
|
|
|
.send-btn:active:not(:disabled) {
|
|
transform: scale(0.94);
|
|
}
|
|
|
|
.send-btn:disabled {
|
|
opacity: 0.25;
|
|
cursor: default;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.send-btn svg {
|
|
width: 15px;
|
|
height: 15px;
|
|
}
|
|
|
|
.hint {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 5px 2px 0;
|
|
}
|
|
|
|
.hint-text {
|
|
font-size: 10.5px;
|
|
color: rgba(255, 255, 255, 0.13);
|
|
}
|
|
|
|
kbd {
|
|
padding: 0 3px;
|
|
border-radius: 3px;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
|
font-family: inherit;
|
|
font-size: 10px;
|
|
}
|
|
`;
|
|
|
|
constructor() {
|
|
super();
|
|
this.disabled = false;
|
|
this.placeholder = 'Type a message...';
|
|
}
|
|
|
|
private handleKeydown(e: KeyboardEvent) {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
this.submit();
|
|
}
|
|
}
|
|
|
|
private handleInput(e: Event) {
|
|
const textarea = e.target as HTMLTextAreaElement;
|
|
this.value = textarea.value;
|
|
textarea.style.height = 'auto';
|
|
textarea.style.height = Math.min(textarea.scrollHeight, 110) + 'px';
|
|
}
|
|
|
|
private submit() {
|
|
if (!this.value.trim() || this.disabled) return;
|
|
this.dispatchEvent(
|
|
new CustomEvent('send', {
|
|
detail: { text: this.value.trim() },
|
|
bubbles: true,
|
|
composed: true,
|
|
}),
|
|
);
|
|
this.value = '';
|
|
const textarea = this.shadowRoot?.querySelector('textarea');
|
|
if (textarea) {
|
|
textarea.value = '';
|
|
textarea.style.height = 'auto';
|
|
}
|
|
}
|
|
|
|
render(): TemplateResult {
|
|
const canSend = !this.disabled && !!this.value.trim();
|
|
return html`
|
|
<div class="input-wrap ${this.disabled ? 'disabled' : ''}">
|
|
<textarea
|
|
rows="1"
|
|
.value=${this.value}
|
|
@input=${this.handleInput}
|
|
@keydown=${this.handleKeydown}
|
|
placeholder=${this.disabled ? 'Waiting for response...' : this.placeholder}
|
|
?disabled=${this.disabled}
|
|
></textarea>
|
|
<button
|
|
class="send-btn"
|
|
@click=${this.submit}
|
|
?disabled=${!canSend}
|
|
aria-label="Send message"
|
|
>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="12" y1="19" x2="12" y2="5"></line>
|
|
<polyline points="5 12 12 5 19 12"></polyline>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="hint">
|
|
<span class="hint-text"><kbd>Enter</kbd> send · <kbd>Shift+Enter</kbd> new line</span>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define('smartchat-input', SmartchatInput);
|