🐛 Fix(custom): fix an issue of per-picbed watermark type setting button

This commit is contained in:
Kuingsmile
2026-01-21 14:34:54 +08:00
parent 0801bf063f
commit 8357d3acb5
7 changed files with 109 additions and 625 deletions

View File

@@ -21,3 +21,4 @@
- 修复了管理页面中排序下拉框显示异常的问题 - 修复了管理页面中排序下拉框显示异常的问题
- 修复了暗色模式下任务页面的显示问题 - 修复了暗色模式下任务页面的显示问题
- 修复了图床设置页面设置为默认图床按钮状态没有及时更新的问题 - 修复了图床设置页面设置为默认图床按钮状态没有及时更新的问题
- 修复了预处理设置页面,图床水印独立设置的按钮状态没有及时更新的问题

View File

@@ -21,3 +21,4 @@
- Fixed an issue where the sort dropdown in the management page displayed abnormally - Fixed an issue where the sort dropdown in the management page displayed abnormally
- Fixed an issue where the task page displayed incorrectly in dark mode - Fixed an issue where the task page displayed incorrectly in dark mode
- Fixed an issue where the default image bed button status on the image bed settings page did not update in time - Fixed an issue where the default image bed button status on the image bed settings page did not update in time
- Fixed an issue where the button status for independent watermark settings on the image bed watermark settings page did not update in time

View File

@@ -1,25 +1,42 @@
<template> <template>
<Teleport to="body"> <Teleport to="body">
<Transition name="inputbox-fade"> <Transition
<div v-if="showInputBoxVisible" class="inputbox-overlay" @click="handleInputBoxCancel"> name="inputbox-fade"
enter-active-class="transition-all duration-200 ease-apple"
leave-active-class="transition-all duration-200 ease-apple"
enter-from-class="opacity-0"
leave-to-class="opacity-0"
>
<div
v-if="showInputBoxVisible"
class="fixed inset-0 z-1000 flex items-center justify-center overflow-y-auto bg-black/30"
:class="{ 'advanced-animation': enableAdvancedAnimation }"
>
<Transition name="inputbox-scale"> <Transition name="inputbox-scale">
<div v-if="showInputBoxVisible" class="inputbox-container" @click.stop> <div
<button class="inputbox-close" @click="handleInputBoxCancel"> v-if="showInputBoxVisible"
class="fkex-col relative m-auto flex w-full max-w-[30rem] flex-col overflow-hidden rounded-2xl border border-border-secondary bg-bg-tertiary shadow-xl"
@click.stop
>
<button
class="absolute top-4 right-4 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full border border-border bg-surface-elevated text-secondary transition-all duration-fast ease-apple hover:scale-105 hover:border-danger hover:bg-danger hover:text-white focus-visible:focus-ring"
@click="handleInputBoxCancel"
>
<XIcon :size="20" /> <XIcon :size="20" />
</button> </button>
<div class="inputbox-body"> <div class="p-4">
<h3 class="inputbox-title"> <h3 class="mb-4 pr-8 text-lg leading-[1.4] font-semibold text-main">
{{ inputBoxOptions.title || t('pages.inputBox.title') }} {{ inputBoxOptions.title || t('pages.inputBox.title') }}
</h3> </h3>
<div class="inputbox-content"> <div class="relative">
<textarea <textarea
v-if="inputBoxOptions.multiLine" v-if="inputBoxOptions.multiLine"
ref="textareaRef" ref="textareaRef"
v-model="inputBoxValue" v-model="inputBoxValue"
:placeholder="inputBoxOptions.placeholder" :placeholder="inputBoxOptions.placeholder"
class="inputbox-textarea" class="max-h-[20rem] min-h-[6rem] w-full resize-y rounded-sm border border-border bg-bg-tertiary p-4 font-[inherit] text-[0.9375rem] text-main transition-all duration-fast ease-apple outline-none placeholder:text-secondary hover:border-accent focus:border-accent focus:bg-surface"
rows="4" rows="4"
@keyup.ctrl.enter="handleInputBoxConfirm" @keyup.ctrl.enter="handleInputBoxConfirm"
@keyup.meta.enter="handleInputBoxConfirm" @keyup.meta.enter="handleInputBoxConfirm"
@@ -30,7 +47,7 @@
ref="inputRef" ref="inputRef"
v-model="inputBoxValue" v-model="inputBoxValue"
:placeholder="inputBoxOptions.placeholder" :placeholder="inputBoxOptions.placeholder"
class="inputbox-input" class="w-full rounded-sm border border-border bg-bg-tertiary p-4 font-[inherit] text-[0.9375rem] text-main transition-all duration-fast ease-apple outline-none placeholder:text-secondary hover:border-accent focus:border-accent focus:bg-surface"
type="text" type="text"
@keyup.enter="handleInputBoxConfirm" @keyup.enter="handleInputBoxConfirm"
@keyup.escape="handleInputBoxCancel" @keyup.escape="handleInputBoxCancel"
@@ -38,11 +55,18 @@
</div> </div>
</div> </div>
<div class="inputbox-actions"> <div class="flex flex-wrap gap-3 p-4">
<button class="inputbox-btn cancel-btn" @click="handleInputBoxCancel"> <button
class="flex-1 rounded-sm border-none bg-danger/50 p-2.5 text-sm font-semibold text-secondary transition-colors duration-fast ease-apple hover:bg-danger/70"
@click="handleInputBoxCancel"
>
{{ t('common.cancel') }} {{ t('common.cancel') }}
</button> </button>
<button class="inputbox-btn confirm-btn" :disabled="!inputBoxValue.trim()" @click="handleInputBoxConfirm"> <button
class="flex-1 rounded-sm border-none bg-accent p-2.5 text-sm font-semibold text-main transition-colors duration-fast ease-apple hover:bg-accent-hover disabled:cursor-not-allowed disabled:opacity-60"
:disabled="!inputBoxValue.trim()"
@click="handleInputBoxConfirm"
>
{{ t('common.confirm') }} {{ t('common.confirm') }}
</button> </button>
</div> </div>
@@ -60,7 +84,9 @@ import { useI18n } from 'vue-i18n'
import $bus from '@/utils/bus' import $bus from '@/utils/bus'
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant' import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
import { getConfig } from '@/utils/dataSender'
const enableAdvancedAnimation = ref(false)
const { t } = useI18n() const { t } = useI18n()
const inputBoxValue = ref('') const inputBoxValue = ref('')
const showInputBoxVisible = ref(false) const showInputBoxVisible = ref(false)
@@ -108,7 +134,13 @@ function handleInputBoxConfirm() {
$bus.emit(SHOW_INPUT_BOX_RESPONSE, inputBoxValue.value) $bus.emit(SHOW_INPUT_BOX_RESPONSE, inputBoxValue.value)
} }
async function initConf() {
const settingConfig = await getConfig<any>('settings')
enableAdvancedAnimation.value = settingConfig?.enableAdvancedAnimation || false
}
onBeforeMount(() => { onBeforeMount(() => {
initConf()
removeInputBoxListenerCallback = window.electron.ipcRendererOn(SHOW_INPUT_BOX, handleIpcInputBoxEvent) removeInputBoxListenerCallback = window.electron.ipcRendererOn(SHOW_INPUT_BOX, handleIpcInputBoxEvent)
$bus.on(SHOW_INPUT_BOX, initInputBoxValue) $bus.on(SHOW_INPUT_BOX, initInputBoxValue)
}) })
@@ -124,5 +156,3 @@ export default {
name: 'InputBoxDialog', name: 'InputBoxDialog',
} }
</script> </script>
<style scoped src="./css/InputBox.css"></style>

View File

