|
|
@ -7,16 +7,15 @@ |
|
|
|
|
|
|
|
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input, OnChanges, SimpleChanges } from '@angular/core'; |
|
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input, OnChanges, SimpleChanges } from '@angular/core'; |
|
|
import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|
|
import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|
|
|
|
|
import { ContentsDto } from '@app/shared'; |
|
|
import { AppsState, ContentDto, ContentsService, getContentValue, LanguageDto, LocalizerService, StatefulControlComponent, Types, UIOptions, value$ } from '@app/shared/internal'; |
|
|
import { AppsState, ContentDto, ContentsService, getContentValue, LanguageDto, LocalizerService, StatefulControlComponent, Types, UIOptions, value$ } from '@app/shared/internal'; |
|
|
|
|
|
import { Observable } from 'rxjs'; |
|
|
|
|
|
|
|
|
export const SQX_REFERENCE_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { |
|
|
export const SQX_REFERENCE_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { |
|
|
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferenceDropdownComponent), multi: true, |
|
|
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferenceDropdownComponent), multi: true, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
interface State { |
|
|
interface State { |
|
|
// The referenced content items.
|
|
|
|
|
|
contents: ReadonlyArray<ContentDto>; |
|
|
|
|
|
|
|
|
|
|
|
// The names of the selected content items for search.
|
|
|
// The names of the selected content items for search.
|
|
|
contentNames: ReadonlyArray<ContentName>; |
|
|
contentNames: ReadonlyArray<ContentName>; |
|
|
|
|
|
|
|
|
@ -29,7 +28,7 @@ type ContentName = { name: string; id?: string }; |
|
|
const NO_EMIT = { emitEvent: false }; |
|
|
const NO_EMIT = { emitEvent: false }; |
|
|
|
|
|
|
|
|
@Component({ |
|
|
@Component({ |
|
|
selector: 'sqx-reference-dropdown[mode][schemaId]', |
|
|
selector: 'sqx-reference-dropdown[mode][language][languages][schemaId]', |
|
|
styleUrls: ['./reference-dropdown.component.scss'], |
|
|
styleUrls: ['./reference-dropdown.component.scss'], |
|
|
templateUrl: './reference-dropdown.component.html', |
|
|
templateUrl: './reference-dropdown.component.html', |
|
|
providers: [ |
|
|
providers: [ |
|
|
@ -39,10 +38,16 @@ const NO_EMIT = { emitEvent: false }; |
|
|
}) |
|
|
}) |
|
|
export class ReferenceDropdownComponent extends StatefulControlComponent<State, ReadonlyArray<string> | string> implements OnChanges { |
|
|
export class ReferenceDropdownComponent extends StatefulControlComponent<State, ReadonlyArray<string> | string> implements OnChanges { |
|
|
private readonly itemCount: number; |
|
|
private readonly itemCount: number; |
|
|
|
|
|
private readonly contents: ContentDto[] = []; |
|
|
|
|
|
private isOpenedBefore = false; |
|
|
|
|
|
private isLoadingFailed = false; |
|
|
|
|
|
|
|
|
@Input() |
|
|
@Input() |
|
|
public language: LanguageDto; |
|
|
public language: LanguageDto; |
|
|
|
|
|
|
|
|
|
|
|
@Input() |
|
|
|
|
|
public languages: ReadonlyArray<LanguageDto>; |
|
|
|
|
|
|
|
|
@Input() |
|
|
@Input() |
|
|
public schemaId: string; |
|
|
public schemaId: string; |
|
|
|
|
|
|
|
|
@ -66,7 +71,6 @@ export class ReferenceDropdownComponent extends StatefulControlComponent<State, |
|
|
private readonly localizer: LocalizerService, |
|
|
private readonly localizer: LocalizerService, |
|
|
) { |
|
|
) { |
|
|
super(changeDetector, { |
|
|
super(changeDetector, { |
|
|
contents: [], |
|
|
|
|
|
contentNames: [], |
|
|
contentNames: [], |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -95,30 +99,13 @@ export class ReferenceDropdownComponent extends StatefulControlComponent<State, |
|
|
|
|
|
|
|
|
public ngOnChanges(changes: SimpleChanges) { |
|
|
public ngOnChanges(changes: SimpleChanges) { |
|
|
if (changes['schemaId']) { |
|
|
if (changes['schemaId']) { |
|
|
this.resetState(); |
|
|
this.contents.clear(); |
|
|
|
|
|
|
|
|
if (this.isValid) { |
|
|
|
|
|
this.contentsService.getContents(this.appsState.appName, this.schemaId, { take: this.itemCount }) |
|
|
|
|
|
.subscribe({ |
|
|
|
|
|
next: ({ items: contents }) => { |
|
|
|
|
|
const contentNames = this.createContentNames(contents); |
|
|
|
|
|
|
|
|
|
|
|
this.next({ contents, contentNames }); |
|
|
this.resetState(); |
|
|
}, |
|
|
|
|
|
error: () => { |
|
|
|
|
|
this.control.disable(NO_EMIT); |
|
|
|
|
|
}, |
|
|
|
|
|
}); |
|
|
|
|
|
} else { |
|
|
|
|
|
this.control.disable(NO_EMIT); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (changes['language']) { |
|
|
if (changes['language'] || changes['schemaId']) { |
|
|
this.next(s => ({ |
|
|
this.resetContentNames(true); |
|
|
...s, |
|
|
|
|
|
contentNames: this.createContentNames(this.snapshot.contents), |
|
|
|
|
|
})); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -140,27 +127,77 @@ export class ReferenceDropdownComponent extends StatefulControlComponent<State, |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private selectContent(value: any) { |
|
|
public onOpened() { |
|
|
this.control.setValue(value, NO_EMIT); |
|
|
if (this.isOpenedBefore) { |
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private createContentNames(contents: ReadonlyArray<ContentDto>): ReadonlyArray<ContentName> { |
|
|
this.isOpenedBefore = true; |
|
|
if (contents.length === 0) { |
|
|
this.loadMore(this.contentsService.getContents(this.appsState.appName, this.schemaId, { take: this.itemCount })); |
|
|
return []; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const names = contents.map(content => { |
|
|
private selectContent(id: string | undefined) { |
|
|
|
|
|
const isNewId = !this.contents.find(x => x.id === id); |
|
|
|
|
|
|
|
|
|
|
|
if (id && isNewId) { |
|
|
|
|
|
this.loadMore(this.contentsService.getAllContents(this.appsState.appName, { ids: [id] })); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.control.setValue(id, NO_EMIT); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private loadMore(observable: Observable<ContentsDto>) { |
|
|
|
|
|
observable |
|
|
|
|
|
.subscribe({ |
|
|
|
|
|
next: ({ items: newContents }) => { |
|
|
|
|
|
if (newContents.length === 0) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (const content of newContents) { |
|
|
|
|
|
const index = this.contents.findIndex(x => x.id === content.id); |
|
|
|
|
|
|
|
|
|
|
|
if (index >= 0) { |
|
|
|
|
|
this.contents[index] = content; |
|
|
|
|
|
} else { |
|
|
|
|
|
this.contents.push(content); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.isLoadingFailed = false; |
|
|
|
|
|
|
|
|
|
|
|
this.resetContentNames(true); |
|
|
|
|
|
}, |
|
|
|
|
|
error: () => { |
|
|
|
|
|
this.isLoadingFailed = true; |
|
|
|
|
|
|
|
|
|
|
|
this.resetContentNames(false); |
|
|
|
|
|
}, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private resetContentNames(rebuild: boolean) { |
|
|
|
|
|
const success = this.isValid && !this.isLoadingFailed; |
|
|
|
|
|
|
|
|
|
|
|
this.onDisabled(!success); |
|
|
|
|
|
|
|
|
|
|
|
if (success && rebuild) { |
|
|
|
|
|
const contentNames: ContentName[] = [ |
|
|
|
|
|
{ name: this.localizer.getOrKey('contents.noReference') }, |
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
for (const content of this.contents) { |
|
|
const name = |
|
|
const name = |
|
|
content.referenceFields |
|
|
content.referenceFields |
|
|
.map(f => getContentValue(content, this.language, f, false)) |
|
|
.map(f => getContentValue(content, this.language, f, false)) |
|
|
.map(v => v.formatted) |
|
|
.map(v => v.formatted).filter(v => !!v) |
|
|
.filter(v => !!v) |
|
|
|
|
|
.join(', ') |
|
|
.join(', ') |
|
|
|| this.localizer.getOrKey('common.noValue'); |
|
|
|| this.localizer.getOrKey('common.noValue'); |
|
|
|
|
|
|
|
|
return { name, id: content.id }; |
|
|
contentNames.push({ name, id: content.id }); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
return [{ name: this.localizer.getOrKey('contents.noReference') }, ...names]; |
|
|
this.next({ contentNames }); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|