From 63add0376e7771ac53939995812aea9f0db8ff93 Mon Sep 17 00:00:00 2001 From: Nelson Chan <chakflying@hotmail.com> Date: Mon, 12 Feb 2024 06:22:41 +0800 Subject: [PATCH 1/2] Feat: Impl. server default appearance by injected JSON --- server/server.js | 3 + server/utils/inject-default-appearance.js | 50 ++++ src/components/HorizontalTabHeader.vue | 66 +++++ src/components/settings/Appearance.vue | 251 ++++++------------ .../settings/AppearanceSettings.vue | 164 ++++++++++++ src/mixins/theme.js | 18 ++ src/pages/Settings.vue | 9 + 7 files changed, 391 insertions(+), 170 deletions(-) create mode 100644 server/utils/inject-default-appearance.js create mode 100644 src/components/HorizontalTabHeader.vue create mode 100644 src/components/settings/AppearanceSettings.vue diff --git a/server/server.js b/server/server.js index eeffb0635..9728c3b15 100644 --- a/server/server.js +++ b/server/server.js @@ -282,6 +282,9 @@ let needSetup = false; const statusPageRouter = require("./routers/status-page-router"); app.use(statusPageRouter); + const { injectDefaultAppearance } = require("./utils/inject-default-appearance"); + app.use(injectDefaultAppearance); + // Universal Route Handler, must be at the end of all express routes. app.get("*", async (_request, response) => { if (_request.originalUrl.startsWith("/upload/")) { diff --git a/server/utils/inject-default-appearance.js b/server/utils/inject-default-appearance.js new file mode 100644 index 000000000..fcf0847ce --- /dev/null +++ b/server/utils/inject-default-appearance.js @@ -0,0 +1,50 @@ +const { Settings } = require("../settings"); +const cheerio = require("cheerio"); +const jsesc = require("jsesc"); +const { log } = require("../../src/util"); + +const injectDefaultAppearance = (req, res, next) => { + + try { + // Intercept send() calls and inject Default Appearance + // https://stackoverflow.com/a/60817116 + const oldSend = res.send; + res.send = async (data) => { + + if (typeof data === "string") { + log.debug("inject-default-appearance", req.method + " " + req.url); + const $ = cheerio.load(data); + + const defaultAppearance = await Settings.get("defaultAppearance"); + if (defaultAppearance) { + const head = $("head"); + + const escapedJSONObject = jsesc(defaultAppearance, { isScriptContext: true }); + + const script = $(` + <script id="default-appearance" data-json="{}"> + window.defaultAppearance = ${escapedJSONObject}; + </script> + `); + + head.append(script); + + data = $.root().html(); + } + } + + res.send = oldSend; // set function back to avoid 'double-send' + return res.send(data); + }; + + next(); + } catch (e) { + + next(e); + } + +}; + +module.exports = { + injectDefaultAppearance +}; diff --git a/src/components/HorizontalTabHeader.vue b/src/components/HorizontalTabHeader.vue new file mode 100644 index 000000000..3857394dc --- /dev/null +++ b/src/components/HorizontalTabHeader.vue @@ -0,0 +1,66 @@ +<template> + <div> + <ul class="nav nav-tabs"> + <li v-for="(tab, index) in tabs" :key="index" class="nav-item"> + <a class="nav-link" :class="{ active: index == selected }" href="#" @click="$emit('update:selected', index)"> + {{ tab }} + </a> + </li> + </ul> + </div> +</template> + +<script lang="js"> +export default { + props: { + tabs: { + type: Array[String], + required: true, + }, + selected: { + type: Number, + required: true, + }, + }, + emits: [ "update:selected" ], + data() { + return {}; + }, +}; +</script> + +<style lang="scss" scoped> +@import "../assets/vars.scss"; + +.nav-tabs { + border-bottom: 1px solid $primary; + + .nav-item { + flex-grow: 1; + + .nav-link { + text-align: center; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + + &.active { + background-color: $highlight-white; + .dark & { + color: $dark-font-color; + background-color: $dark-header-bg; + } + border-color: transparent transparent $primary transparent; + border-width: 1px 1px 6px 1px; + } + + &:hover { + .dark & { + background-color: $dark-header-bg; + } + border-color: $primary; + border-width: 1px 1px 6px 1px; + } + } + } +} +</style> diff --git a/src/components/settings/Appearance.vue b/src/components/settings/Appearance.vue index a1391d614..04411960f 100644 --- a/src/components/settings/Appearance.vue +++ b/src/components/settings/Appearance.vue @@ -1,186 +1,97 @@ <template> <div> - <div class="my-4"> - <label for="language" class="form-label"> - {{ $t("Language") }} - </label> - <select id="language" v-model="$root.language" class="form-select"> - <option - v-for="(lang, i) in $i18n.availableLocales" - :key="`Lang${i}`" - :value="lang" - > - {{ $i18n.messages[lang].languageName }} - </option> - </select> - </div> - <div class="my-4"> - <label for="timezone" class="form-label">{{ $t("Theme") }}</label> - <div> - <div - class="btn-group" - role="group" - aria-label="Basic checkbox toggle button group" - > - <input - id="btncheck1" - v-model="$root.userTheme" - type="radio" - class="btn-check" - name="theme" - autocomplete="off" - value="light" - /> - <label class="btn btn-outline-primary" for="btncheck1"> - {{ $t("Light") }} - </label> - - <input - id="btncheck2" - v-model="$root.userTheme" - type="radio" - class="btn-check" - name="theme" - autocomplete="off" - value="dark" - /> - <label class="btn btn-outline-primary" for="btncheck2"> - {{ $t("Dark") }} - </label> - - <input - id="btncheck3" - v-model="$root.userTheme" - type="radio" - class="btn-check" - name="theme" - autocomplete="off" - value="auto" - /> - <label class="btn btn-outline-primary" for="btncheck3"> - {{ $t("Auto") }} - </label> - </div> - </div> - </div> - <div class="my-4"> - <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label> - <div> - <div - class="btn-group" - role="group" - aria-label="Basic checkbox toggle button group" - > - <input - id="btncheck4" - v-model="$root.userHeartbeatBar" - type="radio" - class="btn-check" - name="heartbeatBarTheme" - autocomplete="off" - value="normal" - /> - <label class="btn btn-outline-primary" for="btncheck4"> - {{ $t("Normal") }} - </label> - - <input - id="btncheck5" - v-model="$root.userHeartbeatBar" - type="radio" - class="btn-check" - name="heartbeatBarTheme" - autocomplete="off" - value="bottom" - /> - <label class="btn btn-outline-primary" for="btncheck5"> - {{ $t("Bottom") }} - </label> - - <input - id="btncheck6" - v-model="$root.userHeartbeatBar" - type="radio" - class="btn-check" - name="heartbeatBarTheme" - autocomplete="off" - value="none" - /> - <label class="btn btn-outline-primary" for="btncheck6"> - {{ $t("None") }} - </label> - </div> - </div> - </div> - - <!-- Timeline --> - <div class="my-4"> - <label class="form-label">{{ $t("styleElapsedTime") }}</label> - <div> - <div class="btn-group" role="group"> - <input - id="styleElapsedTimeShowNoLine" - v-model="$root.styleElapsedTime" - type="radio" - class="btn-check" - name="styleElapsedTime" - autocomplete="off" - value="no-line" - /> - <label class="btn btn-outline-primary" for="styleElapsedTimeShowNoLine"> - {{ $t("styleElapsedTimeShowNoLine") }} - </label> - - <input - id="styleElapsedTimeShowWithLine" - v-model="$root.styleElapsedTime" - type="radio" - class="btn-check" - name="styleElapsedTime" - autocomplete="off" - value="with-line" - /> - <label class="btn btn-outline-primary" for="styleElapsedTimeShowWithLine"> - {{ $t("styleElapsedTimeShowWithLine") }} - </label> - - <input - id="styleElapsedTimeNone" - v-model="$root.styleElapsedTime" - type="radio" - class="btn-check" - name="styleElapsedTime" - autocomplete="off" - value="none" - /> - <label class="btn btn-outline-primary" for="styleElapsedTimeNone"> - {{ $t("None") }} - </label> - </div> - </div> - </div> + <HorizontalTabHeader + v-model:selected="storageLoc" + class="mt-4" + :tabs="['Browser', 'Server']" + /> + <AppearanceSettings + v-if="storageLoc == 0" + v-model:language="$root.language" + v-model:userTheme="$root.userTheme" + v-model:userHeartbeatBar="$root.userHeartbeatBar" + v-model:styleElapsedTime="$root.styleElapsedTime" + :languages="languagesList" + /> + <AppearanceSettings + v-if="storageLoc == 1" + :language="settings.defaultAppearance.language || ''" + :userTheme="settings.defaultAppearance.theme || ''" + :userHeartbeatBar="settings.defaultAppearance.heartbeatBarTheme || ''" + :styleElapsedTime="settings.defaultAppearance.styleElapsedTime || ''" + :languages="languagesList" + @update:language="updateDefaultLanguage" + @update:user-theme="updateDefaultTheme" + @update:user-heartbeat-bar="updateDefaultHeartbeatBarTheme" + @update:style-elapsed-time="updateDefaultStyleElapsedTime" + /> </div> </template> <script> -export default { +import HorizontalTabHeader from "../HorizontalTabHeader.vue"; +import AppearanceSettings from "./AppearanceSettings.vue"; +export default { + components: { + HorizontalTabHeader, + AppearanceSettings + }, + data() { + return { + storageLoc: 0, + languagesList: this.$i18n.availableLocales.map(lang => { + return { + value: lang, + label: this.$i18n.messages[lang].languageName, + }; + }) + }; + }, + computed: { + settings() { + return this.$parent.$parent.$parent.settings; + }, + saveSettings() { + return this.$parent.$parent.$parent.saveSettings; + } + }, + methods: { + updateDefaultLanguage(language) { + this.updateDefaultAppearance({ + ...this.settings.defaultAppearance, + language + }); + }, + updateDefaultTheme(theme) { + this.updateDefaultAppearance({ + ...this.settings.defaultAppearance, + theme: theme + }); + }, + updateDefaultHeartbeatBarTheme(theme) { + this.updateDefaultAppearance({ + ...this.settings.defaultAppearance, + heartbeatBarTheme: theme + }); + }, + updateDefaultStyleElapsedTime(styleElapsedTime) { + this.updateDefaultAppearance({ + ...this.settings.defaultAppearance, + styleElapsedTime + }); + }, + updateDefaultAppearance(appearance) { + this.$parent.$parent.$parent.settings.defaultAppearance = appearance; + this.saveSettings(); + } + } }; </script> <style lang="scss" scoped> @import "../../assets/vars.scss"; -.btn-check:active + .btn-outline-primary, -.btn-check:checked + .btn-outline-primary, -.btn-check:hover + .btn-outline-primary { - color: #fff; - - .dark & { - color: #000; - } -} - .dark { .list-group-item { background-color: $dark-bg2; diff --git a/src/components/settings/AppearanceSettings.vue b/src/components/settings/AppearanceSettings.vue new file mode 100644 index 000000000..54c3470b9 --- /dev/null +++ b/src/components/settings/AppearanceSettings.vue @@ -0,0 +1,164 @@ +<template> + <div> + <div class="my-4"> + <label for="language" class="form-label"> + {{ $t("Language") }} + </label> + <select + id="language" :value="language" class="form-select" + @input="$emit('update:language', $event.target.value)" + > + <option v-for="(lang, i) in languages" :key="`Lang${i}`" :value="lang.value"> + {{ lang.label }} + </option> + </select> + </div> + <div class="my-4"> + <label for="timezone" class="form-label">{{ $t("Theme") }}</label> + <div> + <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> + <input + id="btncheck1" :checked="userTheme == 'light'" type="radio" class="btn-check" name="theme" + autocomplete="off" value="light" @input="$emit('update:userTheme', 'light')" + /> + <label class="btn btn-outline-primary" for="btncheck1"> + {{ $t("Light") }} + </label> + + <input + id="btncheck2" :checked="userTheme == 'dark'" type="radio" class="btn-check" name="theme" + autocomplete="off" value="dark" @input="$emit('update:userTheme', 'dark')" + /> + <label class="btn btn-outline-primary" for="btncheck2"> + {{ $t("Dark") }} + </label> + + <input + id="btncheck3" :checked="userTheme == 'auto'" type="radio" class="btn-check" name="theme" + autocomplete="off" value="auto" @input="$emit('update:userTheme', 'auto')" + /> + <label class="btn btn-outline-primary" for="btncheck3"> + {{ $t("Auto") }} + </label> + </div> + </div> + </div> + <div class="my-4"> + <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label> + <div> + <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> + <input + id="btncheck4" :checked="userHeartbeatBar == 'normal'" type="radio" class="btn-check" + name="heartbeatBarTheme" autocomplete="off" value="normal" + @input="$emit('update:userHeartbeatBar', 'normal')" + /> + <label class="btn btn-outline-primary" for="btncheck4"> + {{ $t("Normal") }} + </label> + + <input + id="btncheck5" :checked="userHeartbeatBar == 'bottom'" type="radio" class="btn-check" + name="heartbeatBarTheme" autocomplete="off" value="bottom" + @input="$emit('update:userHeartbeatBar', 'bottom')" + /> + <label class="btn btn-outline-primary" for="btncheck5"> + {{ $t("Bottom") }} + </label> + + <input + id="btncheck6" :checked="userHeartbeatBar == 'none'" type="radio" class="btn-check" + name="heartbeatBarTheme" autocomplete="off" value="none" + @input="$emit('update:userHeartbeatBar', 'none')" + /> + <label class="btn btn-outline-primary" for="btncheck6"> + {{ $t("None") }} + </label> + </div> + </div> + </div> + + <!-- Timeline --> + <div class="my-4"> + <label class="form-label">{{ $t("styleElapsedTime") }}</label> + <div> + <div class="btn-group" role="group"> + <input + id="styleElapsedTimeShowNoLine" :checked="styleElapsedTime == 'no-line'" type="radio" + class="btn-check" name="styleElapsedTime" autocomplete="off" value="no-line" + @input="$emit('update:styleElapsedTime', 'no-line')" + /> + <label class="btn btn-outline-primary" for="styleElapsedTimeShowNoLine"> + {{ $t("styleElapsedTimeShowNoLine") }} + </label> + + <input + id="styleElapsedTimeShowWithLine" :checked="styleElapsedTime == 'with-line'" type="radio" + class="btn-check" name="styleElapsedTime" autocomplete="off" value="with-line" + @input="$emit('update:styleElapsedTime', 'with-line')" + /> + <label class="btn btn-outline-primary" for="styleElapsedTimeShowWithLine"> + {{ $t("styleElapsedTimeShowWithLine") }} + </label> + + <input + id="styleElapsedTimeNone" :checked="styleElapsedTime == 'none'" type="radio" class="btn-check" + name="styleElapsedTime" autocomplete="off" value="none" + @input="$emit('update:styleElapsedTime', 'none')" + /> + <label class="btn btn-outline-primary" for="styleElapsedTimeNone"> + {{ $t("None") }} + </label> + </div> + </div> + </div> + </div> +</template> + +<script lang="js"> +export default { + props: { + languages: { + type: Array, + required: true, + }, + language: { + type: String, + required: true, + }, + userTheme: { + type: String, + required: true, + }, + userHeartbeatBar: { + type: String, + required: true, + }, + styleElapsedTime: { + type: String, + required: true, + }, + }, + emits: [ + "update:language", + "update:userTheme", + "update:userHeartbeatBar", + "update:styleElapsedTime", + ], + data() { + return {}; + }, +}; +</script> + +<style lang="scss" scoped> +.btn-check:active+.btn-outline-primary, +.btn-check:checked+.btn-outline-primary, +.btn-check:hover+.btn-outline-primary { + color: #fff; + + .dark & { + color: #000; + } +} + +</style> diff --git a/src/mixins/theme.js b/src/mixins/theme.js index e1486d5af..a8479cfb7 100644 --- a/src/mixins/theme.js +++ b/src/mixins/theme.js @@ -13,6 +13,24 @@ export default { }, mounted() { + if (window.defaultAppearance) { + if (window.defaultAppearance.language) { + localStorage.locale = window.defaultAppearance.language; + } + + if (window.defaultAppearance.theme) { + this.userTheme = window.defaultAppearance.theme; + } + + if (window.defaultAppearance.heartbeatBarTheme) { + this.userHeartbeatBar = window.defaultAppearance.heartbeatBarTheme; + } + + if (window.defaultAppearance.styleElapsedTime) { + this.styleElapsedTime = window.defaultAppearance.styleElapsedTime; + } + } + // Default Light if (! this.userTheme) { this.userTheme = "auto"; diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 96bb1fee1..40f5304ee 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -183,6 +183,15 @@ export default { this.settings.trustProxy = false; } + if (this.settings.defaultAppearance === undefined) { + this.settings.defaultAppearance = { + theme: null, + language: null, + heartbeatBarTheme: null, + styleElapsedTime: null, + }; + } + this.settingsLoaded = true; }); }, From 9e2c66657d3a7a39bfa172c49d74205cdf706b12 Mon Sep 17 00:00:00 2001 From: Nelson Chan <chakflying@hotmail.com> Date: Mon, 12 Feb 2024 06:33:22 +0800 Subject: [PATCH 2/2] Chore: Fix lint --- src/components/HorizontalTabHeader.vue | 9 +- .../settings/AppearanceSettings.vue | 128 ++++++++++++++---- 2 files changed, 107 insertions(+), 30 deletions(-) diff --git a/src/components/HorizontalTabHeader.vue b/src/components/HorizontalTabHeader.vue index 3857394dc..00712ad0f 100644 --- a/src/components/HorizontalTabHeader.vue +++ b/src/components/HorizontalTabHeader.vue @@ -2,7 +2,12 @@ <div> <ul class="nav nav-tabs"> <li v-for="(tab, index) in tabs" :key="index" class="nav-item"> - <a class="nav-link" :class="{ active: index == selected }" href="#" @click="$emit('update:selected', index)"> + <a + class="nav-link" + :class="{ active: index == selected }" + href="#" + @click="$emit('update:selected', index)" + > {{ tab }} </a> </li> @@ -45,10 +50,12 @@ export default { &.active { background-color: $highlight-white; + .dark & { color: $dark-font-color; background-color: $dark-header-bg; } + border-color: transparent transparent $primary transparent; border-width: 1px 1px 6px 1px; } diff --git a/src/components/settings/AppearanceSettings.vue b/src/components/settings/AppearanceSettings.vue index 54c3470b9..670fd7164 100644 --- a/src/components/settings/AppearanceSettings.vue +++ b/src/components/settings/AppearanceSettings.vue @@ -5,10 +5,16 @@ {{ $t("Language") }} </label> <select - id="language" :value="language" class="form-select" + id="language" + :value="language" + class="form-select" @input="$emit('update:language', $event.target.value)" > - <option v-for="(lang, i) in languages" :key="`Lang${i}`" :value="lang.value"> + <option + v-for="(lang, i) in languages" + :key="`Lang${i}`" + :value="lang.value" + > {{ lang.label }} </option> </select> @@ -16,26 +22,48 @@ <div class="my-4"> <label for="timezone" class="form-label">{{ $t("Theme") }}</label> <div> - <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> + <div + class="btn-group" + role="group" + aria-label="Basic checkbox toggle button group" + > <input - id="btncheck1" :checked="userTheme == 'light'" type="radio" class="btn-check" name="theme" - autocomplete="off" value="light" @input="$emit('update:userTheme', 'light')" + id="btncheck1" + :checked="userTheme == 'light'" + type="radio" + class="btn-check" + name="theme" + autocomplete="off" + value="light" + @input="$emit('update:userTheme', 'light')" /> <label class="btn btn-outline-primary" for="btncheck1"> {{ $t("Light") }} </label> <input - id="btncheck2" :checked="userTheme == 'dark'" type="radio" class="btn-check" name="theme" - autocomplete="off" value="dark" @input="$emit('update:userTheme', 'dark')" + id="btncheck2" + :checked="userTheme == 'dark'" + type="radio" + class="btn-check" + name="theme" + autocomplete="off" + value="dark" + @input="$emit('update:userTheme', 'dark')" /> <label class="btn btn-outline-primary" for="btncheck2"> {{ $t("Dark") }} </label> <input - id="btncheck3" :checked="userTheme == 'auto'" type="radio" class="btn-check" name="theme" - autocomplete="off" value="auto" @input="$emit('update:userTheme', 'auto')" + id="btncheck3" + :checked="userTheme == 'auto'" + type="radio" + class="btn-check" + name="theme" + autocomplete="off" + value="auto" + @input="$emit('update:userTheme', 'auto')" /> <label class="btn btn-outline-primary" for="btncheck3"> {{ $t("Auto") }} @@ -46,10 +74,19 @@ <div class="my-4"> <label class="form-label">{{ $t("Theme - Heartbeat Bar") }}</label> <div> - <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> + <div + class="btn-group" + role="group" + aria-label="Basic checkbox toggle button group" + > <input - id="btncheck4" :checked="userHeartbeatBar == 'normal'" type="radio" class="btn-check" - name="heartbeatBarTheme" autocomplete="off" value="normal" + id="btncheck4" + :checked="userHeartbeatBar == 'normal'" + type="radio" + class="btn-check" + name="heartbeatBarTheme" + autocomplete="off" + value="normal" @input="$emit('update:userHeartbeatBar', 'normal')" /> <label class="btn btn-outline-primary" for="btncheck4"> @@ -57,8 +94,13 @@ </label> <input - id="btncheck5" :checked="userHeartbeatBar == 'bottom'" type="radio" class="btn-check" - name="heartbeatBarTheme" autocomplete="off" value="bottom" + id="btncheck5" + :checked="userHeartbeatBar == 'bottom'" + type="radio" + class="btn-check" + name="heartbeatBarTheme" + autocomplete="off" + value="bottom" @input="$emit('update:userHeartbeatBar', 'bottom')" /> <label class="btn btn-outline-primary" for="btncheck5"> @@ -66,8 +108,13 @@ </label> <input - id="btncheck6" :checked="userHeartbeatBar == 'none'" type="radio" class="btn-check" - name="heartbeatBarTheme" autocomplete="off" value="none" + id="btncheck6" + :checked="userHeartbeatBar == 'none'" + type="radio" + class="btn-check" + name="heartbeatBarTheme" + autocomplete="off" + value="none" @input="$emit('update:userHeartbeatBar', 'none')" /> <label class="btn btn-outline-primary" for="btncheck6"> @@ -83,29 +130,53 @@ <div> <div class="btn-group" role="group"> <input - id="styleElapsedTimeShowNoLine" :checked="styleElapsedTime == 'no-line'" type="radio" - class="btn-check" name="styleElapsedTime" autocomplete="off" value="no-line" + id="styleElapsedTimeShowNoLine" + :checked="styleElapsedTime == 'no-line'" + type="radio" + class="btn-check" + name="styleElapsedTime" + autocomplete="off" + value="no-line" @input="$emit('update:styleElapsedTime', 'no-line')" /> - <label class="btn btn-outline-primary" for="styleElapsedTimeShowNoLine"> + <label + class="btn btn-outline-primary" + for="styleElapsedTimeShowNoLine" + > {{ $t("styleElapsedTimeShowNoLine") }} </label> <input - id="styleElapsedTimeShowWithLine" :checked="styleElapsedTime == 'with-line'" type="radio" - class="btn-check" name="styleElapsedTime" autocomplete="off" value="with-line" + id="styleElapsedTimeShowWithLine" + :checked="styleElapsedTime == 'with-line'" + type="radio" + class="btn-check" + name="styleElapsedTime" + autocomplete="off" + value="with-line" @input="$emit('update:styleElapsedTime', 'with-line')" /> - <label class="btn btn-outline-primary" for="styleElapsedTimeShowWithLine"> + <label + class="btn btn-outline-primary" + for="styleElapsedTimeShowWithLine" + > {{ $t("styleElapsedTimeShowWithLine") }} </label> <input - id="styleElapsedTimeNone" :checked="styleElapsedTime == 'none'" type="radio" class="btn-check" - name="styleElapsedTime" autocomplete="off" value="none" + id="styleElapsedTimeNone" + :checked="styleElapsedTime == 'none'" + type="radio" + class="btn-check" + name="styleElapsedTime" + autocomplete="off" + value="none" @input="$emit('update:styleElapsedTime', 'none')" /> - <label class="btn btn-outline-primary" for="styleElapsedTimeNone"> + <label + class="btn btn-outline-primary" + for="styleElapsedTimeNone" + > {{ $t("None") }} </label> </div> @@ -151,14 +222,13 @@ export default { </script> <style lang="scss" scoped> -.btn-check:active+.btn-outline-primary, -.btn-check:checked+.btn-outline-primary, -.btn-check:hover+.btn-outline-primary { +.btn-check:active + .btn-outline-primary, +.btn-check:checked + .btn-outline-primary, +.btn-check:hover + .btn-outline-primary { color: #fff; .dark & { color: #000; } } - </style>