@@ -1,47 +1,61 @@
<template> <template>
<div class="per-picbed-setting"> <div class="mt-2">
<div class="map-settings-toggle"> <div class="flex justify-end">
<button type="button" class="btn btn-secondary btn-small" @click="showSettings = !showSettings"> <button
type="button"
class="inline-flex cursor-pointer items-center gap-2 rounded-sm border border-border bg-bg px-1 py-2 text-sm leading-none font-medium text-secondary no-underline transition-colors duration-200 ease-apple hover:border-accent hover:bg-surface-elevated hover:text-main"
@click="showSettings = !showSettings"
>
<Settings :size="14" /> <Settings :size="14" />
{{ t('pages.imageProcess.perPicBed.title') }} {{ t('pages.imageProcess.perPicBed.title') }}
</button> </button>
</div> </div>
<div v-if="showSettings" class="map-settings-panel"> <div v-if="showSettings" class="mt-4 rounded-md border border-border bg-bg-secondary p-4 shadow-sm">
<h4> <h4 class="textsm mb-4 font-semibold text-main">
{{ {{
t('pages.imageProcess.perPicBed.defaultValue', { t('pages.imageProcess.perPicBed.defaultValue', {
value: globalValue !== undefined ? globalValue : defaultValue, value: globalValue !== undefined ? globalValue : defaultValue,
}) })
}} }}
</h4> </h4>
<div class="picbed-settings-grid"> <div class="grid grid-cols-1 gap-3">
<div v-for="picbed in availablePicbeds" :key="picbed.type" class="picbed-setting-item"> <div
<label class="picbed-name">{{ picbed.name }}</label> v-for="picbed in availablePicbeds"
:key="picbed.type"
class="flex flex-wrap items-center justify-between rounded-sm border border-border bg-bg p-3 transition-all duration-fast ease-apple hover:border-accent hover:bg-surface"
>
<label class="m-0 flex-1 text-sm font-medium text-main">{{ picbed.name }}</label>
<!-- Checkbox input --> <!-- Checkbox input -->
<label v-if="inputType === 'checkbox'" class="switch-label small"> <label v-if="inputType === 'checkbox'" class="m-0 flex cursor-pointer items-center p-0 select-none">
<input <input
:checked="getMapValue(mapField, picbed.type, defaultValue)" :checked="getMapValue(mapField, picbed.type, defaultValue)"
type="checkbox" type="checkbox"
class="switch-input" class="peer hidden"
@change="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).checked)" @change="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).checked)"
/> />
<span class="switch-slider" /> <span
class="relative h-[24px] w-[44px] shrink-0 rounded-lg bg-gray-300 bg-linear-to-r transition-all duration-medium ease-standard peer-checked:bg-accent peer-checked:shadow-lg before:absolute before:top-[2px] before:left-[2px] before:h-[20px] before:w-[20px] before:rounded-full before:bg-linear-to-r before:from-white before:to-gray-100 before:shadow-md before:transition-all before:duration-medium before:ease-standard peer-checked:before:translate-x-[20px]"
/>
</label> </label>
<!-- Range input --> <!-- Range input -->
<div v-else-if="inputType === 'range'" class="range-input-container"> <div v-else-if="inputType === 'range'" class="flex flex-wrap items-center gap-2 rounded-sm shadow-sm">
<input <input
:value="getMapValue(mapField, picbed.type, defaultValue)" :value="getMapValue(mapField, picbed.type, defaultValue)"
type="range" type="range"
:min="rangeMin" :min="rangeMin"
:max="rangeMax" :max="rangeMax"
:step="rangeStep" :step="rangeStep"
class="form-range small" class="my-3 h-[8px] w-[100px] appearance-none rounded-sm bg-linear-to-l from-accent transition-colors duration-150 ease-apple outline-none [&::--moz-range-thumb]:shadow-sm [&::-moz-range-thumb]:h-[22px] [&::-moz-range-thumb]:w-[22px] [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-border [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:h-[22px] [&::-webkit-slider-thumb]:w-[22px] [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-border [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:transition-all [&::-webkit-slider-thumb]:duration-200 [&::-webkit-slider-thumb]:ease-apple [&::-webkit-slider-thumb:hover]:scale-105 [&::-webkit-slider-thumb:hover]:shadow-md"
@input="e => handleMapChange(picbed.type, parseFloat((e.target as HTMLInputElement).value))" @input="e => handleMapChange(picbed.type, parseFloat((e.target as HTMLInputElement).value))"
/> />
<div class="range-value">{{ getMapValue(mapField, picbed.type, defaultValue) }}{{ rangeSuffix }}</div> <div
class="inline-flex min-w-14 items-center justify-center rounded-md bg-accent px-1 py-1 text-sm font-semibold text-white shadow-sm"
>
{{ getMapValue(mapField, picbed.type, defaultValue) }}{{ rangeSuffix }}
</div>
</div> </div>
<!-- Number input --> <!-- Number input -->
@@ -51,7 +65,7 @@
type="number" type="number"
:min="numberMin" :min="numberMin"
:max="numberMax" :max="numberMax"
class="form-input small" class="w-[100px] rounded-sm border border-border bg-bg p-1 text-sm text-main outline-none placeholder:text-xs focus:border-accent focus:bg-surface"
@input="e => handleMapChange(picbed.type, parseFloat((e.target as HTMLInputElement).value))" @input="e => handleMapChange(picbed.type, parseFloat((e.target as HTMLInputElement).value))"
/> />
@@ -60,23 +74,23 @@
v-else-if="inputType === 'text'" v-else-if="inputType === 'text'"
:value="getMapValue(mapField, picbed.type, defaultValue)" :value="getMapValue(mapField, picbed.type, defaultValue)"
type="text" type="text"
class="form-input small" class="w-[100px] rounded-sm border border-border bg-bg p-1 text-sm text-main outline-none placeholder:text-xs focus:border-accent focus:bg-surface"
:placeholder="textPlaceholder" :placeholder="textPlaceholder"
@input="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).value)" @input="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).value)"
/> />
<!-- Color input --> <!-- Color input -->
<div v-else-if="inputType === 'color'" class="color-input-group small"> <div v-else-if="inputType === 'color'" class="flex items-center gap-2">
<input <input
:value="getMapValue(mapField, picbed.type, defaultValue)" :value="getMapValue(mapField, picbed.type, defaultValue)"
type="color" type="color"
class="form-color small" class="rounded-sm border border-border bg-bg p-1 text-sm text-main outline-none focus:border-accent focus:bg-surface"
@input="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).value)" @input="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).value)"
/> />
<input <input
:value="getMapValue(mapField, picbed.type, defaultValue)" :value="getMapValue(mapField, picbed.type, defaultValue)"
type="text" type="text"
class="form-input small" class="w-[100px] rounded-sm border border-border bg-bg p-1 text-sm text-main outline-none placeholder:text-xs focus:border-accent focus:bg-surface"
@input="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).value)" @input="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).value)"
/> />
</div> </div>
@@ -85,7 +99,7 @@
<select <select
v-else-if="inputType === 'select'" v-else-if="inputType === 'select'"
:value="getMapValue(mapField, picbed.type, defaultValue)" :value="getMapValue(mapField, picbed.type, defaultValue)"
class="form-input small" class="w-[100px] rounded-sm border border-border bg-bg text-sm text-main outline-none placeholder:text-xs focus:border-accent focus:bg-surface"
@change="e => handleMapChange(picbed.type, (e.target as HTMLSelectElement).value)" @change="e => handleMapChange(picbed.type, (e.target as HTMLSelectElement).value)"
> >
<option v-for="option in selectOptions" :key="option.value" :value="option.value"> <option v-for="option in selectOptions" :key="option.value" :value="option.value">
@@ -94,17 +108,24 @@
</select> </select>
<!-- Radio input --> <!-- Radio input -->
<div v-else-if="inputType === 'radio'" class="radio-group small"> <div v-else-if="inputType === 'radio'" class="flex flex-wrap gap-2">
<label v-for="option in radioOptions" :key="option.value" class="radio-option small"> <label
v-for="option in radioOptions"
:key="option.value"
class="flex cursor-pointer items-center gap-2.5 rounded-lg border border-border bg-bg px-4 py-3.5 transition-all duration-200 ease-apple hover:border-accent-hover hover:bg-accent/8"
>
<input <input
:id="`radio-${picbed.type}-${option.value}`"
:checked="getMapValue(mapField, picbed.type, defaultValue) === option.value" :checked="getMapValue(mapField, picbed.type, defaultValue) === option.value"
:value="option.value" :value="option.value"
type="radio" type="radio"
class="radio-input" class="peer hidden"
@change="handleMapChange(picbed.type, option.value)" @change="handleMapChange(picbed.type, option.value, `radio-${picbed.type}-${option.value}`)"
/> />
<span class="radio-indicator" /> <span
<span class="radio-label">{{ option.label }}</span> class="relative h-[18px] w-[18px] rounded-full border border-border bg-bg transition-all duration-200 ease-apple peer-checked:border-accent peer-checked:after:absolute peer-checked:after:top-1/2 peer-checked:after:left-1/2 peer-checked:after:h-[10px] peer-checked:after:w-[10px] peer-checked:after:-translate-x-1/2 peer-checked:after:-translate-y-1/2 peer-checked:after:transform peer-checked:after:rounded-full peer-checked:after:bg-accent peer-checked:after:content-['']"
/>
<span class="text-sm font-medium text-main">{{ option.label }}</span>
</label> </label>
</div> </div>
</div> </div>
@@ -188,9 +209,20 @@ function getMapValue(mapObj: Record<string, any> | undefined, picbedType: string
return typeof value === 'object' ? JSON.stringify(getRawData(value)) : value return typeof value === 'object' ? JSON.stringify(getRawData(value)) : value
} }
function handleMapChange(picbedType: string, value: any) { function handleMapChange(picbedType: string, value: any, id?: string) {
if (id) {
const element = document.getElementById(id) as HTMLInputElement | null
if (element) {
element.checked = true
}
for (const sibling of element?.parentElement?.parentElement?.children || []) {
const input = sibling.querySelector('input') as HTMLInputElement | null
if (input && input !== element) {
console.log('uncheck', input)
input.checked = false
}
}
}
emit('mapChange', picbedType, value) emit('mapChange', picbedType, value)
} }
</script> </script>
<style scoped src="./css/PerPicbedSetting.css"></style>

