156 lines
4.6 KiB
Vue
156 lines
4.6 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
import { Eye, Edit2, Columns, Smartphone } from 'lucide-vue-next'
|
|
import WorkoutVisualEditor from './WorkoutVisualEditor.vue'
|
|
import WorkoutJsonEditor from './WorkoutJsonEditor.vue'
|
|
|
|
const props = defineProps({
|
|
original: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
modelValue: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
editorTab: {
|
|
type: String,
|
|
default: 'visual'
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
// Tab state for mobile or constrained views
|
|
const activeTab = ref('modified') // 'original' | 'modified' | 'split'
|
|
|
|
// Helper to determine if we can show split view (simple width check or just controlled by user preference)
|
|
const canSplit = ref(true)
|
|
|
|
const setTab = (tab) => {
|
|
activeTab.value = tab
|
|
}
|
|
|
|
const updateTabBasedOnWidth = () => {
|
|
if (window.innerWidth >= 768) {
|
|
if (activeTab.value !== 'split') activeTab.value = 'split'
|
|
}
|
|
}
|
|
|
|
import { onMounted, onUnmounted } from 'vue'
|
|
|
|
onMounted(() => {
|
|
updateTabBasedOnWidth()
|
|
window.addEventListener('resize', updateTabBasedOnWidth)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('resize', updateTabBasedOnWidth)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="compare-view h-full flex flex-col">
|
|
<!-- View Controls (only visible on small screens or if needed) -->
|
|
<div class="mobile-tabs md:hidden flex bg-gray-900 border-b border-gray-800 p-1">
|
|
<button
|
|
class="flex-1 py-2 text-sm font-medium rounded-md transition-colors flex items-center justify-center gap-2"
|
|
:class="
|
|
activeTab === 'original'
|
|
? 'bg-gray-800 text-white shadow'
|
|
: 'text-gray-400 hover:text-white'
|
|
"
|
|
@click="setTab('original')"
|
|
>
|
|
<Eye class="w-4 h-4" /> Original
|
|
</button>
|
|
<button
|
|
class="flex-1 py-2 text-sm font-medium rounded-md transition-colors flex items-center justify-center gap-2"
|
|
:class="
|
|
activeTab === 'modified'
|
|
? 'bg-blue-900/40 text-blue-100 shadow'
|
|
: 'text-gray-400 hover:text-white'
|
|
"
|
|
@click="setTab('modified')"
|
|
>
|
|
<Edit2 class="w-4 h-4" /> Modified
|
|
</button>
|
|
</div>
|
|
|
|
<!-- MAIN CONTENT AREA -->
|
|
<div class="compare-container flex-1 overflow-hidden relative">
|
|
<!-- LEFT PANEL: ORIGINAL (READONLY) -->
|
|
<div
|
|
class="panel original-panel border-r border-gray-800 bg-gray-900/50 flex-1 min-w-0"
|
|
:class="{ hidden: activeTab === 'modified' }"
|
|
>
|
|
<div
|
|
class="panel-header text-xs font-bold text-gray-500 uppercase tracking-wider p-3 border-b border-gray-800 flex justify-between"
|
|
>
|
|
<span>Original Version</span>
|
|
<span class="bg-gray-800 px-2 py-0.5 rounded text-gray-400">Read-only</span>
|
|
</div>
|
|
<div class="panel-content overflow-y-auto h-full p-4 relative">
|
|
<!-- Blocking Overlay for Interaction -->
|
|
<div class="absolute inset-0 z-10 cursor-not-allowed"></div>
|
|
|
|
<WorkoutVisualEditor
|
|
v-if="editorTab === 'visual'"
|
|
:modelValue="original"
|
|
:steps="original.workoutSegments[0].workoutSteps"
|
|
readonly
|
|
/>
|
|
<WorkoutJsonEditor v-else :modelValue="original" readonly />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RIGHT PANEL: MODIFIED (EDITABLE) -->
|
|
<div
|
|
class="panel modified-panel flex-1 min-w-0"
|
|
:class="{ hidden: activeTab === 'original' }"
|
|
>
|
|
<div
|
|
class="panel-header text-xs font-bold text-blue-400 uppercase tracking-wider p-3 border-b border-gray-800 bg-blue-900/10 flex justify-between"
|
|
>
|
|
<span>Working Copy</span>
|
|
<span class="bg-blue-900/30 px-2 py-0.5 rounded text-blue-200">Editable</span>
|
|
</div>
|
|
<div class="panel-content overflow-y-auto h-full p-4">
|
|
<WorkoutVisualEditor
|
|
v-if="editorTab === 'visual'"
|
|
:modelValue="modelValue"
|
|
:steps="modelValue.workoutSegments[0].workoutSteps"
|
|
@update:modelValue="(val) => emit('update:modelValue', val)"
|
|
/>
|
|
<WorkoutJsonEditor
|
|
v-else
|
|
:modelValue="modelValue"
|
|
@update:modelValue="(val) => emit('update:modelValue', val)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.compare-container {
|
|
display: flex !important; /* Force flex to override any potential blocking */
|
|
flex-direction: row;
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
.panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Ensure Desktop defaults to split */
|
|
/* Ensure Desktop defaults to split */
|
|
@media (min-width: 768px) {
|
|
.md\:hidden {
|
|
display: none !important;
|
|
}
|
|
}
|
|
</style>
|