refactor: centralize UI config and inject CSS variables dynamically
This commit is contained in:
12
src/App.vue
12
src/App.vue
@@ -6,6 +6,7 @@ import Footer from './components/Footer.vue'
|
||||
import Sidebar from './components/Sidebar.vue'
|
||||
import InstallPrompt from './components/InstallPrompt.vue'
|
||||
import ReloadPrompt from './components/ReloadPrompt.vue'
|
||||
import { UI_CONFIG } from './config/ui'
|
||||
|
||||
const isSidebarOpen = ref(window.innerWidth >= 768)
|
||||
const router = useRouter()
|
||||
@@ -31,6 +32,11 @@ const handleResize = () => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Set global CSS variables from config
|
||||
document.documentElement.style.setProperty('--header-height', `${UI_CONFIG.headerHeight}px`)
|
||||
document.documentElement.style.setProperty('--footer-height', `${UI_CONFIG.footerHeight}px`)
|
||||
document.documentElement.style.setProperty('--page-padding', `${UI_CONFIG.pagePadding}px`)
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
@@ -70,14 +76,14 @@ onUnmounted(() => {
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
/* Space for fixed footer on mobile + extra margin (match top padding 2rem + footer height ~40px) */
|
||||
padding-bottom: calc(1rem + 40px + env(safe-area-inset-bottom));
|
||||
/* Space for fixed footer on mobile + extra margin */
|
||||
padding-bottom: calc(1rem + var(--footer-height) + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.main-content {
|
||||
padding: 0.5rem;
|
||||
padding-bottom: calc(0.5rem + 40px + env(safe-area-inset-bottom));
|
||||
padding-bottom: calc(0.5rem + var(--footer-height) + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ const version = __APP_VERSION__;
|
||||
<style scoped>
|
||||
.app-footer {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
height: var(--footer-height);
|
||||
padding: 0 0.5rem;
|
||||
/* Background handled by glass-panel */
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
@@ -24,12 +25,9 @@ const version = __APP_VERSION__;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
/* Remove fixed height to allow content to dictate size */
|
||||
/* height: 30px; */
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
@@ -65,7 +65,10 @@ onMounted(() => {
|
||||
/* Remove hardcoded colors and use theme variables */
|
||||
background: var(--header-bg);
|
||||
color: var(--text-color);
|
||||
padding: 1rem;
|
||||
height: var(--header-height);
|
||||
padding: 0 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* box-shadow handled by glass-panel class */
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@@ -77,7 +80,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.header-content {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
|
||||
@@ -16,7 +16,7 @@ const {
|
||||
stopListening
|
||||
} = useExtension()
|
||||
|
||||
const { height: textareaHeight } = useFillHeight(textareaRef, 120)
|
||||
const { height: textareaHeight } = useFillHeight(textareaRef, 40)
|
||||
|
||||
// Watch for clipboard updates from extension
|
||||
watch(lastClipboardText, (newText) => {
|
||||
|
||||
@@ -142,7 +142,6 @@ const generatePasswords = () => {
|
||||
class="tool-textarea"
|
||||
v-model="result"
|
||||
placeholder="Generated passwords will appear here..."
|
||||
readonly
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ const format = useLocalStorage('format', 'png', 'qr-code')
|
||||
const svgContent = ref('')
|
||||
const previewRef = ref(null)
|
||||
|
||||
const { height: previewHeight } = useFillHeight(previewRef, 40) // 40px margin bottom
|
||||
const { height: previewHeight } = useFillHeight(previewRef, 40) // 40px extra margin
|
||||
|
||||
const generateQR = async () => {
|
||||
if (!text.value) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { onMounted, onUnmounted, ref, nextTick } from 'vue'
|
||||
import { UI_CONFIG } from '../config/ui'
|
||||
|
||||
export function useFillHeight(elementRef, marginBottom = 20) {
|
||||
export function useFillHeight(elementRef, extraMargin = 0) {
|
||||
const height = ref('auto')
|
||||
|
||||
const updateHeight = () => {
|
||||
@@ -8,16 +9,10 @@ export function useFillHeight(elementRef, marginBottom = 20) {
|
||||
|
||||
const rect = elementRef.value.getBoundingClientRect()
|
||||
const windowHeight = window.innerHeight
|
||||
// Calculate available space: window height - element top position - margin bottom
|
||||
// We also need to account for the footer height if it's fixed or layout related
|
||||
// The user mentioned "margin bottom from footer".
|
||||
// If footer is in the flow, we might just want to fill the parent container?
|
||||
// But user asked for JS resizing.
|
||||
|
||||
// Let's assume we want to fill down to (windowHeight - marginBottom).
|
||||
// This assumes the element should stretch to the bottom of the viewport.
|
||||
|
||||
const availableHeight = windowHeight - rect.top - marginBottom
|
||||
// Calculate available space: window height - element top position - footer height - padding - extra margin
|
||||
const bottomOffset = UI_CONFIG.footerHeight + UI_CONFIG.pagePadding + extraMargin
|
||||
const availableHeight = windowHeight - rect.top - bottomOffset
|
||||
|
||||
// Ensure minimum height
|
||||
if (availableHeight > 100) {
|
||||
|
||||
6
src/config/ui.js
Normal file
6
src/config/ui.js
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
export const UI_CONFIG = {
|
||||
headerHeight: 64,
|
||||
footerHeight: 50,
|
||||
pagePadding: 16 // Single side padding (1rem)
|
||||
}
|
||||
Reference in New Issue
Block a user