View File

@@ -1,231 +0,0 @@
/* Transitions */
.inputbox-fade-enter-active,
.inputbox-fade-leave-active {
transition: opacity 0.2s ease;
}
.inputbox-fade-enter-from,
.inputbox-fade-leave-to {
opacity: 0;
}
.inputbox-scale-enter-active {
transition: all var(--transition-bounce-md);
}
.inputbox-scale-leave-active {
transition: all 0.2s ease;
}
.inputbox-scale-enter-from {
opacity: 0;
transform: scale(0.9) translateY(-10px);
}
.inputbox-scale-leave-to {
opacity: 0;
transform: scale(0.95);
}
/* Overlay */
.inputbox-overlay {
position: fixed;
inset: 0;
z-index: 2000;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
background: rgb(0 0 0 / 40%);
}
/* Container */
.inputbox-container {
position: relative;
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: 1rem;
width: 100%;
max-width: 28rem;
background: var(--color-background-tertiary);
}
/* Close Button */
.inputbox-close {
position: absolute;
top: 1rem;
right: 1rem;
z-index: 10;
display: flex;
justify-content: center;
align-items: center;
border-radius: 0.5rem;
color: var(--color-text-secondary);
border: 1px solid var(--color-border);
background: var(--color-background-tertiary);
transition: all 0.15s ease;
cursor: pointer;
}
.inputbox-close:hover {
color: var(--color-danger);
background: var(--color-surface-elevated);
}
/* Body */
.inputbox-body {
padding: 1rem 1rem 0.5rem;
}
.inputbox-title {
margin: 0 0 1.25rem;
padding-right: 2rem;
font-size: 1.125rem;
font-weight: 600;
line-height: 1.4;
color: var(--color-text-primary);
}
.inputbox-content {
position: relative;
}
/* Input */
.inputbox-input {
border: 1.5px solid var(--color-border);
border-radius: 0.625rem;
padding: 0.75rem 1rem;
width: 100%;
font-size: 0.9375rem;
font-family: inherit;
color: var(--color-text-primary);
background: var(--color-background-tertiary);
outline: none;
transition: all 0.2s ease;
}
.inputbox-input:hover {
border-color: var(--color-accent);
background: var(--color-background-tertiary);
}
.inputbox-input:focus {
border-color: var(--color-accent);
background: var(--color-background-tertiary);
box-shadow: 0 0 0 3px rgb(59 130 246 / 10%);
}
.inputbox-input::placeholder {
color: var(--color-text-primary);
}
/* Textarea */
.inputbox-textarea {
border: 1.5px solid var(--color-border);
border-radius: 0.625rem;
padding: 0.75rem 1rem;
width: 100%;
min-height: 6rem;
font-size: 0.9375rem;
font-family: inherit;
line-height: 1.6;
color: var(--color-text-primary);
background: var(--color-background-tertiary);
outline: none;
resize: vertical;
transition: all 0.2s ease;
}
.inputbox-textarea:hover {
border-color: var(--color-accent);
background: var(--color-background-tertiary);
}
.inputbox-textarea:focus {
border-color: var(--color-accent);
background: var(--color-background-tertiary);
}
.inputbox-textarea::placeholder {
color: var(--color-text-primary);
}
/* Actions */
.inputbox-actions {
display: flex;
border-top: 1px solid var(--color-border);
padding: 1rem 1.5rem;
gap: 0.75rem;
}
.inputbox-btn {
flex: 1;
border: none;
border-radius: 0.5rem;
padding: 0.625rem 1.25rem;
font-size: 0.875rem;
font-weight: 500;
transition: all 0.15s ease;
cursor: pointer;
}
.inputbox-btn:active {
transform: scale(0.98);
}
.inputbox-btn:disabled {
opacity: 0.8;
cursor: not-allowed;
}
.inputbox-btn:disabled:active {
transform: none;
}
/* Cancel Button */
.cancel-btn {
border: 1px solid var(--color-border);
color: var(--color-text-secondary);
background: var(--color-background-secondary);
}
.cancel-btn:hover {
border-color: var(--color-accent);
background: var(--color-background-secondary);
}
/* Confirm Button */
.confirm-btn {
border: none;
color: var(--color-text-primary);
background: var(--color-accent);
}
.confirm-btn:hover:not(:disabled) {
background: var(--color-accent-hover);
}
/* Responsive */
@media (width <= 640px) {
.inputbox-overlay {
align-items: flex-end;
padding: 0;
}
.inputbox-container {
border-radius: 1rem 1rem 0 0;
max-width: 100%;
}
.inputbox-body {
padding: 1.75rem 1.5rem 1.25rem;
}
.inputbox-actions {
flex-direction: column-reverse;
}
.inputbox-btn {
width: 100%;
}
}

