TUI Renderer, Element Types, and Page Updates

This commit is contained in:
2025-11-30 18:26:56 -05:00
parent f09c198f17
commit 1167c686e2
31 changed files with 1642 additions and 809 deletions
+49 -38
View File
@@ -1,59 +1,69 @@
<script lang="ts">
import Icon from '@iconify/svelte';
import { getButtonStyle, parseColorText, getSegmentStyle } from './utils';
import { themeColors } from '$lib/stores/theme';
import type { TerminalLine } from './types';
import { createEventDispatcher } from 'svelte';
import '$lib/assets/css/tui-textarea.css';
import Icon from "@iconify/svelte";
import { getButtonStyle, parseColorText, getSegmentStyle } from "./utils";
import { themeColors } from "$lib/stores/theme";
import type { TextareaLine } from "./types";
import "$lib/assets/css/tui-textarea.css";
export let line: TerminalLine;
export let inline: boolean = false;
export let value: string = '';
interface Props {
line: TextareaLine;
inline?: boolean;
value?: string;
oninput?: (value: string) => void;
onchange?: (value: string) => void;
onfocus?: () => void;
onblur?: () => void;
}
const dispatch = createEventDispatcher<{
input: string;
change: string;
focus: void;
blur: void;
}>();
let {
line,
inline = false,
value = $bindable(""),
oninput,
onchange,
onfocus,
onblur,
}: Props = $props();
$: labelSegments = line.content ? parseColorText(line.content, $themeColors.colorMap) : [];
$: placeholder = line.inputPlaceholder || '';
$: isDisabled = line.inputDisabled || false;
$: hasError = line.inputError;
$: errorMessage = line.inputErrorMessage || '';
$: rows = line.textareaRows || 4;
$: maxLength = line.textareaMaxLength;
const labelSegments = $derived(
line.content ? parseColorText(line.content, $themeColors.colorMap) : [],
);
const placeholder = $derived(line.inputPlaceholder || "");
const isDisabled = $derived(line.inputDisabled || false);
const hasError = $derived(line.inputError);
const errorMessage = $derived(line.inputErrorMessage || "");
const rows = $derived(line.textareaRows || 4);
const maxLength = $derived(line.textareaMaxLength);
let isFocused = false;
let charCount = 0;
let isFocused = $state(false);
let charCount = $state(value.length);
function handleInput(e: Event) {
const target = e.target as HTMLTextAreaElement;
value = target.value;
charCount = value.length;
dispatch('input', value);
oninput?.(value);
}
function handleChange(e: Event) {
const target = e.target as HTMLTextAreaElement;
dispatch('change', target.value);
onchange?.(target.value);
}
function handleFocus() {
isFocused = true;
dispatch('focus');
onfocus?.();
}
function handleBlur() {
isFocused = false;
dispatch('blur');
onblur?.();
}
</script>
<div
class="tui-textarea"
class:inline={inline}
<div
class="tui-textarea"
class:inline
class:focused={isFocused}
class:error={hasError}
class:disabled={isDisabled}
@@ -75,10 +85,10 @@
{/each}
</label>
{/if}
<div class="textarea-wrapper">
<div class="line-numbers">
{#each Array(Math.max(rows, value.split('\n').length)) as _, i}
{#each Array(Math.max(rows, value.split("\n").length)) as _, i}
<span class="line-num">{i + 1}</span>
{/each}
</div>
@@ -88,10 +98,10 @@
maxlength={maxLength}
disabled={isDisabled}
bind:value
on:input={handleInput}
on:change={handleChange}
on:focus={handleFocus}
on:blur={handleBlur}
oninput={handleInput}
onchange={handleChange}
onfocus={handleFocus}
onblur={handleBlur}
></textarea>
</div>
@@ -146,7 +156,8 @@
.tui-textarea.focused .textarea-wrapper {
border-color: var(--input-color);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--input-color) 30%, transparent);
box-shadow: 0 0 0 1px
color-mix(in srgb, var(--input-color) 30%, transparent);
}
.tui-textarea.error .textarea-wrapper {