mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
feat: update Ace editor themes to follow system color scheme and standardize dialog UI layouts
This commit is contained in:
@@ -14,6 +14,10 @@ import modeIniUrl from 'ace-builds/src-noconflict/mode-ini?url'
|
|||||||
|
|
||||||
import themeGithubUrl from 'ace-builds/src-noconflict/theme-github?url'
|
import themeGithubUrl from 'ace-builds/src-noconflict/theme-github?url'
|
||||||
|
|
||||||
|
import themeGithubDarkUrl from 'ace-builds/src-noconflict/theme-github_dark?url'
|
||||||
|
|
||||||
|
import themeGithubLightDefaultUrl from 'ace-builds/src-noconflict/theme-github_light_default?url'
|
||||||
|
|
||||||
import themeChromeUrl from 'ace-builds/src-noconflict/theme-chrome?url'
|
import themeChromeUrl from 'ace-builds/src-noconflict/theme-chrome?url'
|
||||||
|
|
||||||
import themeMonokaiUrl from 'ace-builds/src-noconflict/theme-monokai?url'
|
import themeMonokaiUrl from 'ace-builds/src-noconflict/theme-monokai?url'
|
||||||
@@ -533,6 +537,8 @@ ace.config.setModuleUrl('ace/mode/yaml', modeYamlUrl)
|
|||||||
ace.config.setModuleUrl('ace/mode/css', modeCssUrl)
|
ace.config.setModuleUrl('ace/mode/css', modeCssUrl)
|
||||||
ace.config.setModuleUrl('ace/mode/ini', modeIniUrl)
|
ace.config.setModuleUrl('ace/mode/ini', modeIniUrl)
|
||||||
ace.config.setModuleUrl('ace/theme/github', themeGithubUrl)
|
ace.config.setModuleUrl('ace/theme/github', themeGithubUrl)
|
||||||
|
ace.config.setModuleUrl('ace/theme/github_dark', themeGithubDarkUrl)
|
||||||
|
ace.config.setModuleUrl('ace/theme/github_light_default', themeGithubLightDefaultUrl)
|
||||||
ace.config.setModuleUrl('ace/theme/chrome', themeChromeUrl)
|
ace.config.setModuleUrl('ace/theme/chrome', themeChromeUrl)
|
||||||
ace.config.setModuleUrl('ace/theme/monokai', themeMonokaiUrl)
|
ace.config.setModuleUrl('ace/theme/monokai', themeMonokaiUrl)
|
||||||
ace.config.setModuleUrl('ace/mode/base', workerBaseUrl)
|
ace.config.setModuleUrl('ace/mode/base', workerBaseUrl)
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ const visible = computed({
|
|||||||
|
|
||||||
// 正在编辑的 CSS 内容
|
// 正在编辑的 CSS 内容
|
||||||
const editableCSS = ref(props.css)
|
const editableCSS = ref(props.css)
|
||||||
|
const editorOptions = {
|
||||||
|
displayIndentGuides: true,
|
||||||
|
fontSize: 14,
|
||||||
|
highlightActiveLine: true,
|
||||||
|
scrollPastEnd: 0.2,
|
||||||
|
showPrintMargin: false,
|
||||||
|
tabSize: 2,
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.css,
|
() => props.css,
|
||||||
@@ -56,25 +64,66 @@ function submitCustomCSS() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog v-if="visible" v-model="visible" max-width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
|
<VDialog v-if="visible" v-model="visible" max-width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard>
|
<VCard class="custom-css-dialog">
|
||||||
<VCardItem>
|
<VCardItem class="custom-css-header py-3">
|
||||||
|
<template #prepend>
|
||||||
|
<VAvatar color="primary" variant="tonal" rounded size="40" class="me-2">
|
||||||
|
<VIcon icon="mdi-palette" size="22" />
|
||||||
|
</VAvatar>
|
||||||
|
</template>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
<VIcon icon="mdi-palette" class="me-2" />
|
|
||||||
{{ t('theme.custom') }}
|
{{ t('theme.custom') }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<VDialogCloseBtn v-model="visible" />
|
<VDialogCloseBtn v-model="visible" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VDivider />
|
<div>
|
||||||
<VAceEditor v-model:value="editableCSS" lang="css" :theme="props.editorTheme" class="w-full min-h-[30rem]" />
|
<VAceEditor
|
||||||
<VDivider />
|
v-model:value="editableCSS"
|
||||||
<VCardText class="text-center">
|
lang="css"
|
||||||
<VBtn @click="submitCustomCSS" class="w-1/2">
|
:theme="props.editorTheme"
|
||||||
<template #prepend>
|
:options="editorOptions"
|
||||||
<VIcon icon="mdi-content-save" />
|
wrap
|
||||||
</template>
|
class="custom-css-editor"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<VCardActions class="custom-css-actions">
|
||||||
|
<VBtn color="primary" prepend-icon="mdi-content-save" class="px-5" @click="submitCustomCSS">
|
||||||
{{ t('common.save') }}
|
{{ t('common.save') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</VCardText>
|
</VCardActions>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.custom-css-dialog {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-css-header {
|
||||||
|
border-block-end: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-css-editor {
|
||||||
|
overflow: hidden;
|
||||||
|
background: rgb(var(--v-theme-surface));
|
||||||
|
block-size: min(62vh, 34rem);
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-css-actions {
|
||||||
|
border-block-start: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||||
|
padding-block: 0.875rem;
|
||||||
|
padding-inline: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.custom-css-body {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-css-editor {
|
||||||
|
block-size: calc(100dvh - 10rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ const visible = computed({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const editableContent = ref(props.content)
|
const editableContent = ref(props.content)
|
||||||
|
const editorOptions = {
|
||||||
|
displayIndentGuides: true,
|
||||||
|
fontSize: 14,
|
||||||
|
highlightActiveLine: true,
|
||||||
|
scrollPastEnd: 0.2,
|
||||||
|
showPrintMargin: false,
|
||||||
|
tabSize: 2,
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.content,
|
() => props.content,
|
||||||
@@ -58,10 +66,12 @@ function submitTemplate() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VDialog v-if="visible" v-model="visible" max-width="50rem" :fullscreen="!display.mdAndUp.value">
|
<VDialog v-if="visible" v-model="visible" max-width="50rem" :fullscreen="!display.mdAndUp.value">
|
||||||
<VCard>
|
<VCard class="notification-template-editor-dialog">
|
||||||
<VCardItem class="py-2">
|
<VCardItem class="template-editor-header py-3">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<VIcon icon="mdi-code-json" class="me-2" />
|
<VAvatar color="primary" variant="tonal" rounded size="40" class="me-2">
|
||||||
|
<VIcon icon="mdi-code-json" size="22" />
|
||||||
|
</VAvatar>
|
||||||
</template>
|
</template>
|
||||||
<VCardTitle>
|
<VCardTitle>
|
||||||
{{ t('setting.notification.templateConfigTitle') }}
|
{{ t('setting.notification.templateConfigTitle') }}
|
||||||
@@ -71,16 +81,18 @@ function submitTemplate() {
|
|||||||
</VCardSubtitle>
|
</VCardSubtitle>
|
||||||
<VDialogCloseBtn v-model="visible" />
|
<VDialogCloseBtn v-model="visible" />
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VCardText class="py-0">
|
<div>
|
||||||
<VAceEditor
|
<VAceEditor
|
||||||
:key="`${props.templateType}-jinja2-json`"
|
:key="`${props.templateType}-jinja2-json`"
|
||||||
v-model:value="editableContent"
|
v-model:value="editableContent"
|
||||||
lang="jinja2_json"
|
lang="jinja2_json"
|
||||||
:theme="props.editorTheme"
|
:theme="props.editorTheme"
|
||||||
class="w-full h-full min-h-[30rem] rounded"
|
:options="editorOptions"
|
||||||
|
wrap
|
||||||
|
class="template-ace-editor"
|
||||||
/>
|
/>
|
||||||
</VCardText>
|
</div>
|
||||||
<VCardActions class="pt-3">
|
<VCardActions class="template-editor-actions">
|
||||||
<VBtn color="primary" prepend-icon="mdi-content-save" class="px-5" @click="submitTemplate">
|
<VBtn color="primary" prepend-icon="mdi-content-save" class="px-5" @click="submitTemplate">
|
||||||
{{ t('common.save') }}
|
{{ t('common.save') }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
@@ -88,3 +100,36 @@ function submitTemplate() {
|
|||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.notification-template-editor-dialog {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-editor-header {
|
||||||
|
border-block-end: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-ace-editor {
|
||||||
|
overflow: hidden;
|
||||||
|
background: rgb(var(--v-theme-surface));
|
||||||
|
block-size: min(62vh, 34rem);
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-editor-actions {
|
||||||
|
border-block-start: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||||
|
padding-block: 0.875rem;
|
||||||
|
padding-inline: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.template-editor-body {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-ace-editor {
|
||||||
|
block-size: calc(100dvh - 11rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -293,8 +293,8 @@ const themes: ThemeSwitcherTheme[] = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
// 编辑器主题
|
// Ace 跟随 Vuetify 当前生效主题,避免 auto 模式或弹窗打开后切主题时颜色不同步。
|
||||||
const editorTheme = computed(() => (currentThemeName.value === 'light' ? 'github' : 'monokai'))
|
const editorTheme = computed(() => (globalTheme.current.value.dark ? 'github_dark' : 'github_light_default'))
|
||||||
|
|
||||||
// 更新主题
|
// 更新主题
|
||||||
async function updateTheme() {
|
async function updateTheme() {
|
||||||
@@ -369,6 +369,11 @@ function showCustomCssDialog() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 共享弹窗打开后也要同步主题变化,否则 Ace 会停留在打开时的配色。
|
||||||
|
watch(editorTheme, theme => {
|
||||||
|
customCssDialogController?.updateProps({ editorTheme: theme })
|
||||||
|
})
|
||||||
|
|
||||||
/** 打开透明主题设置共享弹窗。 */
|
/** 打开透明主题设置共享弹窗。 */
|
||||||
function showTransparencySettingsDialog() {
|
function showTransparencySettingsDialog() {
|
||||||
openSharedDialog(TransparencySettingsDialog, {}, {}, { closeOn: ['close', 'update:modelValue'] })
|
openSharedDialog(TransparencySettingsDialog, {}, {}, { closeOn: ['close', 'update:modelValue'] })
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ const SystemSettings = ref<any>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 编辑器主题
|
// 编辑器主题
|
||||||
const editorTheme = computed(() => (globalTheme.name.value === 'light' ? 'github' : 'monokai'))
|
// Ace 跟随 Vuetify 当前生效主题,auto 模式下也按实际明暗色渲染。
|
||||||
|
const editorTheme = computed(() => (globalTheme.current.value.dark ? 'github_dark' : 'github_light_default'))
|
||||||
|
|
||||||
const renameEditorOptions = {
|
const renameEditorOptions = {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|||||||
@@ -25,39 +25,51 @@ const NotificationTemplateEditorDialog = defineAsyncComponent(
|
|||||||
() => import('@/components/dialog/NotificationTemplateEditorDialog.vue'),
|
() => import('@/components/dialog/NotificationTemplateEditorDialog.vue'),
|
||||||
)
|
)
|
||||||
|
|
||||||
// 初始化模板配置字典
|
// 通知模板入口的图标和强调色统一维护,避免模板中散落长判断。
|
||||||
const templateConfigs = ref<Record<string, string>>({
|
const templateTypeDefaults = [
|
||||||
organizeSuccess: '{}',
|
|
||||||
downloadAdded: '{}',
|
|
||||||
subscribeAdded: '{}',
|
|
||||||
subscribeComplete: '{}',
|
|
||||||
})
|
|
||||||
|
|
||||||
// 模板类型配置
|
|
||||||
const templateTypes = ref([
|
|
||||||
{
|
{
|
||||||
type: 'organizeSuccess',
|
type: 'organizeSuccess',
|
||||||
label: t('setting.notification.organizeSuccess'),
|
icon: 'mdi-folder-check',
|
||||||
|
color: 'primary',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'downloadAdded',
|
type: 'downloadAdded',
|
||||||
label: t('setting.notification.downloadAdded'),
|
icon: 'mdi-download-box',
|
||||||
|
color: 'info',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'subscribeAdded',
|
type: 'subscribeAdded',
|
||||||
label: t('setting.notification.subscribeAdded'),
|
icon: 'mdi-rss-box',
|
||||||
|
color: 'warning',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'subscribeComplete',
|
type: 'subscribeComplete',
|
||||||
label: t('setting.notification.subscribeComplete'),
|
icon: 'mdi-check-circle',
|
||||||
|
color: 'success',
|
||||||
},
|
},
|
||||||
])
|
] as const
|
||||||
|
|
||||||
// 编辑器主题
|
type NotificationTemplateType = (typeof templateTypeDefaults)[number]['type']
|
||||||
const { name: themeName, global: globalTheme } = useTheme()
|
|
||||||
const savedTheme = ref(localStorage.getItem('theme') ?? 'auto')
|
// 初始化模板配置字典
|
||||||
const currentThemeName = ref(savedTheme.value)
|
const templateConfigs = ref<Record<string, string>>(
|
||||||
const editorTheme = computed(() => (currentThemeName.value === 'light' ? 'github' : 'monokai'))
|
templateTypeDefaults.reduce<Record<string, string>>((configs, item) => {
|
||||||
|
configs[item.type] = '{}'
|
||||||
|
return configs
|
||||||
|
}, {}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 模板类型配置
|
||||||
|
const templateTypes = computed(() =>
|
||||||
|
templateTypeDefaults.map(item => ({
|
||||||
|
...item,
|
||||||
|
label: t(`setting.notification.${item.type}`),
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ace 直接跟随 Vuetify 当前生效主题,auto 模式下也能按实际明暗色切换。
|
||||||
|
const { global: globalTheme } = useTheme()
|
||||||
|
const editorTheme = computed(() => (globalTheme.current.value.dark ? 'github_dark' : 'github_light_default'))
|
||||||
|
|
||||||
// 所有消息渠道
|
// 所有消息渠道
|
||||||
const notifications = ref<NotificationConf[]>([])
|
const notifications = ref<NotificationConf[]>([])
|
||||||
@@ -66,7 +78,7 @@ const notifications = ref<NotificationConf[]>([])
|
|||||||
const $toast = useToast()
|
const $toast = useToast()
|
||||||
|
|
||||||
const editorDialogOpen = ref(false)
|
const editorDialogOpen = ref(false)
|
||||||
const currentTemplate = ref('')
|
const currentTemplate = ref<NotificationTemplateType | ''>('')
|
||||||
const editorContent = ref('')
|
const editorContent = ref('')
|
||||||
|
|
||||||
// 消息类型开关
|
// 消息类型开关
|
||||||
@@ -127,7 +139,7 @@ function closeTemplateEditorDialog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 打开通知模板共享弹窗,保持内容通过事件回写到设置页。
|
// 打开通知模板共享弹窗,保持内容通过事件回写到设置页。
|
||||||
function openTemplateEditorDialog(type: string) {
|
function openTemplateEditorDialog(type: NotificationTemplateType) {
|
||||||
closeTemplateEditorDialog()
|
closeTemplateEditorDialog()
|
||||||
editorDialogOpen.value = true
|
editorDialogOpen.value = true
|
||||||
editorDialogController = openSharedDialog(
|
editorDialogController = openSharedDialog(
|
||||||
@@ -158,6 +170,13 @@ function openTemplateEditorDialog(type: string) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 共享弹窗的 props 是打开时写入的,主题切换时主动推送给已打开的编辑器。
|
||||||
|
watch(editorTheme, theme => {
|
||||||
|
if (!editorDialogOpen.value) return
|
||||||
|
|
||||||
|
editorDialogController?.updateProps({ editorTheme: theme })
|
||||||
|
})
|
||||||
|
|
||||||
// 添加通知渠道
|
// 添加通知渠道
|
||||||
function addNotification(notification: string) {
|
function addNotification(notification: string) {
|
||||||
let name = `${t('setting.notification.channel')}${notifications.value.length + 1}`
|
let name = `${t('setting.notification.channel')}${notifications.value.length + 1}`
|
||||||
@@ -229,7 +248,7 @@ async function loadNotificationSetting() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openEditor(type: string) {
|
async function openEditor(type: NotificationTemplateType) {
|
||||||
try {
|
try {
|
||||||
currentTemplate.value = type
|
currentTemplate.value = type
|
||||||
const result: { [key: string]: any } = await api.get('system/setting/NotificationTemplates')
|
const result: { [key: string]: any } = await api.get('system/setting/NotificationTemplates')
|
||||||
@@ -460,34 +479,25 @@ useSilentSettingRefresh(loadPageData, {
|
|||||||
<VCardSubtitle>{{ t('setting.notification.templateConfigDesc') }}</VCardSubtitle>
|
<VCardSubtitle>{{ t('setting.notification.templateConfigDesc') }}</VCardSubtitle>
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<VRow>
|
<div class="notification-template-grid">
|
||||||
<VCol v-for="item in templateTypes" :key="item.type" cols="12" sm="6" md="3">
|
<button
|
||||||
<VCard variant="tonal" class="template-card" :class="{ 'on-hover': true }" @click="openEditor(item.type)">
|
v-for="item in templateTypes"
|
||||||
<VCardItem>
|
:key="item.type"
|
||||||
<template #prepend>
|
type="button"
|
||||||
<VAvatar color="primary" variant="tonal" rounded size="42" class="me-3">
|
class="notification-template-card"
|
||||||
<VIcon
|
:class="`template-accent-${item.color}`"
|
||||||
size="24"
|
@click="openEditor(item.type)"
|
||||||
:icon="
|
>
|
||||||
item.type === 'organizeSuccess'
|
<span class="template-card-icon">
|
||||||
? 'mdi-folder-check'
|
<VIcon :icon="item.icon" size="24" />
|
||||||
: item.type === 'downloadAdded'
|
</span>
|
||||||
? 'mdi-download'
|
<span class="template-card-copy">
|
||||||
: item.type === 'subscribeAdded'
|
<span class="template-card-title">{{ item.label }}</span>
|
||||||
? 'mdi-rss'
|
<span class="template-card-subtitle">Jinja2 JSON</span>
|
||||||
: 'mdi-check-circle'
|
</span>
|
||||||
"
|
<VIcon class="template-card-arrow" icon="mdi-chevron-right" size="22" />
|
||||||
/>
|
</button>
|
||||||
</VAvatar>
|
</div>
|
||||||
</template>
|
|
||||||
<VCardTitle>{{ item.label }}</VCardTitle>
|
|
||||||
<template #append>
|
|
||||||
<VIcon icon="mdi-chevron-right" />
|
|
||||||
</template>
|
|
||||||
</VCardItem>
|
|
||||||
</VCard>
|
|
||||||
</VCol>
|
|
||||||
</VRow>
|
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VCol>
|
</VCol>
|
||||||
@@ -575,20 +585,126 @@ useSilentSettingRefresh(loadPageData, {
|
|||||||
</VRow>
|
</VRow>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Monaco编辑器容器样式 */
|
/* 模板入口保持设置页的紧凑密度,同时用轻量强调色区分不同通知场景。 */
|
||||||
.monaco-editor-container {
|
.notification-template-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-template-card {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
|
align-items: center;
|
||||||
|
border: 1px solid rgba(var(--template-accent), 0.18);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-block-start: 1rem;
|
background:
|
||||||
}
|
linear-gradient(135deg, rgba(var(--template-accent), 0.12), rgba(var(--v-theme-surface), 0) 58%),
|
||||||
|
rgba(var(--v-theme-surface), 0.72);
|
||||||
.template-card {
|
color: rgb(var(--v-theme-on-surface));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
gap: 0.875rem;
|
||||||
|
inline-size: 100%;
|
||||||
|
min-block-size: 5.25rem;
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: start;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.template-card.on-hover:hover {
|
.notification-template-card::before {
|
||||||
transform: translateY(-4px);
|
position: absolute;
|
||||||
|
background: rgb(var(--template-accent));
|
||||||
|
block-size: 100%;
|
||||||
|
content: "";
|
||||||
|
inline-size: 0.25rem;
|
||||||
|
inset-block: 0;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
opacity: 0.86;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-template-card:hover {
|
||||||
|
border-color: rgba(var(--template-accent), 0.36);
|
||||||
|
box-shadow: 0 0.75rem 1.75rem rgba(var(--template-accent), 0.12);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-template-card:focus-visible {
|
||||||
|
outline: 2px solid rgba(var(--template-accent), 0.7);
|
||||||
|
outline-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-card-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(var(--template-accent), 0.16);
|
||||||
|
block-size: 2.75rem;
|
||||||
|
color: rgb(var(--template-accent));
|
||||||
|
inline-size: 2.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-card-copy {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
min-inline-size: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-card-title {
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
|
||||||
|
font-size: 0.98rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.35;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-card-subtitle {
|
||||||
|
margin-block-start: 0.25rem;
|
||||||
|
color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-card-arrow {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: rgba(var(--v-theme-on-surface), 0.42);
|
||||||
|
transition: color 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-template-card:hover .template-card-arrow {
|
||||||
|
color: rgb(var(--template-accent));
|
||||||
|
transform: translateX(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-accent-primary {
|
||||||
|
--template-accent: var(--v-theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-accent-info {
|
||||||
|
--template-accent: var(--v-theme-info);
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-accent-warning {
|
||||||
|
--template-accent: var(--v-theme-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-accent-success {
|
||||||
|
--template-accent: var(--v-theme-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 600px) {
|
||||||
|
.notification-template-grid {
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-template-card {
|
||||||
|
min-block-size: 4.75rem;
|
||||||
|
padding: 0.875rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user