Skip to content

Commit e768dba

Browse files
committed
mobile view
1 parent 8e0eb18 commit e768dba

File tree

3 files changed

+282
-110
lines changed

3 files changed

+282
-110
lines changed

src/App.vue

Lines changed: 138 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
<template>
22
<div id="app">
3+
<!-- Mobile Header -->
4+
<header class="mobile-header">
5+
<button class="mobile-menu-btn" @click="mobileMenuOpen = !mobileMenuOpen">
6+
<span class="icon">☰</span> Equations
7+
</button>
8+
<button
9+
class="mobile-menu-btn"
10+
@click="mobileEditorOpen = !mobileEditorOpen"
11+
>
12+
<span class="icon">✎</span> Code
13+
</button>
14+
</header>
15+
316
<!-- Left Sidebar -->
4-
<aside class="sidebar">
5-
<h2>Equations</h2>
6-
<EquationSelector @change="markdown = $event" />
17+
<aside class="sidebar" :class="{ 'mobile-open': mobileMenuOpen }">
18+
<div class="mobile-close-header">
19+
<h2>Equations</h2>
20+
<button class="close-btn" @click="mobileMenuOpen = false">✕</button>
21+
</div>
22+
<EquationSelector
23+
@change="
24+
markdown = $event;
25+
mobileMenuOpen = false;
26+
"
27+
/>
728
<footer class="sidebar-footer">
829
<p>
930
Demo by
@@ -43,10 +64,17 @@
4364
</main>
4465

