Files
web-app-launcher/src/lib/components/app/AppIconPicker.svelte
T
alexei.dolgolyov 44e1849821
CI / test (push) Has been cancelled
CI / docker-build (push) Has been cancelled
CI / lint-and-check (push) Has been cancelled
fix: resolve all linter errors and a11y warnings
- Fix TS errors: editMode property order, implicit any, string|undefined
- Add $state() to bind:this element refs (IconGrid, EntityPicker, etc.)
- Fix a11y: labels, aria-labels, roles, tabindex on dialogs
- Remove unused imports (tick, svelte-i18n)
- Make AutocompleteInput/TagsInput accept optional string values
2026-04-10 19:05:25 +03:00

96 lines
2.9 KiB
Svelte

<script lang="ts">
import { t } from 'svelte-i18n';
import IconGrid from '$lib/components/ui/IconGrid.svelte';
import IconPickerButton from '$lib/components/ui/IconPickerButton.svelte';
import DynamicIcon from '$lib/components/ui/DynamicIcon.svelte';
import type { IconGridItem } from '$lib/components/ui/IconGrid.svelte';
interface Props {
iconType: string;
iconValue: string;
onchange?: (type: string, value: string) => void;
}
let { iconType = $bindable('lucide'), iconValue = $bindable(''), onchange }: Props = $props();
const iconTypeItems: IconGridItem[] = [
{ value: 'lucide', icon: '◇', label: $t('app.icon_lucide') ?? 'Lucide' },
{ value: 'simple', icon: '◆', label: $t('app.icon_simple') ?? 'Simple Icons' },
{ value: 'url', icon: '🔗', label: $t('app.icon_url') ?? 'Image URL' },
{ value: 'emoji', icon: '😀', label: $t('app.icon_emoji') ?? 'Emoji' }
];
function handleTypeChange(value: string) {
iconType = value;
iconValue = '';
onchange?.(iconType, iconValue);
}
function handleValueChange(e: Event) {
const target = e.target as HTMLInputElement;
iconValue = target.value;
onchange?.(iconType, iconValue);
}
function handleIconPickerChange(value: string) {
iconValue = value;
onchange?.(iconType, iconValue);
}
</script>
<div class="space-y-2">
<!-- svelte-ignore a11y_label_has_associated_control -->
<label class="block text-sm font-medium text-card-foreground">{$t('app.icon')}</label>
<div class="flex gap-2">
<div class="w-44 shrink-0">
<IconGrid
items={iconTypeItems}
value={iconType}
onchange={handleTypeChange}
columns={2}
/>
</div>
{#if iconType === 'lucide'}
<div class="flex flex-1 items-start">
<IconPickerButton
value={iconValue}
onchange={handleIconPickerChange}
placeholder={$t('app.icon_lucide_placeholder') ?? 'Select icon'}
/>
</div>
{:else}
<input
type="text"
value={iconValue}
oninput={handleValueChange}
placeholder={iconType === 'simple'
? $t('app.icon_simple_placeholder')
: iconType === 'url'
? $t('app.icon_url_placeholder')
: $t('app.icon_emoji_placeholder')}
class="flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
/>
{/if}
</div>
<!-- Preview -->
{#if iconType === 'lucide' && iconValue}
<div class="flex items-center gap-2">
<DynamicIcon name={iconValue} size={24} />
<span class="text-xs text-muted-foreground">{iconValue}</span>
</div>
{:else if iconType === 'emoji' && iconValue}
<div class="text-2xl">{iconValue}</div>
{:else if iconType === 'url' && iconValue}
<img src={iconValue} alt={$t('app.icon_preview')} class="h-8 w-8 rounded object-contain" />
{:else if iconType === 'simple' && iconValue}
<img
src="https://cdn.simpleicons.org/{iconValue.toLowerCase()}"
alt="{iconValue} icon"
class="h-8 w-8"
/>
{/if}
</div>