184 lines
4.8 KiB
Vue
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>
|