26 changed files with 578 additions and 61 deletions
@ -0,0 +1,61 @@ |
|||||||
|
@using System.Runtime.InteropServices |
||||||
|
@inject ISearchService searchService |
||||||
|
@inject NavigationManager navigationManager |
||||||
|
@inject IJSRuntime jsRuntime |
||||||
|
|
||||||
|
<button class="searchButtonContainer" @onclick="ButtonClicked"> |
||||||
|
<div class="searchText"> |
||||||
|
Search... |
||||||
|
</div> |
||||||
|
@if (false) |
||||||
|
{ |
||||||
|
<div class="searchHotkey"> |
||||||
|
@CommandKey + K |
||||||
|
</div> |
||||||
|
|
||||||
|
} |
||||||
|
</button> |
||||||
|
|
||||||
|
<style> |
||||||
|
.searchButtonContainer { |
||||||
|
background-color: var(--primary); |
||||||
|
border: 2px solid var(--primary-border); |
||||||
|
border-radius: 8px; |
||||||
|
font-weight: 800; |
||||||
|
width: 350px; |
||||||
|
|
||||||
|
padding: 5px; |
||||||
|
display: flex; |
||||||
|
flex-direction: row; |
||||||
|
justify-content: space-between; |
||||||
|
align-items: center; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.searchHotkey { |
||||||
|
padding: 2px; |
||||||
|
|
||||||
|
border: 2px solid var(--primary-border); |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
|
||||||
|
@code { |
||||||
|
[Parameter] |
||||||
|
public RenderFragment ChildContent { get; set; } = default!; |
||||||
|
|
||||||
|
private string userAgent = ""; |
||||||
|
|
||||||
|
string CommandKey => userAgent.Contains("Mac OS") ? "CMD" : "Ctrl"; |
||||||
|
|
||||||
|
private void ButtonClicked(EventArgs eventArgs) |
||||||
|
{ |
||||||
|
searchService.Show(); |
||||||
|
} |
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync() |
||||||
|
{ |
||||||
|
userAgent = await jsRuntime.InvokeAsync<string>("getUserAgent"); |
||||||
|
} |
||||||
|
} |
||||||
Binary file not shown.
@ -0,0 +1,172 @@ |
|||||||
|
@implements IDisposable; |
||||||
|
@inject ISearchService searchService |
||||||
|
@inject IJSRuntime jsRuntime |
||||||
|
|
||||||
|
|
||||||
|
@inject NavigationManager navigationManager |
||||||
|
|
||||||
|
@if (searchService.IsLoaded()) |
||||||
|
{ |
||||||
|
<div class="searchBackground" onclick="@CloseDialog"> |
||||||
|
<div class="searchContainer" |
||||||
|
@onclick:preventDefault="true" |
||||||
|
@onclick:stopPropagation="true"> |
||||||
|
|
||||||
|
<FormLayoutComponent> |
||||||
|
<FormTextComponent Placeholder="Search..." OnChange="SearchChanged"></FormTextComponent> |
||||||
|
</FormLayoutComponent> |
||||||
|
|
||||||
|
<div class="searchBox"> |
||||||
|
@if (SearchText.Length > 0) |
||||||
|
{ |
||||||
|
foreach (var searchSection in searchService.Searches) |
||||||
|
{ |
||||||
|
var searchPoints = searchSection.Value.FindAll(x => x.Title.ToLower().Contains(SearchText.ToLower())); |
||||||
|
|
||||||
|
@if (searchPoints.Count > 0) |
||||||
|
{ |
||||||
|
<div> |
||||||
|
<div class="searchSectionTitle"> |
||||||
|
@searchSection.Key |
||||||
|
</div> |
||||||
|
<div class="searchContents"> |
||||||
|
@foreach (var searchPoint in searchPoints) |
||||||
|
{ |
||||||
|
<button class="searchLink @searchPoint.PointType.ToLower()" @onclick="() => OnSearch(searchPoint)">@searchPoint.Title</button> |
||||||
|
} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
} |
||||||
|
<style> |
||||||
|
.pageContents * { |
||||||
|
filter: blur(2px); |
||||||
|
} |
||||||
|
|
||||||
|
.searchBackground { |
||||||
|
position: fixed; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||||
|
display: flex; |
||||||
|
} |
||||||
|
|
||||||
|
.searchBox { |
||||||
|
padding: 12px; |
||||||
|
overflow-y: scroll; |
||||||
|
overflow-x: hidden; |
||||||
|
height: 530px; |
||||||
|
border: 1px solid black; |
||||||
|
border-radius: 2px; |
||||||
|
} |
||||||
|
|
||||||
|
.searchContents { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
gap: 6px; |
||||||
|
align-items: flex-start; |
||||||
|
|
||||||
|
padding: 12px; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.searchSectionTitle { |
||||||
|
font-weight: bolder; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.searchContainer { |
||||||
|
margin-left: auto; |
||||||
|
margin-right: auto; |
||||||
|
margin-top: 64px; |
||||||
|
width: 800px; |
||||||
|
height: 600px; |
||||||
|
|
||||||
|
background-color: var(--background); |
||||||
|
border-width: var(--dialog-border-width); |
||||||
|
border-style: solid; |
||||||
|
border-color: var(--dialog-border-color); |
||||||
|
border-radius: var(--dialog-radius); |
||||||
|
|
||||||
|
padding: 8px; |
||||||
|
|
||||||
|
|
||||||
|
box-shadow: 1px 2px 2px black; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.searchLink { |
||||||
|
text-decoration: underline; |
||||||
|
|
||||||
|
} |
||||||
|
</style> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
private ElementReference searchBox; |
||||||
|
|
||||||
|
private string SearchText { get; set; } = ""; |
||||||
|
|
||||||
|
protected override void OnInitialized() |
||||||
|
{ |
||||||
|
searchService.Subscribe(OnSearchChanged); |
||||||
|
} |
||||||
|
|
||||||
|
private void OnSearchChanged() |
||||||
|
{ |
||||||
|
if (searchService.IsVisible) |
||||||
|
{ |
||||||
|
jsRuntime.InvokeVoidAsync("SetFocusToElement", "search-dialog-input"); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
searchService.Unsubscribe(OnSearchChanged); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void CloseDialog() |
||||||
|
{ |
||||||
|
searchService.Hide(); |
||||||
|
} |
||||||
|
|
||||||
|
public void NavigateTo(string url) |
||||||
|
{ |
||||||
|
if (url.Contains("#")) |
||||||
|
{ |
||||||
|
|
||||||
|
navigationManager.NavigateTo(url, |
||||||
|
navigationManager.Uri.Split("#").First().Contains(url.Split("#").First())); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
navigationManager.NavigateTo(url); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void SearchChanged(ChangeEventArgs obj) |
||||||
|
{ |
||||||
|
SearchText = obj.Value!.ToString()!; |
||||||
|
} |
||||||
|
|
||||||
|
private void OnSearch(SearchPointModel searchPoint) |
||||||
|
{ |
||||||
|
NavigateTo(searchPoint.Href); |
||||||
|
searchService.Hide(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
@implements IDisposable; |
||||||
|
|
||||||
|
@inject ISearchService searchService |
||||||
|
|
||||||
|
@if (searchService.IsVisible) |
||||||
|
{ |
||||||
|
<SearchDialogComponent></SearchDialogComponent> |
||||||
|
} |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
protected override void OnInitialized() |
||||||
|
{ |
||||||
|
searchService.Subscribe(OnUpdate); |
||||||
|
|
||||||
|
searchService.Load(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
searchService.Unsubscribe(OnUpdate); |
||||||
|
} |
||||||
|
|
||||||
|
void OnUpdate() |
||||||
|
{ |
||||||
|
StateHasChanged(); |
||||||
|
} |
||||||
|
} |
||||||
File diff suppressed because one or more lines are too long
@ -1 +1 @@ |
|||||||
[{"Id":1,"ParentId":null,"NoteSectionModelId":3,"Href":"holdout","CreatedDate":"2022-02-18T00:00:00","UpdatedDate":"2022-02-18T00:00:00","Name":"Coop Holdout, Some distant place (Nuath)","Description":"First coop test map in pre-alpha.","Content":"coop/holdout","IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":2,"ParentId":null,"NoteSectionModelId":2,"Href":"hotkeys","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Custom HotKey Setup","Description":"Customize your hotkeys in the pre-alpha.","Content":"settings/hotkeys","IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":3,"ParentId":null,"NoteSectionModelId":1,"Href":"armor-types","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Armor Types","Description":"Heavy, Medium, and Light. What does it mean?","Content":"the-basics/armor-types","IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0}] |
[{"Id":1,"ParentId":null,"NoteSectionModelId":3,"Href":"holdout","CreatedDate":"2022-02-18T00:00:00","UpdatedDate":"2022-02-18T00:00:00","Name":"Coop Holdout, Some distant place (Nuath)","Description":"First coop test map in pre-alpha.","Content":"coop/holdout","LoadedContent":null,"IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":2,"ParentId":null,"NoteSectionModelId":2,"Href":"hotkeys","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Custom HotKey Setup","Description":"Customize your hotkeys in the pre-alpha.","Content":"settings/hotkeys","LoadedContent":null,"IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":3,"ParentId":null,"NoteSectionModelId":1,"Href":"armor-types","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Armor Types","Description":"Heavy, Medium, and Light. What does it mean?","Content":"the-basics/armor-types","LoadedContent":null,"IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0}] |
||||||
@ -1 +1 @@ |
|||||||
[{"Id":1,"Name":"Tools","Description":"Tools Stuff","Href":null,"Order":1,"IsPrivate":"False","WebPageModels":[]},{"Id":2,"Name":"Resources","Description":"Resources Stuff","Href":null,"Order":2,"IsPrivate":"False","WebPageModels":[]},{"Id":3,"Name":"General","Description":"About Stuff","Href":null,"Order":3,"IsPrivate":"False","WebPageModels":[]},{"Id":4,"Name":"Development","Description":"Development Stuff","Href":null,"Order":4,"IsPrivate":"False","WebPageModels":[]},{"Id":5,"Name":"Settings","Description":"Settings Stuff","Href":null,"Order":5,"IsPrivate":"False","WebPageModels":[]}] |
[{"Id":1,"Name":"Tools","Description":"Tools Stuff","Order":1,"IsPrivate":"False","WebPageModels":[]},{"Id":2,"Name":"Resources","Description":"Resources Stuff","Order":2,"IsPrivate":"False","WebPageModels":[]},{"Id":3,"Name":"General","Description":"About Stuff","Order":3,"IsPrivate":"False","WebPageModels":[]},{"Id":4,"Name":"Development","Description":"Development Stuff","Order":4,"IsPrivate":"False","WebPageModels":[]},{"Id":5,"Name":"Settings","Description":"Settings Stuff","Order":5,"IsPrivate":"False","WebPageModels":[]}] |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
namespace Model.Website; |
||||||
|
|
||||||
|
public class SearchPointModel |
||||||
|
{ |
||||||
|
public string Title { get; set; } = ""; |
||||||
|
public string Tags { get; set; } = ""; |
||||||
|
public string PointType { get; set; } = ""; |
||||||
|
public string Href { get; set; } = ""; |
||||||
|
} |
||||||
@ -0,0 +1,131 @@ |
|||||||
|
using Model.Entity.Data; |
||||||
|
using Model.Feedback; |
||||||
|
using Model.Website; |
||||||
|
|
||||||
|
namespace Services.Website; |
||||||
|
|
||||||
|
public class SearchService : ISearchService |
||||||
|
{ |
||||||
|
|
||||||
|
private bool isLoaded = false; |
||||||
|
public List<SearchPointModel> SearchPoints { get; set; } = new(); |
||||||
|
|
||||||
|
public Dictionary<string, List<SearchPointModel>> Searches { get; set; } = new(); |
||||||
|
|
||||||
|
|
||||||
|
private IWebsiteService websiteService; |
||||||
|
private INoteService noteService; |
||||||
|
private IDocumentationService documentationService; |
||||||
|
|
||||||
|
public SearchService(IWebsiteService websiteService, INoteService noteService, IDocumentationService documentationService) |
||||||
|
{ |
||||||
|
this.websiteService = websiteService; |
||||||
|
this.noteService = noteService; |
||||||
|
this.documentationService = documentationService; |
||||||
|
|
||||||
|
// All Unit Data |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsVisible { get; set; } |
||||||
|
|
||||||
|
public void Subscribe(Action action) |
||||||
|
{ |
||||||
|
OnChange += action; |
||||||
|
} |
||||||
|
|
||||||
|
public void Unsubscribe(Action action) |
||||||
|
{ |
||||||
|
OnChange += action; |
||||||
|
} |
||||||
|
|
||||||
|
public void Search(string entityId) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public async Task Load() |
||||||
|
{ |
||||||
|
await websiteService.Load(); |
||||||
|
await noteService.Load(); |
||||||
|
await documentationService.Load(); |
||||||
|
|
||||||
|
|
||||||
|
Searches.Add("Pages", new List<SearchPointModel>()); |
||||||
|
Searches.Add("Notes", new List<SearchPointModel>()); |
||||||
|
Searches.Add("Documents", new List<SearchPointModel>()); |
||||||
|
Searches.Add("Entities", new List<SearchPointModel>()); |
||||||
|
|
||||||
|
foreach (var webPage in websiteService.WebPageModels) |
||||||
|
{ |
||||||
|
SearchPoints.Add(new SearchPointModel{ Title = webPage.Name, |
||||||
|
PointType = "WebPage", |
||||||
|
Href=webPage.Href |
||||||
|
}); |
||||||
|
|
||||||
|
Searches["Pages"].Add(SearchPoints.Last()); |
||||||
|
} |
||||||
|
|
||||||
|
foreach (var note in noteService.NoteContentModels) |
||||||
|
{ |
||||||
|
SearchPoints.Add(new SearchPointModel(){ Title = note.Name, |
||||||
|
PointType = "Note", Href = note.GetNoteLink()}); |
||||||
|
|
||||||
|
Searches["Notes"].Add(SearchPoints.Last()); |
||||||
|
|
||||||
|
foreach (var noteHeader in note.GetHeaders()) |
||||||
|
{ |
||||||
|
SearchPoints.Add(new SearchPointModel { Title = noteHeader.Title, |
||||||
|
PointType = "NoteHeader", Href = $"{note.GetNoteLink()}/#{noteHeader.Href}"}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
foreach (var entity in DATA.Get().Values) |
||||||
|
{ |
||||||
|
SearchPoints.Add(new SearchPointModel(){ |
||||||
|
Title = entity.Info().Name, |
||||||
|
Tags = $"{entity.EntityType},{entity.Descriptive}", |
||||||
|
PointType = "Entity", |
||||||
|
Href = $"database/{entity.Info().Name.ToLower()}" |
||||||
|
}); |
||||||
|
|
||||||
|
Searches["Entities"].Add(SearchPoints.Last()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
foreach (var doc in documentationService.DocContentModels) |
||||||
|
{ |
||||||
|
SearchPoints.Add(new SearchPointModel { Title = doc.Name, |
||||||
|
PointType = "Document", Href = doc.GetDocLink()}); |
||||||
|
|
||||||
|
Searches["Documents"].Add(SearchPoints.Last()); |
||||||
|
} |
||||||
|
|
||||||
|
isLoaded = true; |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsLoaded() |
||||||
|
{ |
||||||
|
return isLoaded; |
||||||
|
} |
||||||
|
|
||||||
|
public void Show() |
||||||
|
{ |
||||||
|
IsVisible = true; |
||||||
|
|
||||||
|
NotifyDataChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
public void Hide() |
||||||
|
{ |
||||||
|
IsVisible = false; |
||||||
|
|
||||||
|
NotifyDataChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
private event Action OnChange = null!; |
||||||
|
|
||||||
|
private void NotifyDataChanged() |
||||||
|
{ |
||||||
|
OnChange(); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue