Добавлены настройки темы по умолчанию
This commit is contained in:
+4
-1
@@ -7,4 +7,7 @@ VITE_BANNER_SLOGAN=— qr-код без излишеств
|
|||||||
VITE_BANNER_URL=https://github.com/IliaMiheev/iQR
|
VITE_BANNER_URL=https://github.com/IliaMiheev/iQR
|
||||||
|
|
||||||
# Акцентный цвет (hex, rgb или css-имя)
|
# Акцентный цвет (hex, rgb или css-имя)
|
||||||
VITE_ACCENT_COLOR=#ff4500
|
VITE_ACCENT_COLOR="#ff4500"
|
||||||
|
|
||||||
|
# Тема по умолчанию: dark или light (если пользователь ещё не переключал)
|
||||||
|
VITE_DEFAULT_THEME=light
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ npm run dev
|
|||||||
| `VITE_BANNER_SLOGAN` | Слоган в плашке | `— qr-код без излишеств` |
|
| `VITE_BANNER_SLOGAN` | Слоган в плашке | `— qr-код без излишеств` |
|
||||||
| `VITE_BANNER_URL` | Ссылка плашки | репозиторий iQR |
|
| `VITE_BANNER_URL` | Ссылка плашки | репозиторий iQR |
|
||||||
| `VITE_ACCENT_COLOR` | Акцентный цвет | `#ff4500` |
|
| `VITE_ACCENT_COLOR` | Акцентный цвет | `#ff4500` |
|
||||||
|
| `VITE_DEFAULT_THEME` | Тема по умолчанию (`dark` / `light`) | `dark` |
|
||||||
|
|
||||||
После изменения `.env` перезапустите `npm run dev`.
|
После изменения `.env` перезапустите `npm run dev`.
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ environment:
|
|||||||
BANNER_SLOGAN: "— быстрые qr-коды"
|
BANNER_SLOGAN: "— быстрые qr-коды"
|
||||||
BANNER_URL: "https://example.com"
|
BANNER_URL: "https://example.com"
|
||||||
ACCENT_COLOR: "#7c3aed"
|
ACCENT_COLOR: "#7c3aed"
|
||||||
|
DEFAULT_THEME: "light"
|
||||||
```
|
```
|
||||||
|
|
||||||
При старте контейнера генерируется `config.json`, поэтому пересборка образа для смены текстов не нужна.
|
При старте контейнера генерируется `config.json`, поэтому пересборка образа для смены текстов не нужна.
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ services:
|
|||||||
BANNER_SLOGAN: "— qr-код без излишеств"
|
BANNER_SLOGAN: "— qr-код без излишеств"
|
||||||
BANNER_URL: "https://github.com/IliaMiheev/iQR"
|
BANNER_URL: "https://github.com/IliaMiheev/iQR"
|
||||||
ACCENT_COLOR: "#ff4500"
|
ACCENT_COLOR: "#ff4500"
|
||||||
|
DEFAULT_THEME: "dark"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ BANNER_BRAND="${BANNER_BRAND:-iQR}"
|
|||||||
BANNER_SLOGAN="${BANNER_SLOGAN:-— qr-код без излишеств}"
|
BANNER_SLOGAN="${BANNER_SLOGAN:-— qr-код без излишеств}"
|
||||||
BANNER_URL="${BANNER_URL:-https://github.com/IliaMiheev/iQR}"
|
BANNER_URL="${BANNER_URL:-https://github.com/IliaMiheev/iQR}"
|
||||||
ACCENT_COLOR="${ACCENT_COLOR:-#ff4500}"
|
ACCENT_COLOR="${ACCENT_COLOR:-#ff4500}"
|
||||||
|
DEFAULT_THEME="${DEFAULT_THEME:-dark}"
|
||||||
|
|
||||||
cat > /usr/share/nginx/html/config.json <<EOF
|
cat > /usr/share/nginx/html/config.json <<EOF
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,8 @@ cat > /usr/share/nginx/html/config.json <<EOF
|
|||||||
"bannerBrand": "$(printf '%s' "$BANNER_BRAND" | sed 's/"/\\"/g')",
|
"bannerBrand": "$(printf '%s' "$BANNER_BRAND" | sed 's/"/\\"/g')",
|
||||||
"bannerSlogan": "$(printf '%s' "$BANNER_SLOGAN" | sed 's/"/\\"/g')",
|
"bannerSlogan": "$(printf '%s' "$BANNER_SLOGAN" | sed 's/"/\\"/g')",
|
||||||
"bannerUrl": "$(printf '%s' "$BANNER_URL" | sed 's/"/\\"/g')",
|
"bannerUrl": "$(printf '%s' "$BANNER_URL" | sed 's/"/\\"/g')",
|
||||||
"accentColor": "$(printf '%s' "$ACCENT_COLOR" | sed 's/"/\\"/g')"
|
"accentColor": "$(printf '%s' "$ACCENT_COLOR" | sed 's/"/\\"/g')",
|
||||||
|
"defaultTheme": "$(printf '%s' "$DEFAULT_THEME" | sed 's/"/\\"/g')"
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -134,7 +134,7 @@ onUnmounted(() => {
|
|||||||
<div class="qr-modal__backdrop" @click="closeQrModal" />
|
<div class="qr-modal__backdrop" @click="closeQrModal" />
|
||||||
|
|
||||||
<aside class="qr-modal__toolbar" aria-label="Инструменты" @click.stop>
|
<aside class="qr-modal__toolbar" aria-label="Инструменты" @click.stop>
|
||||||
<button type="button" title="Увеличить" @click="zoomIn">+</button>
|
<button type="button" title="Уменьшить" @click="zoomOut">−</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="qr-modal__tool-btn"
|
class="qr-modal__tool-btn"
|
||||||
@@ -153,7 +153,7 @@ onUnmounted(() => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" title="Уменьшить" @click="zoomOut">−</button>
|
<button type="button" title="Увеличить" @click="zoomIn">+</button>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div class="qr-modal__dialog" @click.stop>
|
<div class="qr-modal__dialog" @click.stop>
|
||||||
|
|||||||
+13
-11
@@ -474,8 +474,9 @@ html.light .qr-modal__backdrop {
|
|||||||
border: 1px solid var(--surface-border);
|
border: 1px solid var(--surface-border);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--surface-solid);
|
background: var(--surface-solid);
|
||||||
color: var(--text);
|
color: var(--accent);
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -486,7 +487,8 @@ html.light .qr-modal__backdrop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.qr-modal__close:hover {
|
.qr-modal__close:hover {
|
||||||
border-color: var(--accent);
|
background: var(--surface-hover);
|
||||||
|
border-color: var(--surface-border);
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
@@ -511,7 +513,7 @@ html.light .qr-modal__backdrop {
|
|||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
background: var(--surface-solid);
|
background: var(--surface-solid);
|
||||||
border: 1px solid var(--surface-border);
|
border: none;
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
-webkit-backdrop-filter: blur(8px);
|
-webkit-backdrop-filter: blur(8px);
|
||||||
@@ -531,11 +533,18 @@ html.light .qr-modal__backdrop {
|
|||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
transition:
|
||||||
|
color 0.2s ease,
|
||||||
|
border-color 0.2s ease,
|
||||||
|
background-color 0.2s ease,
|
||||||
|
box-shadow 0.2s ease,
|
||||||
|
transform 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-modal__toolbar button:hover {
|
.qr-modal__toolbar button:hover {
|
||||||
background: var(--surface-hover);
|
background: var(--surface-hover);
|
||||||
border-color: var(--surface-border);
|
color: var(--accent);
|
||||||
|
border-color: var(--accent);
|
||||||
transform: none;
|
transform: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
@@ -551,11 +560,6 @@ html.light .qr-modal__backdrop {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-modal__tool-btn:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
border-color: var(--accent) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qr-modal__status-slot {
|
.qr-modal__status-slot {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -617,7 +621,6 @@ html.light .qr-modal__backdrop {
|
|||||||
|
|
||||||
#qr-code {
|
#qr-code {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding: 16px;
|
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
background: var(--surface-solid);
|
background: var(--surface-solid);
|
||||||
border: 1px solid var(--surface-border);
|
border: 1px solid var(--surface-border);
|
||||||
@@ -643,7 +646,6 @@ canvas {
|
|||||||
display: block;
|
display: block;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--canvas-bg);
|
background: var(--canvas-bg);
|
||||||
border: 1px solid var(--canvas-border);
|
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
transition: border-color 0.35s ease;
|
transition: border-color 0.35s ease;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { inject, ref, computed } from 'vue'
|
||||||
|
import { resolveTheme } from '../config/applyTheme'
|
||||||
|
|
||||||
export function useTheme() {
|
export function useTheme() {
|
||||||
const theme = ref('dark')
|
const config = inject('appConfig')
|
||||||
|
const theme = ref(resolveTheme(config))
|
||||||
|
|
||||||
const isLightTheme = computed(() => theme.value === 'light')
|
const isLightTheme = computed(() => theme.value === 'light')
|
||||||
|
|
||||||
@@ -15,16 +17,5 @@ export function useTheme() {
|
|||||||
applyTheme(theme.value === 'dark' ? 'light' : 'dark')
|
applyTheme(theme.value === 'dark' ? 'light' : 'dark')
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const saved = localStorage.getItem('theme')
|
|
||||||
if (saved === 'dark' || saved === 'light') {
|
|
||||||
applyTheme(saved)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
||||||
applyTheme(prefersDark ? 'dark' : 'light')
|
|
||||||
})
|
|
||||||
|
|
||||||
return { theme, isLightTheme, toggleTheme }
|
return { theme, isLightTheme, toggleTheme }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,19 @@
|
|||||||
|
import { normalizeTheme } from './defaults'
|
||||||
|
|
||||||
|
export function resolveTheme(config) {
|
||||||
|
const saved = localStorage.getItem('theme')
|
||||||
|
if (saved === 'dark' || saved === 'light') {
|
||||||
|
return saved
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeTheme(config.defaultTheme)
|
||||||
|
}
|
||||||
|
|
||||||
export function applyAppConfig(config) {
|
export function applyAppConfig(config) {
|
||||||
const root = document.documentElement
|
const root = document.documentElement
|
||||||
|
|
||||||
root.style.setProperty('--accent', config.accentColor)
|
root.style.setProperty('--accent', config.accentColor)
|
||||||
|
root.className = resolveTheme(config)
|
||||||
|
|
||||||
const themeMeta = document.querySelector('meta[name="theme-color"]')
|
const themeMeta = document.querySelector('meta[name="theme-color"]')
|
||||||
if (themeMeta) {
|
if (themeMeta) {
|
||||||
|
|||||||
@@ -4,4 +4,10 @@ export const defaultConfig = {
|
|||||||
bannerSlogan: '— qr-код без излишеств',
|
bannerSlogan: '— qr-код без излишеств',
|
||||||
bannerUrl: 'https://github.com/IliaMiheev/iQR',
|
bannerUrl: 'https://github.com/IliaMiheev/iQR',
|
||||||
accentColor: '#ff4500',
|
accentColor: '#ff4500',
|
||||||
|
defaultTheme: 'dark',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeTheme(value) {
|
||||||
|
const normalized = String(value ?? '').trim().toLowerCase()
|
||||||
|
return normalized === 'light' ? 'light' : 'dark'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defaultConfig } from './defaults'
|
import { defaultConfig, normalizeTheme } from './defaults'
|
||||||
|
|
||||||
function fromBuildEnv() {
|
function fromBuildEnv() {
|
||||||
const env = import.meta.env
|
const env = import.meta.env
|
||||||
@@ -9,6 +9,7 @@ function fromBuildEnv() {
|
|||||||
bannerSlogan: env.VITE_BANNER_SLOGAN,
|
bannerSlogan: env.VITE_BANNER_SLOGAN,
|
||||||
bannerUrl: env.VITE_BANNER_URL,
|
bannerUrl: env.VITE_BANNER_URL,
|
||||||
accentColor: env.VITE_ACCENT_COLOR,
|
accentColor: env.VITE_ACCENT_COLOR,
|
||||||
|
defaultTheme: env.VITE_DEFAULT_THEME,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,5 +45,7 @@ export async function loadConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.defaultTheme = normalizeTheme(config.defaultTheme)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user