mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-10 15:02:40 +08:00
feat: enhance theme customizer with shadow options and update styles
- Added shadow customization options to the theme customizer, allowing users to select from 'none', 'low', 'medium', and 'high'. - Updated the theme customizer settings interface and default values to include shadow settings. - Enhanced the CSS variables for shadows in common.scss to support different shadow levels based on user selection. - Modified the VirtualSlideView component styles to improve layout and scrolling behavior. - Updated localization files for English, Simplified Chinese, and Traditional Chinese to include new shadow-related terms. - Adjusted various components to ensure consistent application of shadow styles across the application.
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
themeCustomizerPrimaryColors,
|
||||
useThemeCustomizer,
|
||||
type ThemeCustomizerLayout,
|
||||
type ThemeCustomizerShadow,
|
||||
type ThemeCustomizerSkin,
|
||||
type ThemeCustomizerTheme,
|
||||
} from '@/composables/useThemeCustomizer'
|
||||
@@ -27,8 +28,17 @@ const emit = defineEmits<{
|
||||
|
||||
const customColorInput = ref<HTMLInputElement | null>(null)
|
||||
|
||||
const { isCustomized, resetSettings, setLayout, setPrimaryColor, setSemiDarkMenu, setSkin, setTheme, settings } =
|
||||
useThemeCustomizer()
|
||||
const {
|
||||
isCustomized,
|
||||
resetSettings,
|
||||
setLayout,
|
||||
setPrimaryColor,
|
||||
setSemiDarkMenu,
|
||||
setShadow,
|
||||
setSkin,
|
||||
setTheme,
|
||||
settings,
|
||||
} = useThemeCustomizer()
|
||||
const { appMode } = usePWA()
|
||||
const { t } = useI18n()
|
||||
const { global: globalTheme } = useTheme()
|
||||
@@ -98,6 +108,30 @@ const skinOptions = computed<Array<{ title: string; value: ThemeCustomizerSkin }
|
||||
{ title: t('theme.customizer.skinBordered'), value: 'bordered' },
|
||||
])
|
||||
|
||||
const shadowOptions = computed<
|
||||
Array<{
|
||||
title: string
|
||||
value: ThemeCustomizerShadow
|
||||
}>
|
||||
>(() => [
|
||||
{
|
||||
title: t('theme.customizer.shadowNone'),
|
||||
value: 'none',
|
||||
},
|
||||
{
|
||||
title: t('theme.customizer.shadowLow'),
|
||||
value: 'low',
|
||||
},
|
||||
{
|
||||
title: t('theme.customizer.shadowMedium'),
|
||||
value: 'medium',
|
||||
},
|
||||
{
|
||||
title: t('theme.customizer.shadowHigh'),
|
||||
value: 'high',
|
||||
},
|
||||
])
|
||||
|
||||
const layoutOptions = computed<Array<{ icon: string; title: string; value: ThemeCustomizerLayout }>>(() => [
|
||||
{ title: t('theme.customizer.layoutVertical'), value: 'vertical', icon: 'mdi-dock-left' },
|
||||
{ title: t('theme.customizer.layoutCollapsed'), value: 'collapsed', icon: 'mdi-dock-window' },
|
||||
@@ -109,6 +143,7 @@ const showLayoutSection = computed(() => !appMode.value)
|
||||
const hasAppModeCustomization = computed(() => {
|
||||
return (
|
||||
settings.value.primaryColor !== defaultPrimaryColor ||
|
||||
settings.value.shadow !== 'none' ||
|
||||
settings.value.skin !== 'default' ||
|
||||
settings.value.theme !== 'auto'
|
||||
)
|
||||
@@ -150,6 +185,7 @@ async function handleResetSettings() {
|
||||
|
||||
// App 模式共享定制器,但保留桌面导航相关偏好,只重置 App 侧可调整的外观设置。
|
||||
await setPrimaryColor(defaultPrimaryColor)
|
||||
await setShadow('none')
|
||||
await setSkin('default')
|
||||
await setTheme('auto')
|
||||
}
|
||||
@@ -262,6 +298,35 @@ async function handleResetSettings() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<VDivider class="mt-7" />
|
||||
|
||||
<h3 class="theme-customizer-section-title">{{ t('theme.customizer.shadow') }}</h3>
|
||||
<div class="theme-customizer-preview-grid theme-customizer-preview-grid--shadow">
|
||||
<div
|
||||
v-for="shadow in shadowOptions"
|
||||
:key="shadow.value"
|
||||
class="theme-customizer-preview-option"
|
||||
:class="{ 'is-active': settings.shadow === shadow.value }"
|
||||
@click="setShadow(shadow.value)"
|
||||
>
|
||||
<span class="theme-customizer-shadow-scene" :class="`theme-customizer-shadow-scene--${shadow.value}`">
|
||||
<span class="theme-customizer-shadow-scene__panel">
|
||||
<span class="theme-customizer-shadow-scene__panel-line" />
|
||||
<span
|
||||
class="theme-customizer-shadow-scene__panel-line theme-customizer-shadow-scene__panel-line--short"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span class="theme-customizer-shadow-scene__card">
|
||||
<span class="theme-customizer-shadow-scene__badge" />
|
||||
<span class="theme-customizer-shadow-scene__line theme-customizer-shadow-scene__line--short" />
|
||||
<span class="theme-customizer-shadow-scene__line" />
|
||||
</span>
|
||||
</span>
|
||||
<span>{{ shadow.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showSemiDarkMenuOption" class="theme-customizer-semi-dark">
|
||||
<span>{{ t('theme.customizer.semiDarkMenu') }}</span>
|
||||
<VSwitch
|
||||
@@ -573,6 +638,10 @@ html[data-theme='transparent'] .v-overlay__content:has(.theme-customizer-drawer)
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.theme-customizer-preview-grid--shadow {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.theme-customizer-preview-option {
|
||||
align-items: flex-start;
|
||||
padding: 0;
|
||||
@@ -584,7 +653,8 @@ html[data-theme='transparent'] .v-overlay__content:has(.theme-customizer-drawer)
|
||||
background: transparent;
|
||||
box-shadow: none !important;
|
||||
|
||||
.theme-customizer-mini-layout {
|
||||
.theme-customizer-mini-layout,
|
||||
.theme-customizer-shadow-scene {
|
||||
border-width: 2px;
|
||||
border-color: rgb(var(--v-theme-primary));
|
||||
background: rgba(var(--v-theme-primary), 0.04);
|
||||
@@ -632,6 +702,11 @@ html[data-theme='transparent'] .v-overlay__content:has(.theme-customizer-drawer)
|
||||
.theme-customizer-mini-layout--horizontal {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 24% 1fr;
|
||||
|
||||
.mini-sidebar {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.mini-sidebar,
|
||||
@@ -667,10 +742,111 @@ html[data-theme='transparent'] .v-overlay__content:has(.theme-customizer-drawer)
|
||||
}
|
||||
}
|
||||
|
||||
.theme-customizer-mini-layout--horizontal {
|
||||
.mini-sidebar {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.theme-customizer-shadow-scene {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(var(--v-theme-on-surface), 0.12);
|
||||
border-radius: 10px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(var(--v-theme-on-surface), 0.02), rgba(var(--v-theme-on-surface), 0.06)),
|
||||
rgb(var(--v-theme-surface));
|
||||
block-size: 110px;
|
||||
inline-size: 100%;
|
||||
min-inline-size: 0;
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__panel,
|
||||
.theme-customizer-shadow-scene__card {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||
background: rgb(var(--v-theme-surface));
|
||||
box-shadow: none;
|
||||
transition: box-shadow 0.18s ease;
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__panel {
|
||||
padding: 12px;
|
||||
gap: 8px;
|
||||
inset-block-start: 16px;
|
||||
inset-inline: 14px;
|
||||
min-block-size: 54px;
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__card {
|
||||
gap: 8px;
|
||||
inset-block-end: 12px;
|
||||
inset-inline: 20px 16px;
|
||||
min-block-size: 46px;
|
||||
padding-block: 10px;
|
||||
padding-inline: 12px;
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__panel-line,
|
||||
.theme-customizer-shadow-scene__line,
|
||||
.theme-customizer-shadow-scene__badge {
|
||||
display: block;
|
||||
border-radius: 999px;
|
||||
background: rgba(var(--v-theme-on-surface), 0.1);
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__badge {
|
||||
block-size: 6px;
|
||||
inline-size: 34%;
|
||||
min-inline-size: 28px;
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__panel-line,
|
||||
.theme-customizer-shadow-scene__line {
|
||||
block-size: 7px;
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__panel-line--short,
|
||||
.theme-customizer-shadow-scene__line--short {
|
||||
inline-size: 62%;
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene--low {
|
||||
.theme-customizer-shadow-scene__panel {
|
||||
box-shadow:
|
||||
0 8px 18px rgba(var(--v-theme-on-surface), 0.08),
|
||||
0 2px 6px rgba(var(--v-theme-on-surface), 0.05);
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__card {
|
||||
box-shadow:
|
||||
0 10px 22px rgba(var(--v-theme-on-surface), 0.1),
|
||||
0 4px 10px rgba(var(--v-theme-on-surface), 0.06);
|
||||
}
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene--medium {
|
||||
.theme-customizer-shadow-scene__panel {
|
||||
box-shadow:
|
||||
0 12px 28px rgba(var(--v-theme-on-surface), 0.12),
|
||||
0 4px 12px rgba(var(--v-theme-on-surface), 0.08);
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__card {
|
||||
box-shadow:
|
||||
0 16px 34px rgba(var(--v-theme-on-surface), 0.14),
|
||||
0 6px 16px rgba(var(--v-theme-on-surface), 0.09);
|
||||
}
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene--high {
|
||||
.theme-customizer-shadow-scene__panel {
|
||||
box-shadow:
|
||||
0 16px 38px rgba(var(--v-theme-on-surface), 0.16),
|
||||
0 6px 18px rgba(var(--v-theme-on-surface), 0.1);
|
||||
}
|
||||
|
||||
.theme-customizer-shadow-scene__card {
|
||||
box-shadow:
|
||||
0 22px 48px rgba(var(--v-theme-on-surface), 0.18),
|
||||
0 8px 22px rgba(var(--v-theme-on-surface), 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user