lastwebnovel-app/app/components/ReaderControls.vue
2026-04-11 22:55:16 +02:00

184 lines
4.8 KiB
Vue

<script setup lang="ts">
import type { ReaderPreferences } from '~/types'
const toast = useToast()
const props = defineProps<{
preferences: ReaderPreferences
backgroundColor?: string
textColor?: string
}>()
const emit = defineEmits<{
update: [preferences: Partial<ReaderPreferences>]
}>()
const readingPresets = [
{
id: 'book',
name: 'Book',
description: 'Serif, Cream',
settings: { fontFamily: 'serif', backgroundColor: 'cream', textColor: 'black' }
},
{
id: 'modern',
name: 'Modern',
description: 'Sans-serif, White',
settings: { fontFamily: 'sans-serif', backgroundColor: 'white', textColor: 'black' }
},
{
id: 'dark',
name: 'Dark',
description: 'Serif, Black',
settings: { fontFamily: 'serif', backgroundColor: 'black', textColor: 'white' }
}
]
const updateFontSize = (value: number) => {
emit('update', { fontSize: value })
}
const updateLineHeight = (value: number) => {
emit('update', { lineHeight: value })
}
const applyPreset = (preset: typeof readingPresets[0]) => {
emit('update', preset.settings)
}
const updatePreference = (key: keyof ReaderPreferences, value: any) => {
emit('update', { [key]: value })
}
const getCurrentPreset = () => {
return readingPresets.find(
p => p.settings.fontFamily === props.preferences.fontFamily
&& p.settings.backgroundColor === props.preferences.backgroundColor
&& p.settings.textColor === props.preferences.textColor
)?.id
}
const handleSave = () => {
emit('save')
toast.add({
title: 'Settings Saved',
description: 'Your reader preferences have been saved.',
icon: 'i-lucide-check-circle',
color: 'green'
})
}
const handleReset = () => {
emit('reset')
toast.add({
title: 'Settings Reset',
description: 'Your reader preferences have been reset to default.',
icon: 'i-lucide-undo-2',
color: 'blue'
})
}
</script>
<template>
<div
class="space-y-4 p-4 rounded-lg transition-colors duration-300"
:style="{
backgroundColor: props.backgroundColor || '#f9fafb',
color: props.textColor || '#000000'
}"
>
<p class="text-lg font-bold mb-4">
Reader Settings
</p>
<div>
<label class="text-xs font-semibold mb-2 block">Font Size: {{ preferences.fontSize }}px</label>
<div class="flex items-center gap-3">
<UIcon name="i-lucide-type" class="size-4" />
<input
type="range"
:value="preferences.fontSize"
min="12"
max="24"
class="flex-1"
@input="e => updateFontSize(Number((e.target as HTMLInputElement).value))"
>
<span class="text-xs w-8">{{ preferences.fontSize }}</span>
</div>
</div>
<div>
<label class="text-xs font-semibold mb-2 block">Line Height: {{ preferences.lineHeight.toFixed(1) }}</label>
<div class="flex items-center gap-3">
<UIcon name="i-lucide-maximize-2" class="size-4" />
<input
type="range"
:value="preferences.lineHeight"
min="1.5"
max="2.5"
step="0.1"
class="flex-1"
@input="e => updateLineHeight(Number((e.target as HTMLInputElement).value))"
>
<span class="text-xs w-8">{{ preferences.lineHeight.toFixed(1) }}</span>
</div>
</div>
<div>
<label class="text-xs font-semibold mb-3 block">Reading Presets</label>
<div class="grid grid-cols-3 gap-2">
<button
v-for="preset in readingPresets"
:key="preset.id"
:class="[
'p-2 rounded-lg text-xs font-medium transition-all duration-200 border-2',
getCurrentPreset() === preset.id
? 'border-current font-bold bg-opacity-20'
: 'border-transparent opacity-60 hover:opacity-100'
]"
@click="applyPreset(preset)"
>
<div class="font-semibold">
{{ preset.name }}
</div>
<div class="text-xs opacity-75">
{{ preset.description }}
</div>
</button>
</div>
</div>
<div class="flex items-center gap-2 py-3">
<input
type="checkbox"
:checked="preferences.textJustify"
class="rounded"
@change="e => updatePreference('textJustify', (e.target as HTMLInputElement).checked)"
>
<label class="text-xs font-semibold">Justify Text</label>
</div>
<div class="flex gap-2 pt-4">
<UButton
size="lg"
variant="subtle"
color="info"
icon="i-lucide-undo-2"
class="flex-1"
@click="handleReset"
>
Reset
</UButton>
<UButton
size="lg"
variant="subtle"
icon="i-lucide-check"
class="flex-1"
@click="handleSave"
>
Save
</UButton>
</div>
</div>
</template>