4566
<!-- Editor Sidebar -->
46-
<aside class="editor-sidebar" :class="{ collapsed: editorCollapsed }">
67+
<aside
68+
class="editor-sidebar"
69+
:class="{ collapsed: editorCollapsed, 'mobile-open': mobileEditorOpen }"
70+
>
71+
<div class="mobile-close-header">
72+
<span class="edit-label">EDITOR</span>
73+
<button class="close-btn" @click="mobileEditorOpen = false">✕</button>
74+
</div>
4775
<div class="editor-toolbar">
4876
<button
49-
class="toolbar-btn toggle-btn"
77+
class="toolbar-btn toggle-btn desktop-only"
5078
title="Show/hide editor"
5179
@click="editorCollapsed = !editorCollapsed"
5280
>
@@ -101,6 +129,8 @@ import MarkdownEditor from "./components/MarkdownEditor.vue";
101129
const markdown = ref("");
102130
const colorScheme = ref<ColorScheme>(defaultScheme);
103131
const editorCollapsed = ref(false);
132+
const mobileMenuOpen = ref(false);
133+
const mobileEditorOpen = ref(false);
104134
105135
// Parsed content derived from markdown
106136
const parsedContent = computed(() => {
@@ -324,36 +354,131 @@ body {
324354
background-color: var(--bg-primary);
325355
}
326356
357+
/* Mobile Header */
358+
.mobile-header {
359+
display: none;
360+
height: 50px;
361+
background-color: var(--bg-secondary);
362+
border-bottom: 1px solid var(--border-color);
363+
padding: 0 1rem;
364+
align-items: center;
365+
justify-content: space-between;
366+
position: fixed;
367+
top: 0;
368+
left: 0;
369+
right: 0;
370+
z-index: 40;
371+
}
372+
373+
.mobile-menu-btn {
374+
background: transparent;
375+
border: 1px solid var(--border-color);
376+
padding: 0.5rem 1rem;
377+
border-radius: 4px;
378+
color: var(--text-primary);
379+
font-family: var(--font-ui);
380+
font-size: 0.875rem;
381+
cursor: pointer;
382+
display: flex;
383+
align-items: center;
384+
gap: 0.5rem;
385+
}
386+
387+
.mobile-close-header {
388+
display: none;
389+
padding: 1rem;
390+
border-bottom: 1px solid var(--border-color);
391+
align-items: center;
392+
justify-content: space-between;
393+
margin-bottom: 1rem;
394+
}
395+
396+
.close-btn {
397+
background: transparent;
398+
border: none;
399+
font-size: 1.25rem;
400+
color: var(--text-secondary);
401+
cursor: pointer;
402+
padding: 0.25rem;
403+
}
404+
327405
/* Responsive */
328406
@media (max-width: 768px) {
329407
#app {
330408
flex-direction: column;
331409
height: auto;
332410
overflow-y: auto;
411+
padding-top: 50px; /* Space for mobile header */
412+
}
413+
414+
.mobile-header {
415+
display: flex;
416+
}
417+
418+
.mobile-close-header {
419+
display: flex;
420+
}
421+
422+
.desktop-only {
423+
display: none;
333424
}
425+
334426
.sidebar {
427+
position: fixed;
428+
inset: 0;
429+
z-index: 100;
335430
width: 100%;
336-
min-width: 100%;
431+
max-width: 100%;
432+
height: 100%;
433+
max-height: 100vh;
434+
transform: translateX(-100%);
435+
transition: transform 0.3s ease;
337436
border-right: none;
338-
border-bottom: 1px solid var(--border-color);
339-
height: auto;
340-
max-height: 200px;
341437
}
438+
439+
.sidebar.mobile-open {
440+
transform: translateX(0);
441+
}
442+
443+
.sidebar h2 {
444+
display: none; /* Hidden in favor of mobile-close-header */
445+
}
446+
447+
.sidebar .mobile-close-header h2 {
448+
display: block;
449+
margin: 0;
450+
padding: 0;
451+
}
452+
342453
.main-content {
343454
padding: 2rem 1rem;
344455
overflow: visible;
345456
}
457+
346458
.editor-sidebar {
347-
position: relative;
459+
position: fixed;
460+
inset: 0;
461+
z-index: 100;
348462
width: 100%;
349463
max-width: 100%;
350-
height: 500px;
351-
transform: none;
464+
height: 100%;
465+
transform: translateY(100%);
466+
transition: transform 0.3s ease;
467+
border-left: none;
468+
}
469+
470+
.editor-sidebar.mobile-open {
471+
transform: translateY(0);
352472
}
473+
353474
.editor-sidebar.collapsed {
354-
height: 50px;
475+
height: 100%;
355476
width: 100%;
356477
min-width: 100%;
357478
}
479+
480+
.editor-sidebar .editor-toolbar {
481+
padding: 0 1rem; /* Adjust padding for mobile */
482+
}
358483
}
359484
</style>

src/components/CentralPanel.vue

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,61 +32,66 @@
3232
</template>
3333

3434
<script setup lang="ts">
35-
import { ref, computed, watch } from 'vue'
36-
import type { ParsedContent } from '../utils/parser'
37-
import type { ColorScheme } from '../export'
38-
import { buildTermColorMap } from '../utils/colorSchemes'
35+
import { ref, computed, watch } from "vue";
36+
import type { ParsedContent } from "../utils/parser";
37+
import type { ColorScheme } from "../export";
38+
import { buildTermColorMap } from "../utils/colorSchemes";
3939
40-
import EquationDisplay from './equation/EquationDisplay.vue'
41-
import DescriptionPanel from './equation/DescriptionPanel.vue'
42-
import DefinitionPopup from './equation/DefinitionPopup.vue'
40+
import EquationDisplay from "./equation/EquationDisplay.vue";
41+
import DescriptionPanel from "./equation/DescriptionPanel.vue";
42+
import DefinitionPopup from "./equation/DefinitionPopup.vue";
4343
4444
const props = defineProps<{
45-
content: ParsedContent
46-
colors: ColorScheme
47-
}>()
45+
content: ParsedContent;
46+
colors: ColorScheme;
47+
}>();
4848
49-
const termOrder = computed(() => props.content.termOrder)
49+
const termOrder = computed(() => props.content.termOrder);
5050
5151
// Build color map once (O(1) lookups instead of O(n) indexOf)
52-
const termColorMap = computed(() => buildTermColorMap(termOrder.value, props.colors))
52+
const termColorMap = computed(() =>
53+
buildTermColorMap(termOrder.value, props.colors)
54+
);
5355
5456
function getTermColor(term: string): string {
55-
return termColorMap.value.get(term) ?? '#000000'
57+
return termColorMap.value.get(term) ?? "#000000";
5658
}
5759
5860
// Hover/click state (owned internally)
59-
const hoveredTerm = ref<string | null>(null)
60-
const clickedTerm = ref<string | null>(null)
61-
const activeTerm = computed(() => clickedTerm.value ?? hoveredTerm.value)
61+
const hoveredTerm = ref<string | null>(null);
62+
const clickedTerm = ref<string | null>(null);
63+
const activeTerm = computed(() => clickedTerm.value ?? hoveredTerm.value);
6264
6365
const activeDefinition = computed(() => {
64-
if (!activeTerm.value) return null
65-
return props.content.definitions.get(activeTerm.value) ?? null
66-
})
66+
if (!activeTerm.value) return null;
67+
return props.content.definitions.get(activeTerm.value) ?? null;
68+
});
6769
6870
const activeColor = computed(() =>
6971
activeTerm.value ? getTermColor(activeTerm.value) : null
70-
)
72+
);
7173
7274
function setHover(term: string | null) {
73-
hoveredTerm.value = term
75+
hoveredTerm.value = term;
7476
}
7577
7678
function handleTermClick(term: string) {
77-
if (term === '') {
78-
clickedTerm.value = null
79+
if (term === "") {
80+
clickedTerm.value = null;
7981
} else if (clickedTerm.value === term) {
80-
clickedTerm.value = null
82+
clickedTerm.value = null;
8183
} else {
82-
clickedTerm.value = term
84+
clickedTerm.value = term;
8385
}
8486
}
8587
8688
// Clear click when content changes
87-
watch(() => props.content, () => {
88-
clickedTerm.value = null
89-
})
89+
watch(
90+
() => props.content,
91+
() => {
92+
clickedTerm.value = null;
93+
}
94+
);
9095
</script>
9196

9297
<style scoped>
@@ -115,4 +120,15 @@ h1 {
115120
text-align: center;
116121
opacity: 0.8;
117122
}
123+
124+
@media (max-width: 768px) {
125+
h1 {
126+
font-size: 1.75rem;
127+
}
128+
129+
.subtitle {
130+
font-size: 0.875rem;
131+
margin-bottom: 1.5rem;
132+
}
133+
}
118134
</style>

0 commit comments

Comments
 (0)