Files
memory-infrastructure-palace/code/websites/pokedex.online/src/components/gamemaster/JsonViewer.vue

202 lines
4.2 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div
v-if="fileContent"
class="content-viewer"
:class="{
'dark-mode': preferences.darkMode,
'line-wrap': preferences.lineWrap
}"
>
<RecycleScroller
v-if="displayLines.length > 1000"
ref="virtualScroller"
class="scroller"
:items="displayLines"
:item-size="lineHeight"
key-field="lineNumber"
>
<template #default="{ item }">
<div
:data-line="item.lineNumber"
:class="[
'line',
{ selected: selectedLines.has(item.lineNumber) },
{ 'highlight-match': item.hasMatch },
{ 'current-result': isCurrentResult(item.lineNumber) }
]"
@click="toggleLineSelection(item.lineNumber, $event)"
>
<span v-if="preferences.showLineNumbers" class="line-number">
{{ item.lineNumber }}
</span>
<pre
v-highlight="highlightConfig"
><code>{{ item.content }}</code></pre>
</div>
</template>
</RecycleScroller>
<div v-else class="lines-container">
<div
v-for="line in displayLines"
:key="line.lineNumber"
:data-line="line.lineNumber"
:class="[
'line',
{ selected: selectedLines.has(line.lineNumber) },
{ 'highlight-match': line.hasMatch },
{ 'current-result': isCurrentResult(line.lineNumber) }
]"
@click="toggleLineSelection(line.lineNumber, $event)"
>
<span v-if="preferences.showLineNumbers" class="line-number">
{{ line.lineNumber }}
</span>
<pre v-highlight="highlightConfig"><code>{{ line.content }}</code></pre>
</div>
</div>
<div v-if="fileTooLarge" class="warning-banner">
File exceeds 10,000 lines. Showing first 10,000 lines only. Use search
or filters to find specific content.
</div>
</div>
</template>
<script setup>
import { computed, toRef, ref } from 'vue';
import { RecycleScroller } from 'vue-virtual-scroller';
import { useLineSelection } from '../../composables/useLineSelection.js';
const props = defineProps({
displayLines: {
type: Array,
default: () => []
},
fileContent: {
type: String,
default: ''
},
selectedFile: {
type: String,
default: ''
},
fileTooLarge: {
type: Boolean,
default: false
},
preferences: {
type: Object,
default: () => ({
darkMode: false,
lineWrap: false,
showLineNumbers: true
})
},
searchResults: {
type: Array,
default: () => []
},
currentResultIndex: {
type: Number,
default: 0
},
highlightConfig: {
type: Object,
default: () => ({
theme: 'github',
language: 'json'
})
},
lineHeight: {
type: Number,
default: 20
}
});
const displayLines = toRef(props, 'displayLines');
const fileContent = toRef(props, 'fileContent');
const selectedFile = toRef(props, 'selectedFile');
const virtualScroller = ref(null);
const { selectedLines, toggleLineSelection } = useLineSelection(
displayLines,
fileContent,
selectedFile
);
const isCurrentResult = lineNumber => {
if (!props.searchResults.length) return false;
const currentLine = props.searchResults[props.currentResultIndex] + 1;
return lineNumber === currentLine;
};
defineExpose({
selectedLines,
toggleLineSelection,
virtualScroller
});
</script>
<style scoped>
.content-viewer {
border: 1px solid #ddd;
border-radius: 8px;
background: #fafafa;
max-height: 70vh;
overflow: hidden;
}
.content-viewer.dark-mode {
background: #1e1e1e;
border-color: #333;
color: #eee;
}
.content-viewer.line-wrap pre {
white-space: pre-wrap;
}
.scroller,
.lines-container {
max-height: 65vh;
overflow-y: auto;
}
.line {
display: flex;
gap: 10px;
padding: 2px 8px;
font-size: 13px;
cursor: pointer;
}
.line.selected {
background: rgba(0, 123, 255, 0.15);
}
.line.highlight-match {
background: rgba(255, 234, 0, 0.2);
}
.line.current-result {
outline: 1px solid #ff9800;
}
.line-number {
width: 50px;
text-align: right;
color: #999;
user-select: none;
}
.warning-banner {
padding: 8px 10px;
font-size: 13px;
background: #fff3cd;
border-top: 1px solid #ffeeba;
color: #856404;
}
</style>