🎨 Refactor components to use modular structure and computed properties for improved maintainability and reusability in Gamemaster Explorer and FilterPanel
This commit is contained in:
@@ -64,9 +64,16 @@ const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
filterState: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const internalFilterState = useJsonFilter();
|
||||
const activeFilterState = computed(() => props.filterState || internalFilterState);
|
||||
|
||||
const {
|
||||
filterProperty,
|
||||
filterValue,
|
||||
@@ -79,7 +86,7 @@ const {
|
||||
setFilter,
|
||||
clearFilters,
|
||||
getUniqueValues
|
||||
} = useJsonFilter();
|
||||
} = activeFilterState.value;
|
||||
|
||||
const hasData = computed(
|
||||
() => Array.isArray(props.data) && props.data.length > 0
|
||||
|
||||
@@ -90,120 +90,17 @@
|
||||
</div>
|
||||
|
||||
<!-- File Selector -->
|
||||
<div class="file-selector">
|
||||
<label for="file-select">Select File:</label>
|
||||
<select id="file-select" v-model="selectedFile" @change="onFileChange">
|
||||
<option value="">-- Choose a file --</option>
|
||||
<option
|
||||
v-for="file in uniqueFiles"
|
||||
:key="file.filename"
|
||||
:value="getFileType(file.filename)"
|
||||
>
|
||||
{{ formatFileName(file.filename) }} ({{ formatSize(file.size) }})
|
||||
</option>
|
||||
</select>
|
||||
<span v-if="fileContent" class="file-info"
|
||||
>{{ fileLines.length.toLocaleString() }} lines</span
|
||||
>
|
||||
</div>
|
||||
<FileSelector :files-state="filesState" />
|
||||
|
||||
<!-- Search Bar -->
|
||||
<div v-if="fileContent" class="search-bar">
|
||||
<div class="search-input-wrapper">
|
||||
<input
|
||||
ref="searchInput"
|
||||
type="text"
|
||||
v-model="searchQuery"
|
||||
@input="onSearchInput"
|
||||
placeholder="Search in file... (Ctrl+F)"
|
||||
class="search-input"
|
||||
/>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
@click="clearSearch"
|
||||
class="btn-clear"
|
||||
title="Clear search"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="searchResults.length > 0" class="search-results">
|
||||
<span :title="`Line ${searchResults[currentResultIndex] + 1}`">
|
||||
{{ currentResultIndex + 1 }} / {{ searchResults.length }} (Line
|
||||
{{ searchResults[currentResultIndex] + 1 }})
|
||||
</span>
|
||||
<button
|
||||
@click="goToPrevResult"
|
||||
class="btn-nav"
|
||||
:disabled="searchResults.length === 0"
|
||||
title="Previous result (Shift+Ctrl+G)"
|
||||
>
|
||||
↑
|
||||
</button>
|
||||
<button
|
||||
@click="goToNextResult"
|
||||
class="btn-nav"
|
||||
:disabled="searchResults.length === 0"
|
||||
title="Next result (Ctrl+G)"
|
||||
>
|
||||
↓
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="searchHistory.history.value.length > 0"
|
||||
class="search-history"
|
||||
>
|
||||
<button
|
||||
v-for="(item, index) in searchHistory.history.value"
|
||||
:key="index"
|
||||
@click="applyHistoryItem(item)"
|
||||
class="history-item"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<SearchBar
|
||||
v-if="fileContent"
|
||||
:file-lines="fileLines"
|
||||
:display-lines="displayLines"
|
||||
:search-state="searchState"
|
||||
/>
|
||||
|
||||
<!-- Property Filter -->
|
||||
<div v-if="fileContent && jsonPaths.length > 0" class="property-filter">
|
||||
<label for="property-select">Filter by Property:</label>
|
||||
<select
|
||||
id="property-select"
|
||||
v-model="filterProperty"
|
||||
@change="onFilterChange"
|
||||
>
|
||||
<option value="">All Properties</option>
|
||||
<option v-for="path in jsonPaths" :key="path" :value="path">
|
||||
{{ path }}
|
||||
</option>
|
||||
</select>
|
||||
<input
|
||||
v-if="filterProperty"
|
||||
type="text"
|
||||
v-model="filterValue"
|
||||
@input="onFilterChange"
|
||||
placeholder="Enter value..."
|
||||
class="filter-value-input"
|
||||
/>
|
||||
<label v-if="filterProperty" class="filter-mode">
|
||||
<input
|
||||
type="radio"
|
||||
value="OR"
|
||||
v-model="filterMode"
|
||||
@change="onFilterChange"
|
||||
/>
|
||||
OR
|
||||
<input
|
||||
type="radio"
|
||||
value="AND"
|
||||
v-model="filterMode"
|
||||
@change="onFilterChange"
|
||||
/>
|
||||
AND
|
||||
</label>
|
||||
</div>
|
||||
<FilterPanel v-if="fileContent" :data="filterData" :filter-state="filterState" />
|
||||
|
||||
<!-- Progress Bar (for long operations) -->
|
||||
<div v-if="operationProgress.active" class="progress-bar-container">
|
||||
@@ -220,100 +117,28 @@
|
||||
</div>
|
||||
|
||||
<!-- JSON Content Viewer -->
|
||||
<div
|
||||
<JsonViewer
|
||||
v-if="fileContent"
|
||||
class="content-viewer"
|
||||
:class="{
|
||||
'dark-mode': preferences.darkMode,
|
||||
'line-wrap': preferences.lineWrap
|
||||
}"
|
||||
>
|
||||
<!-- Virtual Scroller for large files -->
|
||||
<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':
|
||||
item.lineNumber === searchResults[currentResultIndex] + 1
|
||||
}
|
||||
]"
|
||||
@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>
|
||||
|
||||
<!-- Regular render for smaller files -->
|
||||
<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':
|
||||
line.lineNumber === searchResults[currentResultIndex] + 1
|
||||
}
|
||||
]"
|
||||
@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>
|
||||
|
||||
<!-- Too Large Warning -->
|
||||
<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>
|
||||
:display-lines="displayLines"
|
||||
:file-content="fileContent"
|
||||
:selected-file="selectedFile"
|
||||
:file-too-large="fileTooLarge"
|
||||
:preferences="preferences"
|
||||
:search-results="searchResults"
|
||||
:current-result-index="currentResultIndex"
|
||||
:highlight-config="highlightConfig"
|
||||
:line-height="lineHeight"
|
||||
:selection-state="selectionState"
|
||||
/>
|
||||
|
||||
<!-- Action Bar -->
|
||||
<div v-if="fileContent" class="action-bar">
|
||||
<button
|
||||
@click="copySelected"
|
||||
:disabled="selectedLines.size === 0"
|
||||
class="btn-action"
|
||||
>
|
||||
📋 Copy Selected ({{ selectedLines.size }} lines)
|
||||
</button>
|
||||
<button @click="copyAll" class="btn-action">📋 Copy All</button>
|
||||
<button
|
||||
@click="exportSelected"
|
||||
:disabled="selectedLines.size === 0"
|
||||
class="btn-action"
|
||||
>
|
||||
💾 Export Selected
|
||||
</button>
|
||||
<button @click="exportAll" class="btn-action">💾 Export All</button>
|
||||
<button @click="shareUrl" class="btn-action">🔗 Share Link</button>
|
||||
</div>
|
||||
<ActionToolbar
|
||||
v-if="fileContent"
|
||||
:display-lines="displayLines"
|
||||
:file-content="fileContent"
|
||||
:selected-file="selectedFile"
|
||||
:selection-state="selectionState"
|
||||
/>
|
||||
|
||||
<!-- Toast Notifications -->
|
||||
<div v-if="clipboard.copied.value" class="toast success">
|
||||
|
||||
Reference in New Issue
Block a user