mirror of
https://github.com/Myxelium/Bridge-Multi.git
synced 2026-04-11 14:19:38 +00:00
Interface conversion, search bar layout
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<div id="searchMenu" class="ui bottom attached borderless menu">
|
||||
<!-- <div id="searchMenu" class="ui bottom attached borderless menu">
|
||||
<div class="item">
|
||||
<div class="ui icon input" [class.loading]="isLoading()">
|
||||
<input #searchBox type="text" placeholder=" Search..." (keyup.enter)="onSearch(searchBox.value)" />
|
||||
@@ -237,4 +237,334 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="collapse navbar grid bg-base-100 overflow-visible rounded-none" [ngClass]="showAdvanced ? 'collapse-open' : 'collapse-close'">
|
||||
<div class="flex flex-wrap justify-end gap-1">
|
||||
<!-- Search Input -->
|
||||
<div class="flex items-center order-3 md:order-2 xl:order-2 flex-none w-full md:w-auto md:flex-1 xl:flex-grow-[6] min-w-[21rem]">
|
||||
<div class="form-control w-full">
|
||||
<input type="text" [formControl]="searchControl" placeholder="What do you feel like playing today?" class="input input-bordered pr-14" />
|
||||
</div>
|
||||
<i class="bi bi-search -ml-11"></i>
|
||||
</div>
|
||||
<div class="basis-full h-0 order-4 xl:order-7"></div>
|
||||
<div class="flex order-5 md:order-5 xl:order-3">
|
||||
<!-- Instrument Dropdown -->
|
||||
<div class="dropdown">
|
||||
<label tabindex="0" class="btn btn-neutral rounded-btn rounded-r-none my-1">
|
||||
@if (instrument) {
|
||||
<img class="w-8 hidden sm:block" src="assets/images/instruments/{{ instrument }}.png" />
|
||||
}
|
||||
{{ instrumentDisplay(instrument) }}
|
||||
</label>
|
||||
<ul tabindex="0" class="menu dropdown-content z-[2] p-2 shadow bg-neutral text-neutral-content rounded-box w-64">
|
||||
<li>
|
||||
<a (click)="setInstrument(null, $event)">{{ instrumentDisplay(null) }}</a>
|
||||
</li>
|
||||
@for (instrument of instruments; track $index) {
|
||||
<li>
|
||||
<a (click)="setInstrument(instrument, $event)">
|
||||
<img class="w-8" src="assets/images/instruments/{{ instrument }}.png" />
|
||||
{{ instrumentDisplay(instrument) }}
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Difficulty Dropdown -->
|
||||
<div class="dropdown">
|
||||
<label tabindex="0" class="btn btn-neutral rounded-btn rounded-l-none my-1">{{ difficultyDisplay(difficulty) }}</label>
|
||||
<ul tabindex="0" class="menu dropdown-content z-[2] p-2 shadow bg-neutral text-neutral-content rounded-box w-40">
|
||||
<li>
|
||||
<a (click)="setDifficulty(null, $event)">{{ difficultyDisplay(null) }}</a>
|
||||
</li>
|
||||
@for (difficulty of difficulties; track $index) {
|
||||
<li>
|
||||
<a (click)="setDifficulty(difficulty, $event)">{{ difficultyDisplay(difficulty) }}</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Advanced Search -->
|
||||
<div class="order-6 md:order-6 xl:order-4 xl:flex-grow-[5]">
|
||||
<button class="btn btn-ghost" (click)="setShowAdvanced(!showAdvanced)">
|
||||
Advanced Search
|
||||
<div class="cursor-pointer swap swap-rotate" [class.swap-active]="showAdvanced">
|
||||
<i class="swap-off bi bi-chevron-down"></i>
|
||||
<i class="swap-on bi bi-chevron-up"></i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse-content justify-center">
|
||||
<form [formGroup]="advancedSearchForm">
|
||||
<div class="flex flex-wrap gap-5 justify-center">
|
||||
<div>
|
||||
<table class="table table-xs">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<div
|
||||
class="tooltip tooltip-bottom font-normal [text-wrap:balance]"
|
||||
data-tip='Search for text in these specific chart properties. Note: you can put a minus sign (-) before words to return only results without that word. (e.g. "Dragon -Dragonforce")'>
|
||||
<span class="font-bold underline decoration-dotted cursor-help">Search by</span>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div
|
||||
class="tooltip tooltip-bottom font-normal [text-wrap:balance]"
|
||||
data-tip="Only include results that match perfectly. (not case sensitive)">
|
||||
<span class="font-bold underline decoration-dotted cursor-help">Exact</span>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div class="tooltip tooltip-bottom font-normal [text-wrap:balance]" data-tip="Do not include results that match this.">
|
||||
<span class="font-bold underline decoration-dotted cursor-help">Exclude</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="border-b-0" formGroupName="name">
|
||||
<td><input type="text" placeholder="Name" class="input input-bordered input-sm" formControlName="value" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exact" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exclude" /></td>
|
||||
</tr>
|
||||
<tr class="border-b-0" formGroupName="artist">
|
||||
<td><input type="text" placeholder="Artist" class="input input-bordered input-sm" formControlName="value" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exact" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exclude" /></td>
|
||||
</tr>
|
||||
<tr class="border-b-0" formGroupName="album">
|
||||
<td><input type="text" placeholder="Album" class="input input-bordered input-sm" formControlName="value" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exact" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exclude" /></td>
|
||||
</tr>
|
||||
<tr class="border-b-0" formGroupName="genre">
|
||||
<td><input type="text" placeholder="Genre" class="input input-bordered input-sm" formControlName="value" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exact" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exclude" /></td>
|
||||
</tr>
|
||||
<tr class="border-b-0" formGroupName="year">
|
||||
<td><input type="text" placeholder="Year" class="input input-bordered input-sm" formControlName="value" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exact" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exclude" /></td>
|
||||
</tr>
|
||||
<tr class="border-b-0" formGroupName="charter">
|
||||
<td><input type="text" placeholder="Charter" class="input input-bordered input-sm" formControlName="value" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exact" /></td>
|
||||
<td><input type="checkbox" class="checkbox" formControlName="exclude" /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 justify-end">
|
||||
<table class="table table-xs">
|
||||
<tbody>
|
||||
<tr class="border-b-0">
|
||||
<td class="text-sm">Length (minutes)</td>
|
||||
<td>
|
||||
<div class="join">
|
||||
<input type="number" placeholder="Min" class="input input-bordered join-item input-sm w-16" formControlName="minLength" />
|
||||
<input type="number" placeholder="Max" class="input input-bordered join-item input-sm w-16" formControlName="maxLength" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b-0">
|
||||
<td class="text-sm">
|
||||
<span
|
||||
class="label-text underline decoration-dotted cursor-help tooltip [text-wrap:balance]"
|
||||
data-tip="Also known as chart difficulty. Typically a number between 0 and 6.">
|
||||
Intensity
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="join">
|
||||
<input type="number" placeholder="Min" class="input input-bordered join-item input-sm w-16" formControlName="minIntensity" />
|
||||
<input type="number" placeholder="Max" class="input input-bordered join-item input-sm w-16" formControlName="maxIntensity" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b-0">
|
||||
<td class="text-sm">Average NPS</td>
|
||||
<td>
|
||||
<div class="join">
|
||||
<input type="number" placeholder="Min" class="input input-bordered join-item input-sm w-16" formControlName="minAverageNPS" />
|
||||
<input type="number" placeholder="Max" class="input input-bordered join-item input-sm w-16" formControlName="maxAverageNPS" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b-0">
|
||||
<td class="text-sm">Max NPS</td>
|
||||
<td>
|
||||
<div class="join">
|
||||
<input type="number" placeholder="Min" class="input input-bordered join-item input-sm w-16" formControlName="minMaxNPS" />
|
||||
<input type="number" placeholder="Max" class="input input-bordered join-item input-sm w-16" formControlName="maxMaxNPS" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b-0">
|
||||
<td class="text-sm">
|
||||
<span
|
||||
class="label-text underline decoration-dotted cursor-help tooltip [text-wrap:balance]"
|
||||
data-tip="The date of the last time this chart was modified in Google Drive.">
|
||||
Modified After
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="date"
|
||||
min="2012-01-01"
|
||||
[max]="todayDate"
|
||||
placeholder="YYYY/MM/DD"
|
||||
class="input input-bordered join-item input-sm w-32"
|
||||
formControlName="modifiedAfter"
|
||||
(blur)="startValidation = true"
|
||||
[class.input-error]="advancedSearchForm.invalid && startValidation" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b-0">
|
||||
<td class="text-sm">
|
||||
<span
|
||||
class="label-text underline decoration-dotted cursor-help tooltip [text-wrap:balance]"
|
||||
data-tip="The MD5 hash of the chart folder or .sng file. You can enter multiple values if they are separated by commas.">
|
||||
Hash
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="input input-bordered join-item input-sm w-32" formControlName="hash" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between">
|
||||
<div class="flex gap-2">
|
||||
<div class="flex flex-col">
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input
|
||||
#hasSoloSections
|
||||
type="checkbox"
|
||||
class="toggle toggle-sm"
|
||||
[indeterminate]="true"
|
||||
(click)="clickCheckbox('hasSoloSections', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasSoloSections') === null">
|
||||
{{ formValue('hasSoloSections') === false ? 'No ' : '' }}Solo Sections
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input
|
||||
#hasForcedNotes
|
||||
type="checkbox"
|
||||
class="toggle toggle-sm"
|
||||
[indeterminate]="true"
|
||||
(click)="clickCheckbox('hasForcedNotes', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasForcedNotes') === null">
|
||||
{{ formValue('hasForcedNotes') === false ? 'No ' : '' }}Forced Notes
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input
|
||||
#hasOpenNotes
|
||||
type="checkbox"
|
||||
class="toggle toggle-sm"
|
||||
[indeterminate]="true"
|
||||
(click)="clickCheckbox('hasOpenNotes', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasOpenNotes') === null">
|
||||
{{ formValue('hasOpenNotes') === false ? 'No ' : '' }}Open Notes
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input
|
||||
#hasTapNotes
|
||||
type="checkbox"
|
||||
class="toggle toggle-sm"
|
||||
[indeterminate]="true"
|
||||
(click)="clickCheckbox('hasTapNotes', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasTapNotes') === null">
|
||||
{{ formValue('hasTapNotes') === false ? 'No ' : '' }}Tap Notes
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input type="checkbox" class="toggle toggle-sm" [indeterminate]="true" (click)="clickCheckbox('hasLyrics', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasLyrics') === null">
|
||||
{{ formValue('hasLyrics') === false ? 'No ' : '' }}Lyrics
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input type="checkbox" class="toggle toggle-sm" [indeterminate]="true" (click)="clickCheckbox('hasVocals', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasVocals') === null">
|
||||
{{ formValue('hasVocals') === false ? 'No ' : '' }}Vocals
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input
|
||||
#hasRollLanes
|
||||
type="checkbox"
|
||||
class="toggle toggle-sm"
|
||||
[indeterminate]="true"
|
||||
(click)="clickCheckbox('hasRollLanes', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasRollLanes') === null">
|
||||
{{ formValue('hasRollLanes') === false ? 'No ' : '' }}Roll Lanes
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input #has2xKick type="checkbox" class="toggle toggle-sm" [indeterminate]="true" (click)="clickCheckbox('has2xKick', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('has2xKick') === null">
|
||||
{{ formValue('has2xKick') === false ? 'No ' : '' }}2x Kick
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input type="checkbox" class="toggle toggle-sm" [indeterminate]="true" (click)="clickCheckbox('hasIssues', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasIssues') === null">
|
||||
{{ formValue('hasIssues') === false ? 'No ' : '' }}Chart Issues
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input type="checkbox" class="toggle toggle-sm" [indeterminate]="true" (click)="clickCheckbox('hasVideoBackground', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('hasVideoBackground') === null">
|
||||
{{ formValue('hasVideoBackground') === false ? 'No ' : '' }}Video Background
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label cursor-pointer justify-normal gap-2">
|
||||
<input type="checkbox" class="toggle toggle-sm" [indeterminate]="true" (click)="clickCheckbox('modchart', $event)" />
|
||||
<span class="label-text" [class.text-opacity-70]="formValue('modchart') === null">
|
||||
{{ formValue('modchart') === false ? 'Not a ' : '' }}Modchart
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-primary" [class.btn-disabled]="advancedSearchForm.invalid && startValidation" (click)="searchAdvanced()">
|
||||
Search{{ advancedSearchForm.invalid && startValidation ? ' ("Modified After" is invalid)' : '' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#searchMenu {
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 0em;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#searchMenu>.item {
|
||||
padding: .3em .4em;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
#searchMenu>.item:first-child {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
#searchIcon {
|
||||
cursor: default;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#advancedSearchForm {
|
||||
margin: 0em;
|
||||
border-width: 0px;
|
||||
overflow: hidden;
|
||||
transition: max-height 350ms cubic-bezier(0.45, 0, 0.55, 1);
|
||||
max-height: 243.913px; /* This is its preferred height. Transition needs a static target number to work. */
|
||||
}
|
||||
|
||||
.collapsed {
|
||||
max-height: 0px !important;
|
||||
}
|
||||
|
||||
#quantityDropdownItem, #similarityDropdownItem {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
transition: visibility 0s, opacity 350ms cubic-bezier(0.45, 0, 0.55, 1);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
max-width: 0px !important;
|
||||
max-height: 0px !important;
|
||||
transition:
|
||||
opacity 350ms cubic-bezier(0.45, 0, 0.55, 1),
|
||||
visibility 0s linear 350ms,
|
||||
max-width 0s linear 350ms,
|
||||
max-height 0s linear 350ms !important;
|
||||
}
|
||||
@@ -1,78 +1,246 @@
|
||||
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'
|
||||
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'
|
||||
import { AbstractControl, FormBuilder, FormControl } from '@angular/forms'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import { distinctUntilChanged, switchMap, throttleTime } from 'rxjs'
|
||||
import { Difficulty, Instrument } from 'scan-chart'
|
||||
import { SearchService } from 'src-angular/app/core/services/search.service'
|
||||
|
||||
import { getDefaultSearch } from '../../../../../src-shared/interfaces/search.interface'
|
||||
import { difficulties, difficultyDisplay, instrumentDisplay, instruments } from 'src-shared/UtilFunctions'
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-bar',
|
||||
templateUrl: './search-bar.component.html',
|
||||
styleUrls: ['./search-bar.component.scss'],
|
||||
})
|
||||
export class SearchBarComponent implements AfterViewInit {
|
||||
export class SearchBarComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@ViewChild('searchIcon', { static: true }) searchIcon: ElementRef
|
||||
@ViewChild('quantityDropdown', { static: true }) quantityDropdown: ElementRef
|
||||
@ViewChild('similarityDropdown', { static: true }) similarityDropdown: ElementRef
|
||||
@ViewChild('diffSlider', { static: true }) diffSlider: ElementRef
|
||||
@ViewChild('hasSoloSections') hasSoloSections: ElementRef<HTMLInputElement>
|
||||
@ViewChild('hasForcedNotes') hasForcedNotes: ElementRef<HTMLInputElement>
|
||||
@ViewChild('hasOpenNotes') hasOpenNotes: ElementRef<HTMLInputElement>
|
||||
@ViewChild('hasTapNotes') hasTapNotes: ElementRef<HTMLInputElement>
|
||||
@ViewChild('hasRollLanes') hasRollLanes: ElementRef<HTMLInputElement>
|
||||
@ViewChild('has2xKick') has2xKick: ElementRef<HTMLInputElement>
|
||||
|
||||
isError = false
|
||||
showAdvanced = false
|
||||
searchSettings = getDefaultSearch()
|
||||
private sliderInitialized = false
|
||||
public showAdvanced = false
|
||||
public instruments = instruments
|
||||
public difficulties = difficulties
|
||||
public instrumentDisplay = instrumentDisplay
|
||||
public difficultyDisplay = difficultyDisplay
|
||||
|
||||
constructor(public searchService: SearchService) { }
|
||||
public advancedSearchForm: ReturnType<this['getAdvancedSearchForm']>
|
||||
public startValidation = false
|
||||
|
||||
constructor(
|
||||
private searchService: SearchService,
|
||||
private fb: FormBuilder,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.searchControl.valueChanges.pipe(
|
||||
throttleTime(400, undefined, { leading: true, trailing: true }),
|
||||
distinctUntilChanged(),
|
||||
switchMap(search => this.searchService.search(search || '*'))
|
||||
).subscribe()
|
||||
|
||||
this.initializeAdvancedSearchForm()
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
// TODO
|
||||
// $(this.searchIcon.nativeElement).popup({
|
||||
// onShow: () => this.isError, // Only show the popup if there is an error
|
||||
// })
|
||||
this.searchService.onSearchErrorStateUpdate(isError => {
|
||||
this.isError = isError
|
||||
this.updateDisabledControls()
|
||||
this.searchService.instrument.valueChanges.subscribe(() => {
|
||||
this.updateDisabledControls()
|
||||
})
|
||||
// $(this.quantityDropdown.nativeElement).dropdown({
|
||||
// onChange: (value: string) => {
|
||||
// this.searchSettings.quantity = value as 'all' | 'any'
|
||||
// },
|
||||
// })
|
||||
// $(this.similarityDropdown.nativeElement).dropdown({
|
||||
// onChange: (value: string) => {
|
||||
// this.searchSettings.similarity = value as 'similar' | 'exact'
|
||||
// },
|
||||
// })
|
||||
}
|
||||
|
||||
onSearch(query: string) {
|
||||
this.searchSettings.query = query
|
||||
this.searchSettings.limit = 50 + 1
|
||||
this.searchSettings.offset = 0
|
||||
this.searchService.newSearch(this.searchSettings)
|
||||
}
|
||||
|
||||
onAdvancedSearchClick() {
|
||||
this.showAdvanced = !this.showAdvanced
|
||||
|
||||
if (!this.sliderInitialized) {
|
||||
setTimeout(() => { // Initialization requires this element to not be collapsed
|
||||
// TODO
|
||||
// $(this.diffSlider.nativeElement).slider({
|
||||
// min: 0,
|
||||
// max: 6,
|
||||
// start: 0,
|
||||
// end: 6,
|
||||
// step: 1,
|
||||
// onChange: (_length: number, min: number, max: number) => {
|
||||
// this.searchSettings.minDiff = min
|
||||
// this.searchSettings.maxDiff = max
|
||||
// },
|
||||
// })
|
||||
}, 50)
|
||||
this.sliderInitialized = true
|
||||
setShowAdvanced(showAdvanced: boolean) {
|
||||
this.showAdvanced = showAdvanced
|
||||
if (showAdvanced) {
|
||||
this.startValidation = false
|
||||
this.searchControl.disable()
|
||||
} else {
|
||||
this.searchControl.enable()
|
||||
}
|
||||
}
|
||||
|
||||
isLoading() {
|
||||
return this.searchService.isLoading()
|
||||
get searchControl() {
|
||||
return this.searchService.searchControl
|
||||
}
|
||||
get instrument() {
|
||||
return this.searchService.instrument.value
|
||||
}
|
||||
setInstrument(instrument: Instrument | null, event: MouseEvent) {
|
||||
this.searchService.instrument.setValue(instrument)
|
||||
if (event.target instanceof HTMLElement) {
|
||||
event.target.parentElement?.parentElement?.blur()
|
||||
}
|
||||
}
|
||||
|
||||
get difficulty() {
|
||||
return this.searchService.difficulty.value
|
||||
}
|
||||
setDifficulty(difficulty: Difficulty | null, event: MouseEvent) {
|
||||
this.searchService.difficulty.setValue(difficulty)
|
||||
if (event.target instanceof HTMLElement) {
|
||||
event.target.parentElement?.parentElement?.blur()
|
||||
}
|
||||
}
|
||||
|
||||
get logoType() {
|
||||
switch (localStorage.getItem('theme')) {
|
||||
case 'emerald': return 'emerald'
|
||||
case 'halloween': return 'halloween'
|
||||
case 'lemonade': return 'lemonade'
|
||||
case 'night': return 'night'
|
||||
case 'synthwave': return 'synthwave'
|
||||
case 'aqua': return 'orange'
|
||||
case 'valentine': return 'valentine'
|
||||
case 'winter': return 'winter'
|
||||
case 'aren': return 'aren'
|
||||
case 'froogs': return 'froogs'
|
||||
default: return 'default'
|
||||
}
|
||||
}
|
||||
|
||||
get todayDate() {
|
||||
return dayjs().format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
// TODO: run this when infinite scroll should happen
|
||||
// @HostListener("window:scroll", [])
|
||||
// onScroll(): void {
|
||||
// if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
|
||||
// if (this.searchService.areMorePages && !this.searchService.searchLoading) {
|
||||
// this.searchService.search(this.searchControl.value || '*', true).subscribe()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
initializeAdvancedSearchForm() {
|
||||
this.advancedSearchForm = this.getAdvancedSearchForm() as ReturnType<this['getAdvancedSearchForm']>
|
||||
|
||||
for (const key of ['name', 'artist', 'album', 'genre', 'year', 'charter'] as const) {
|
||||
this.advancedSearchForm.get(key)?.get('exact')?.disable()
|
||||
this.advancedSearchForm.get(key)?.get('exclude')?.disable()
|
||||
this.advancedSearchForm.get(key)?.get('value')?.valueChanges.subscribe(value => {
|
||||
if (value) {
|
||||
this.advancedSearchForm.get(key)?.get('exact')?.enable()
|
||||
this.advancedSearchForm.get(key)?.get('exclude')?.enable()
|
||||
} else {
|
||||
this.advancedSearchForm.get(key)?.get('exact')?.disable()
|
||||
this.advancedSearchForm.get(key)?.get('exact')?.setValue(false)
|
||||
this.advancedSearchForm.get(key)?.get('exclude')?.disable()
|
||||
this.advancedSearchForm.get(key)?.get('exclude')?.setValue(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
updateDisabledControls() {
|
||||
const isDrums = this.searchService.instrument.value === 'drums'
|
||||
const isAny = this.searchService.instrument.value === null
|
||||
const explanation = 'Not available for the current instrument.'
|
||||
|
||||
this.hasSoloSections.nativeElement.disabled = isDrums && !isAny
|
||||
this.hasForcedNotes.nativeElement.disabled = isDrums && !isAny
|
||||
this.hasOpenNotes.nativeElement.disabled = isDrums && !isAny
|
||||
this.hasTapNotes.nativeElement.disabled = isDrums && !isAny
|
||||
this.hasRollLanes.nativeElement.disabled = !isDrums && !isAny
|
||||
this.has2xKick.nativeElement.disabled = !isDrums && !isAny
|
||||
|
||||
this.hasSoloSections.nativeElement.title = isDrums && !isAny ? explanation : ''
|
||||
this.hasForcedNotes.nativeElement.title = isDrums && !isAny ? explanation : ''
|
||||
this.hasOpenNotes.nativeElement.title = isDrums && !isAny ? explanation : ''
|
||||
this.hasTapNotes.nativeElement.title = isDrums && !isAny ? explanation : ''
|
||||
this.hasRollLanes.nativeElement.title = !isDrums && !isAny ? explanation : ''
|
||||
this.has2xKick.nativeElement.title = !isDrums && !isAny ? explanation : ''
|
||||
|
||||
if (!isAny) {
|
||||
if (isDrums) {
|
||||
this.advancedSearchForm.get('hasSoloSections')?.setValue(null)
|
||||
this.advancedSearchForm.get('hasForcedNotes')?.setValue(null)
|
||||
this.advancedSearchForm.get('hasOpenNotes')?.setValue(null)
|
||||
this.advancedSearchForm.get('hasTapNotes')?.setValue(null)
|
||||
this.hasSoloSections.nativeElement.indeterminate = true
|
||||
this.hasForcedNotes.nativeElement.indeterminate = true
|
||||
this.hasOpenNotes.nativeElement.indeterminate = true
|
||||
this.hasTapNotes.nativeElement.indeterminate = true
|
||||
} else {
|
||||
this.advancedSearchForm.get('hasRollLanes')?.setValue(null)
|
||||
this.advancedSearchForm.get('has2xKick')?.setValue(null)
|
||||
this.hasRollLanes.nativeElement.indeterminate = true
|
||||
this.has2xKick.nativeElement.indeterminate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAdvancedSearchForm() {
|
||||
return this.fb.group({
|
||||
name: this.fb.nonNullable.group({ value: '', exact: false, exclude: false }),
|
||||
artist: this.fb.nonNullable.group({ value: '', exact: false, exclude: false }),
|
||||
album: this.fb.nonNullable.group({ value: '', exact: false, exclude: false }),
|
||||
genre: this.fb.nonNullable.group({ value: '', exact: false, exclude: false }),
|
||||
year: this.fb.nonNullable.group({ value: '', exact: false, exclude: false }),
|
||||
charter: this.fb.nonNullable.group({ value: '', exact: false, exclude: false }),
|
||||
minLength: null as number | null,
|
||||
maxLength: null as number | null,
|
||||
minIntensity: null as number | null,
|
||||
maxIntensity: null as number | null,
|
||||
minAverageNPS: null as number | null,
|
||||
maxAverageNPS: null as number | null,
|
||||
minMaxNPS: null as number | null,
|
||||
maxMaxNPS: null as number | null,
|
||||
modifiedAfter: this.fb.nonNullable.control('', { validators: dateVaidator }),
|
||||
hash: this.fb.nonNullable.control(''),
|
||||
hasSoloSections: null as boolean | null,
|
||||
hasForcedNotes: null as boolean | null,
|
||||
hasOpenNotes: null as boolean | null,
|
||||
hasTapNotes: null as boolean | null,
|
||||
hasLyrics: null as boolean | null,
|
||||
hasVocals: null as boolean | null,
|
||||
hasRollLanes: null as boolean | null,
|
||||
has2xKick: null as boolean | null,
|
||||
hasIssues: null as boolean | null,
|
||||
hasVideoBackground: null as boolean | null,
|
||||
modchart: null as boolean | null,
|
||||
})
|
||||
}
|
||||
|
||||
clickCheckbox(key: string, event: MouseEvent) {
|
||||
if (event.target instanceof HTMLInputElement) {
|
||||
const control = this.advancedSearchForm.get(key) as FormControl<boolean | null>
|
||||
if (control.value === true) {
|
||||
control.setValue(false)
|
||||
event.target.checked = false
|
||||
} else if (control.value === false) {
|
||||
control.setValue(null)
|
||||
event.target.checked = false
|
||||
event.target.indeterminate = true
|
||||
} else if (control.value === null) {
|
||||
control.setValue(true)
|
||||
event.target.checked = true
|
||||
event.target.indeterminate = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formValue(key: string) {
|
||||
return this.advancedSearchForm.get(key)?.value
|
||||
}
|
||||
|
||||
searchAdvanced() {
|
||||
this.startValidation = true
|
||||
if (this.advancedSearchForm.valid && !this.searchService.searchLoading) {
|
||||
this.searchService.advancedSearch({
|
||||
instrument: this.instrument,
|
||||
difficulty: this.difficulty,
|
||||
...this.advancedSearchForm.getRawValue(),
|
||||
}).subscribe()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dateVaidator(control: AbstractControl) {
|
||||
if (control.value && isNaN(Date.parse(control.value))) {
|
||||
return { 'dateVaidator': true }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user