207 lines
3.8 KiB
Vue
207 lines
3.8 KiB
Vue
<template>
|
|
<div class="search-bar">
|
|
<div class="search-input-wrapper">
|
|
<input
|
|
ref="searchInput"
|
|
type="text"
|
|
v-model="searchQuery"
|
|
placeholder="Search in file... (Ctrl+F)"
|
|
class="search-input"
|
|
:disabled="isDisabled"
|
|
/>
|
|
<button
|
|
v-if="searchQuery"
|
|
@click="clearSearch"
|
|
class="btn-clear"
|
|
title="Clear search"
|
|
:disabled="isDisabled"
|
|
>
|
|
✕
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="hasSearchResults" class="search-results">
|
|
<span :title="currentResultLineTitle">
|
|
{{ resultCountDisplay }}
|
|
<template v-if="currentResultLineNumber">
|
|
(Line {{ currentResultLineNumber }})
|
|
</template>
|
|
</span>
|
|
<button
|
|
@click="goToPrevResult"
|
|
class="btn-nav"
|
|
:disabled="!hasSearchResults"
|
|
title="Previous result (Shift+Ctrl+G)"
|
|
>
|
|
↑
|
|
</button>
|
|
<button
|
|
@click="goToNextResult"
|
|
class="btn-nav"
|
|
:disabled="!hasSearchResults"
|
|
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 v-if="searchError" class="search-error">
|
|
{{ searchError }}
|
|
</div>
|
|
|
|
<div v-if="isSearching" class="search-status">Searching...</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, toRef } from 'vue';
|
|
import { useGamemasterSearch } from '../../composables/useGamemasterSearch.js';
|
|
|
|
const props = defineProps({
|
|
fileLines: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
displayLines: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
const fileLines = toRef(props, 'fileLines');
|
|
const displayLines = toRef(props, 'displayLines');
|
|
|
|
const {
|
|
searchQuery,
|
|
searchResults,
|
|
currentResultIndex,
|
|
isSearching,
|
|
searchError,
|
|
searchHistory,
|
|
clearSearch,
|
|
goToNextResult,
|
|
goToPrevResult,
|
|
applyHistoryItem,
|
|
currentResultLineNumber,
|
|
resultCountDisplay,
|
|
hasSearchResults
|
|
} = useGamemasterSearch(fileLines, displayLines);
|
|
|
|
const isDisabled = computed(
|
|
() => props.disabled || fileLines.value.length === 0
|
|
);
|
|
|
|
const currentResultLineTitle = computed(() => {
|
|
if (!currentResultLineNumber.value) return '';
|
|
return `Line ${currentResultLineNumber.value}`;
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.search-bar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
margin: 15px 0;
|
|
}
|
|
|
|
.search-input-wrapper {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.search-input {
|
|
width: 100%;
|
|
padding: 8px 35px 8px 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
background: white;
|
|
}
|
|
|
|
.search-input:disabled {
|
|
background: #f5f5f5;
|
|
color: #999;
|
|
}
|
|
|
|
.btn-clear {
|
|
position: absolute;
|
|
right: 8px;
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
color: #666;
|
|
padding: 4px;
|
|
}
|
|
|
|
.btn-clear:disabled {
|
|
cursor: not-allowed;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.search-results {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
|
|
.btn-nav {
|
|
background: #f5f5f5;
|
|
border: 1px solid #ddd;
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.btn-nav:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.search-history {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
}
|
|
|
|
.history-item {
|
|
background: #f0f0f0;
|
|
border: 1px solid #ddd;
|
|
border-radius: 12px;
|
|
padding: 4px 10px;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
color: #555;
|
|
}
|
|
|
|
.search-error {
|
|
color: #d9534f;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.search-status {
|
|
color: #666;
|
|
font-size: 13px;
|
|
}
|
|
</style>
|