View File

@@ -1,349 +0,0 @@
/* Component root */
.per-picbed-setting {
margin-top: 0.5rem;
}
/* Toggle section */
.map-settings-toggle {
display: flex;
justify-content: flex-end;
margin-top: 0.5rem;
}
/* Button styling */
.btn {
display: inline-flex;
align-items: center;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
padding: 0.75rem 1rem;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
color: var(--color-text-primary);
background: var(--color-background-primary);
transition: all 0.2s ease;
gap: 0.5rem;
cursor: pointer;
line-height: 1;
}
.btn:hover {
border-color: var(--color-accent);
background: var(--color-background-secondary);
}
.btn-secondary {
border-color: var(--color-border);
color: var(--color-text-secondary);
background: var(--color-background-primary);
}
.btn-secondary:hover {
border-color: var(--color-border-secondary);
color: var(--color-text-primary);
background: var(--color-background-secondary);
}
.btn-small {
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
gap: 0.375rem;
}
/* Settings panel */
.map-settings-panel {
margin-top: 1rem;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 1rem;
background: var(--color-background-secondary);
box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
}
.map-settings-panel h4 {
margin: 0 0 1rem;
font-size: 1rem;
font-weight: 600;
color: var(--color-text-primary);
}
/* Grid layout */
.picbed-settings-grid {
display: grid;
grid-template-columns: 1fr;
gap: 0.75rem;
}
.picbed-setting-item {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
padding: 0.75rem;
background: var(--color-background-primary);
transition: all 0.2s ease;
}
.picbed-setting-item:hover {
border-color: var(--color-accent);
box-shadow: var(--shadow-sm);
}
.picbed-name {
margin: 0;
min-width: 0;
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary);
flex: 1;
}
/* Switch component */
.switch-label {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
}
.switch-label.small {
margin: 0;
padding: 0;
}
.switch-input {
display: none;
}
.switch-slider {
position: relative;
border-radius: var(--radius-lg);
width: 44px;
height: 24px;
background: linear-gradient(180deg, #d0d3d9 0%, #c0c4cc 100%);
transition: all 0.3s ease;
flex-shrink: 0;
}
.switch-slider::before {
position: absolute;
top: 2px;
left: 2px;
border-radius: var(--radius-round);
width: 20px;
height: 20px;
background: linear-gradient(180deg, #ffffff 0%, #f5f5f5 100%);
box-shadow:
0 2px 6px rgb(0 0 0 / 20%),
0 1px 2px rgb(0 0 0 / 10%);
transition: all var(--transition-medium);
content: '';
}
.switch-input:checked + .switch-slider {
background: var(--color-accent);
box-shadow:
inset 0 1px 3px rgb(0 0 0 / 10%),
0 2px 8px rgb(64 158 255 / 30%);
}
.switch-input:checked + .switch-slider::before {
transform: translateX(20px);
}
/* Form inputs */
.form-input,
.form-range,
.form-color {
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
color: var(--color-text-primary);
background: var(--color-background-primary);
outline: none;
transition: all 0.2s ease;
}
.form-input:focus,
.form-color:focus {
border-color: var(--color-accent);
box-shadow: var(--shadow-sm);
}
.form-input.small {
padding: 0.375rem 0.5rem;
width: 100px;
font-size: 0.875rem;
}
.form-range.small {
border-radius: 3px;
width: 100px;
height: 6px;
background: var(--color-border);
appearance: none;
}
.form-range.small::-webkit-slider-thumb {
border: 2px solid #ffffff;
border-radius: var(--radius-round);
width: 18px;
height: 18px;
background: var(--color-accent);
box-shadow: 0 2px 4px rgb(0 0 0 / 20%);
appearance: none;
cursor: pointer;
}
.form-range.small::-moz-range-thumb {
border: 2px solid #ffffff;
border-radius: var(--radius-round);
width: 18px;
height: 18px;
background: var(--color-accent);
box-shadow: 0 2px 4px rgb(0 0 0 / 20%);
cursor: pointer;
}
.form-color.small {
padding: 0;
width: 40px;
height: 32px;
cursor: pointer;
}
/* Range input container */
.range-input-container {
display: flex;
align-items: center;
gap: 0.5rem;
}
.range-value {
border-radius: 4px;
padding: 0.125rem 0.25rem;
min-width: 50px;
font-size: 0.875rem;
font-weight: 500;
text-align: center;
color: var(--color-text-secondary);
color: white;
background: var(--color-accent);
}
/* Color input group */
.color-input-group.small {
display: flex;
align-items: center;
gap: 0.5rem;
}
.color-input-group.small .form-input {
width: 80px;
}
/* Radio group */
.radio-group.small {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.radio-option.small {
display: flex;
align-items: center;
border: 1px solid var(--color-border);
border-radius: 4px;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
background: var(--color-background-primary);
transition: all 0.2s ease;
gap: 0.25rem;
cursor: pointer;
}
.radio-option.small:hover {
border-color: var(--color-accent);
background: rgb(64 158 255 / 10%);
}
.radio-input {
display: none;
}
.radio-indicator {
position: relative;
border: 2px solid var(--color-border);
border-radius: var(--radius-round);
width: 14px;
height: 14px;
background: var(--color-background-primary);
transition: all 0.2s ease;
flex-shrink: 0;
}
.radio-input:checked + .radio-indicator {
border-color: var(--color-accent);
}
.radio-input:checked + .radio-indicator::after {
position: absolute;
top: 50%;
left: 50%;
border-radius: var(--radius-round);
width: 6px;
height: 6px;
background: var(--color-accent);
content: '';
transform: translate(-50%, -50%);
}
.radio-label {
font-size: inherit;
font-weight: 500;
color: var(--color-text-primary);
}
/* Select styling */
select.form-input {
cursor: pointer;
padding-right: 2rem;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1rem;
}
select.form-input:focus {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23409eff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
}
/* Responsive design */
@media (width <= 768px) {
.picbed-setting-item {
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
}
.picbed-name {
margin-bottom: 0.5rem;
}
.radio-group.small {
flex-direction: column;
width: 100%;
}
.range-input-container {
flex-direction: column;
align-items: flex-start;
gap: 0.25rem;
}
.color-input-group.small {
flex-direction: column;
align-items: flex-start;
}
}

View File

@@ -86,8 +86,8 @@
} }
.modal-close-btn { .modal-close-btn {
@apply flex items-center justify-center border border-border-secondary rounded-full w-[32px] h-[32px] text-secondary bg-bg-tertiary transition-all duration-fast ease-apple cursor-pointer; @apply flex h-8 w-8 cursor-pointer items-center justify-center rounded-full border border-border bg-surface-elevated text-secondary transition-all duration-fast ease-apple;
@apply hover:text-main hover:bg-danger; @apply hover:scale-105 hover:border-danger hover:bg-danger hover:text-white focus-visible:focus-ring;
} }
.modal-footer { .modal-footer {