Files
website/src/lib/components/tui/TuiCheckbox.svelte
T
2025-11-29 16:40:05 +00:00

142 lines
3.2 KiB
Svelte

<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-checkbox.css';
export let line: TerminalLine;
export let inline: boolean = false;
export let checked: boolean = false;
const dispatch = createEventDispatcher<{
change: boolean;
}>();
$: labelSegments = line.content ? parseColorText(line.content, $themeColors.colorMap) : [];
$: isDisabled = line.inputDisabled || false;
$: indeterminate = line.checkboxIndeterminate || false;
function handleChange() {
if (isDisabled) return;
checked = !checked;
dispatch('change', checked);
}
function handleKeydown(e: KeyboardEvent) {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
handleChange();
}
}
function getCheckboxSymbol(checked: boolean, indeterminate: boolean): string {
if (indeterminate) return '[-]';
return checked ? '[✓]' : '[ ]';
}
</script>
<div
class="tui-checkbox"
class:inline={inline}
class:checked={checked}
class:disabled={isDisabled}
style="--checkbox-color: {getButtonStyle(line.style)}"
role="checkbox"
aria-checked={indeterminate ? 'mixed' : checked}
aria-disabled={isDisabled}
tabindex={isDisabled ? -1 : 0}
on:click={handleChange}
on:keydown={handleKeydown}
>
<span class="checkbox-box" class:indeterminate={indeterminate}>
{getCheckboxSymbol(checked, indeterminate)}
</span>
{#if line.icon}
<Icon icon={line.icon} width="14" class="checkbox-icon" />
{/if}
{#if line.content}
<span class="checkbox-label">
{#each labelSegments as segment}
{#if segment.icon}
<Icon icon={segment.icon} width="14" class="inline-icon" />
{:else if getSegmentStyle(segment)}
<span style={getSegmentStyle(segment)}>{segment.text}</span>
{:else}
{segment.text}
{/if}
{/each}
</span>
{/if}
</div>
<style>
.tui-checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
margin: 0.35rem 0;
padding: 0.35rem 0.5rem;
font-size: 0.9rem;
cursor: pointer;
border-radius: 4px;
transition: all 0.15s ease;
user-select: none;
}
.tui-checkbox.inline {
display: inline-flex;
margin: 0 0.75rem 0 0;
}
.tui-checkbox:hover:not(.disabled) {
background: color-mix(in srgb, var(--checkbox-color) 10%, transparent);
}
.tui-checkbox:focus-visible {
outline: 1px solid var(--checkbox-color);
outline-offset: 2px;
}
.tui-checkbox.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.checkbox-box {
font-family: inherit;
font-weight: bold;
color: var(--terminal-muted);
transition: color 0.15s ease;
}
.tui-checkbox.checked .checkbox-box,
.checkbox-box.indeterminate {
color: var(--checkbox-color);
}
:global(.checkbox-icon) {
color: var(--checkbox-color);
}
.checkbox-label {
color: var(--terminal-text);
}
.tui-checkbox.disabled .checkbox-label {
color: var(--terminal-muted);
}
/* Mobile: inline checkboxes become full-width */
@media (max-width: 768px) {
.tui-checkbox.inline {
display: flex;
width: 100%;
margin: 0.35rem 0;
}
}
</style>