feat: implement layout switching and saving
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
This commit is contained in:
parent
5191ac7df7
commit
37ebcb74aa
5 changed files with 72 additions and 48 deletions
3
app.vue
3
app.vue
|
@ -9,14 +9,13 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import ContextMenu from '~/components/UserInterface/ContextMenu.vue';
|
import ContextMenu from '~/components/UserInterface/ContextMenu.vue';
|
||||||
import type { ContextMenuInterface } from './types/interfaces';
|
import type { ContextMenuInterface } from './types/interfaces';
|
||||||
import loadPreferredTheme from '~/utils/loadPreferredTheme';
|
|
||||||
|
|
||||||
const banner = useState("banner", () => false);
|
const banner = useState("banner", () => false);
|
||||||
|
|
||||||
const contextMenu = useState<ContextMenuInterface>("contextMenu");
|
const contextMenu = useState<ContextMenuInterface>("contextMenu");
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadPreferredTheme()
|
loadPreferredThemes()
|
||||||
|
|
||||||
document.removeEventListener("contextmenu", contextMenuHandler);
|
document.removeEventListener("contextmenu", contextMenuHandler);
|
||||||
document.addEventListener("contextmenu", (e) => {
|
document.addEventListener("contextmenu", (e) => {
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
<p class="subtitle">STYLES</p>
|
<p class="subtitle">STYLES</p>
|
||||||
<div class="styles">
|
<div class="styles">
|
||||||
<div v-for="style of styles" class="theme-preview-container">
|
<div v-for="style of styles" class="theme-preview-container">
|
||||||
<div class="theme-instance" :title="style.displayName">
|
<span class="theme-instance"
|
||||||
|
:title="style.displayName"
|
||||||
|
@click="changeTheme(StyleLayout.style, style)">
|
||||||
<div class="theme-content-container">
|
<div class="theme-content-container">
|
||||||
<span class="style-background"
|
<span class="style-background"
|
||||||
:style="{background:`linear-gradient(${style.previewGradient})`}"
|
:style="{background:`linear-gradient(${style.previewGradient})`}"
|
||||||
|
@ -16,13 +18,15 @@
|
||||||
{{ style.displayName }}
|
{{ style.displayName }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="subtitle">LAYOUTS</p>
|
<p class="subtitle">LAYOUTS</p>
|
||||||
<div class="layouts">
|
<div class="layouts">
|
||||||
<div v-for="layout of layouts" class="theme-preview-container">
|
<div v-for="layout of layouts" class="theme-preview-container">
|
||||||
<div class="theme-instance" :title="layout.displayName">
|
<div class="theme-instance"
|
||||||
|
:title="layout.displayName"
|
||||||
|
@click="changeTheme(StyleLayout.layout, layout)">
|
||||||
<div class="theme-content-container">
|
<div class="theme-content-container">
|
||||||
<span class="layout-background"
|
<span class="layout-background"
|
||||||
:style="{backgroundImage:`url(${layout.previewImageUrl})`}"
|
:style="{backgroundImage:`url(${layout.previewImageUrl})`}"
|
||||||
|
@ -51,8 +55,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import RadioButtons from '~/components/UserInterface/RadioButtons.vue';
|
import RadioButtons from '~/components/UserInterface/RadioButtons.vue';
|
||||||
import type { TimeFormat } from '~/types/settings';
|
import type { TimeFormat } from '~/types/settings';
|
||||||
import loadPreferredTheme from '~/utils/loadPreferredTheme';
|
import { settingSave, settingsLoad } from '#imports';
|
||||||
import settingSave from '~/utils/settingSave';
|
|
||||||
|
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
const baseURL = runtimeConfig.app.baseURL;
|
const baseURL = runtimeConfig.app.baseURL;
|
||||||
|
@ -61,15 +64,27 @@ const layoutFolder = `${baseURL}themes/layout`
|
||||||
|
|
||||||
const timeFormatTextStrings = ["Auto", "12-Hour", "24-Hour"]
|
const timeFormatTextStrings = ["Auto", "12-Hour", "24-Hour"]
|
||||||
|
|
||||||
|
enum StyleLayout {
|
||||||
|
style,
|
||||||
|
layout
|
||||||
|
}
|
||||||
|
|
||||||
interface Theme {
|
interface Theme {
|
||||||
displayName: string
|
displayName: string
|
||||||
complementaryColor: string
|
complementaryColor: string
|
||||||
cssData: string
|
cssData: string
|
||||||
|
themeUrl: string
|
||||||
previewGradient?: string
|
previewGradient?: string
|
||||||
previewImageUrl?: string
|
previewImageUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseThemeCss(styleData: string): Promise<Theme | void> {
|
async function parseTheme(url: string): Promise<Theme | void> {
|
||||||
|
const styleData: any = await $fetch(url)
|
||||||
|
|
||||||
|
if (typeof styleData != "string") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const metadataMatch = styleData.match(/\/\*([\s\S]*?)\*\//);
|
const metadataMatch = styleData.match(/\/\*([\s\S]*?)\*\//);
|
||||||
if (!metadataMatch) {
|
if (!metadataMatch) {
|
||||||
alert(`Failed to fetch metadata for a theme, panicing`)
|
alert(`Failed to fetch metadata for a theme, panicing`)
|
||||||
|
@ -113,18 +128,18 @@ async function parseThemeCss(styleData: string): Promise<Theme | void> {
|
||||||
displayName,
|
displayName,
|
||||||
complementaryColor,
|
complementaryColor,
|
||||||
cssData,
|
cssData,
|
||||||
|
themeUrl: url,
|
||||||
previewGradient,
|
previewGradient,
|
||||||
previewImageUrl,
|
previewImageUrl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseThemeData(
|
async function parseThemeLayout(
|
||||||
folder: string,
|
folder: string,
|
||||||
incomingThemeList: Array<string>,
|
incomingThemeList: Array<string>,
|
||||||
outputThemeList: Array<Theme>) {
|
outputThemeList: Array<Theme>) {
|
||||||
for (const theme of incomingThemeList) {
|
for (const theme of incomingThemeList) {
|
||||||
const themeData = await $fetch(`${folder}/${theme}`)
|
const parsedThemeData = await parseTheme(`${folder}/${theme}`)
|
||||||
const parsedThemeData = await parseThemeCss(themeData)
|
|
||||||
|
|
||||||
if (parsedThemeData) {
|
if (parsedThemeData) {
|
||||||
outputThemeList.push(parsedThemeData)
|
outputThemeList.push(parsedThemeData)
|
||||||
|
@ -139,18 +154,19 @@ const styleList: any = await $fetch(`${styleFolder}/styles.json`)
|
||||||
const layoutList: any = await $fetch(`${layoutFolder}/layouts.json`)
|
const layoutList: any = await $fetch(`${layoutFolder}/layouts.json`)
|
||||||
|
|
||||||
if (Array.isArray(styleList)) {
|
if (Array.isArray(styleList)) {
|
||||||
await parseThemeData(styleFolder, styleList, styles)
|
await parseThemeLayout(styleFolder, styleList, styles)
|
||||||
}
|
}
|
||||||
if (Array.isArray(layoutList)) {
|
if (Array.isArray(layoutList)) {
|
||||||
await parseThemeData(layoutFolder, layoutList, layouts)
|
await parseThemeLayout(layoutFolder, layoutList, layouts)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(layouts)
|
function changeTheme(themeType: StyleLayout, theme: Theme) {
|
||||||
|
if (themeType == StyleLayout.style) {
|
||||||
|
settingSave("selectedThemeStyle", theme.themeUrl)
|
||||||
function changeTheme(id: string, url: string) {
|
} else {
|
||||||
settingSave("selectedThemeStyle", id)
|
settingSave("selectedThemeLayout", theme.themeUrl)
|
||||||
loadPreferredTheme()
|
}
|
||||||
|
loadPreferredThemes()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onTimeFormatClicked(index: number) {
|
async function onTimeFormatClicked(index: number) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export interface ClientSettings {
|
export interface ClientSettings {
|
||||||
selectedThemeId?: string, // the ID of the theme, not the URL, for example "dark"
|
|
||||||
timeFormat?: TimeFormat
|
timeFormat?: TimeFormat
|
||||||
|
selectedThemeStyle?: string // URL
|
||||||
|
selectedThemeLayout?: string // URL
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TimeFormat {
|
export interface TimeFormat {
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
let themeLinkElement: HTMLLinkElement | null;
|
|
||||||
|
|
||||||
export default function loadPreferredTheme() {
|
|
||||||
const currentTheme = settingsLoad().selectedThemeId ?? "dark"
|
|
||||||
|
|
||||||
if (themeLinkElement) {
|
|
||||||
themeLinkElement.href = getThemeUrl(currentTheme);
|
|
||||||
} else {
|
|
||||||
// create the theme link if one doesn't already exist
|
|
||||||
useHead({
|
|
||||||
link: [{
|
|
||||||
id: "main-theme",
|
|
||||||
rel: "stylesheet",
|
|
||||||
href: getThemeUrl(currentTheme)
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
|
|
||||||
themeLinkElement = document.getElementById('main-theme') as HTMLLinkElement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getThemeUrl(id: string): string {
|
|
||||||
const runtimeConfig = useRuntimeConfig()
|
|
||||||
const baseURL = runtimeConfig.app.baseURL;
|
|
||||||
|
|
||||||
// this should preferrably use version hash, but that's not implemented yet
|
|
||||||
return `${baseURL}themes/style/${id}.css?v=${runtimeConfig.public.buildTimeString}`
|
|
||||||
}
|
|
36
utils/loadPreferredThemes.ts
Normal file
36
utils/loadPreferredThemes.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
let styleLinkElement: HTMLLinkElement | null;
|
||||||
|
let layoutLinkElement: HTMLLinkElement | null;
|
||||||
|
|
||||||
|
|
||||||
|
export default function loadPreferredThemes() {
|
||||||
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
const baseURL = runtimeConfig.app.baseURL;
|
||||||
|
|
||||||
|
const currentStyle = settingsLoad().selectedThemeStyle ?? `${baseURL}themes/style/dark.css`
|
||||||
|
const currentLayout = settingsLoad().selectedThemeLayout ?? `${baseURL}themes/layout/gorb.css`
|
||||||
|
|
||||||
|
if (styleLinkElement) {
|
||||||
|
styleLinkElement.href = currentStyle;
|
||||||
|
} else {
|
||||||
|
createStyleHead("style-theme", currentStyle)
|
||||||
|
styleLinkElement = document.getElementById('style-theme') as HTMLLinkElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layoutLinkElement) {
|
||||||
|
layoutLinkElement.href = currentLayout;
|
||||||
|
} else {
|
||||||
|
createStyleHead("style-layout", currentLayout)
|
||||||
|
layoutLinkElement = document.getElementById('style-layout') as HTMLLinkElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new theme link if one doesn't already exist
|
||||||
|
function createStyleHead(id: string, themeUrl: string) {
|
||||||
|
useHead({
|
||||||
|
link: [{
|
||||||
|
id: id,
|
||||||
|
rel: "stylesheet",
|
||||||
|
href: themeUrl
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue