diff --git a/src/index.ts b/src/index.ts index 2014a20..e765e97 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,6 @@ export * from './lib/loading'; export * from './lib/error'; export * from './lib/search'; export * from './lib/upload-file'; -export * from './lib/empty-state'; export * from './lib/caching'; export * from './lib/translations'; export * from './lib/pipes'; diff --git a/src/lib/buttons/chevron-button/index.ts b/src/lib/buttons/chevron-button/index.ts new file mode 100644 index 0000000..fe12359 --- /dev/null +++ b/src/lib/buttons/chevron-button/index.ts @@ -0,0 +1 @@ +export * from './chevron-button.component'; diff --git a/src/lib/buttons/index.ts b/src/lib/buttons/index.ts index 30ba65f..8476a99 100644 --- a/src/lib/buttons/index.ts +++ b/src/lib/buttons/index.ts @@ -3,4 +3,3 @@ export * from './types/circle-button.type'; export * from './icon-button/icon-button.component'; export * from './circle-button/circle-button.component'; -export * from './chevron-button/chevron-button.component'; diff --git a/src/lib/directives/disable-stop-propagation.directive.ts b/src/lib/directives/disable-stop-propagation.directive.ts index 64d9de0..2f7e0cd 100644 --- a/src/lib/directives/disable-stop-propagation.directive.ts +++ b/src/lib/directives/disable-stop-propagation.directive.ts @@ -1,9 +1,9 @@ -import { booleanAttribute, Directive, Input } from '@angular/core'; +import { booleanAttribute, Directive, input } from '@angular/core'; @Directive({ selector: '[iqserDisableStopPropagation]', standalone: true, }) export class DisableStopPropagationDirective { - @Input({ transform: booleanAttribute }) iqserDisableStopPropagation = true; + readonly iqserDisableStopPropagation = input(true, { transform: booleanAttribute }); } diff --git a/src/lib/directives/stop-propagation.directive.ts b/src/lib/directives/stop-propagation.directive.ts index 51ac9b6..67f23df 100644 --- a/src/lib/directives/stop-propagation.directive.ts +++ b/src/lib/directives/stop-propagation.directive.ts @@ -1,6 +1,6 @@ import { booleanAttribute, Directive, HostListener, inject, Input } from '@angular/core'; -import { DisableStopPropagationDirective } from './disable-stop-propagation.directive'; import { NGXLogger } from 'ngx-logger'; +import { DisableStopPropagationDirective } from './disable-stop-propagation.directive'; @Directive({ selector: '[iqserStopPropagation]', @@ -13,7 +13,7 @@ export class StopPropagationDirective { @HostListener('click', ['$event']) onClick($event: Event) { - if (this.#disableStopPropagation?.iqserDisableStopPropagation) { + if (this.#disableStopPropagation?.iqserDisableStopPropagation()) { this.#logger.info('[CLICK] iqserStopPropagation is disabled by iqserDisableStopPropagation'); return; } diff --git a/src/lib/empty-state/empty-state.component.html b/src/lib/empty-state/empty-state.component.html index b5bef39..a856539 100644 --- a/src/lib/empty-state/empty-state.component.html +++ b/src/lib/empty-state/empty-state.component.html @@ -1,12 +1,5 @@ -
- @if (icon) { +
+ @if (icon(); as icon) { } @@ -14,15 +7,15 @@
-
+
- @if (showButton) { + @if (showButton() && this.action.observed) { } diff --git a/src/lib/empty-state/empty-state.component.ts b/src/lib/empty-state/empty-state.component.ts index 92bc0e3..9a1f56a 100644 --- a/src/lib/empty-state/empty-state.component.ts +++ b/src/lib/empty-state/empty-state.component.ts @@ -1,32 +1,43 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { IconButtonComponent, IconButtonTypes } from '../buttons'; -import { randomString } from '../utils'; import { NgStyle } from '@angular/common'; +import { + booleanAttribute, + ChangeDetectionStrategy, + Component, + computed, + EventEmitter, + input, + numberAttribute, + Output, +} from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; +import { IconButtonComponent } from '../buttons/icon-button/icon-button.component'; +import { IconButtonTypes } from '../buttons/types/icon-button.type'; +import { randomString } from '../utils/functions'; @Component({ - selector: 'iqser-empty-state [text]', + selector: 'iqser-empty-state', templateUrl: './empty-state.component.html', styleUrls: ['./empty-state.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [NgStyle, MatIconModule, IconButtonComponent], }) -export class EmptyStateComponent implements OnInit { - readonly iconButtonTypes = IconButtonTypes; +export class EmptyStateComponent { + protected readonly iconButtonTypes = IconButtonTypes; - @Input() text!: string; - @Input() icon?: string; - @Input() showButton = true; - @Input() buttonIcon = 'iqser:plus'; - @Input() buttonLabel?: string; - @Input() buttonId = `${randomString()}-icon-button`; - @Input() horizontalPadding = 100; - @Input() verticalPadding = 120; - @Input() helpModeKey?: string; + readonly text = input.required(); + readonly icon = input(); + readonly showButton = input(true, { transform: booleanAttribute }); + readonly buttonIcon = input('iqser:plus'); + readonly buttonLabel = input(); + readonly buttonId = input(`${randomString()}-icon-button`); + readonly horizontalPadding = input(100, { transform: numberAttribute }); + readonly verticalPadding = input(120, { transform: numberAttribute }); + protected readonly styles = computed(() => ({ + 'padding-top': this.verticalPadding() + 'px', + 'padding-left': this.horizontalPadding() + 'px', + 'padding-right': this.horizontalPadding() + 'px', + })); + readonly helpModeKey = input(); @Output() readonly action = new EventEmitter(); - - ngOnInit(): void { - this.showButton = this.showButton && this.action.observed; - } } diff --git a/src/lib/error/connection-status/connection-status.component.html b/src/lib/error/connection-status/connection-status.component.html index 93e83aa..4c7cc8e 100644 --- a/src/lib/error/connection-status/connection-status.component.html +++ b/src/lib/error/connection-status/connection-status.component.html @@ -1,4 +1,4 @@ -@if (errorService.connectionStatus$ | async; as status) { +@if (connectionStatus(); as status) {
diff --git a/src/lib/error/connection-status/connection-status.component.ts b/src/lib/error/connection-status/connection-status.component.ts index 04f3e36..d7011c5 100644 --- a/src/lib/error/connection-status/connection-status.component.ts +++ b/src/lib/error/connection-status/connection-status.component.ts @@ -1,6 +1,7 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; -import { connectionStatusTranslations } from '../../translations'; import { animate, state, style, transition, trigger } from '@angular/animations'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { connectionStatusTranslations } from '../../translations'; import { ErrorService } from '../error.service'; @Component({ @@ -18,6 +19,6 @@ import { ErrorService } from '../error.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ConnectionStatusComponent { - connectionStatusTranslations = connectionStatusTranslations; - protected readonly errorService = inject(ErrorService); + protected readonly connectionStatusTranslations = connectionStatusTranslations; + protected readonly connectionStatus = toSignal(inject(ErrorService).connectionStatus$); } diff --git a/src/lib/error/full-page-error/full-page-error.component.ts b/src/lib/error/full-page-error/full-page-error.component.ts index 0a2f7a6..356704f 100644 --- a/src/lib/error/full-page-error/full-page-error.component.ts +++ b/src/lib/error/full-page-error/full-page-error.component.ts @@ -1,7 +1,7 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { IconButtonTypes } from '../../buttons'; import { CustomError, ErrorService, ErrorType } from '../error.service'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @Component({ selector: 'iqser-full-page-error', @@ -10,9 +10,8 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class FullPageErrorComponent { - readonly iconButtonTypes = IconButtonTypes; - - constructor(readonly errorService: ErrorService) {} + protected readonly iconButtonTypes = IconButtonTypes; + protected readonly errorService = inject(ErrorService); errorTitle(error: ErrorType): string { return error instanceof CustomError ? error.label : _('error.title'); diff --git a/src/lib/filtering/filter-card/filter-card.component.html b/src/lib/filtering/filter-card/filter-card.component.html index ae6e2ed..dd72c09 100644 --- a/src/lib/filtering/filter-card/filter-card.component.html +++ b/src/lib/filtering/filter-card/filter-card.component.html @@ -9,7 +9,9 @@ >
} + + @if (primaryFilters$ | async; as filters) {
@for (filter of filters; track filter) { @@ -24,11 +26,13 @@ }
} + @if (secondaryFilterGroup$ | async; as secondaryGroup) {
+ @for (filter of secondaryGroup.filters; track filter) { -
+
@if (!primaryGroup.singleSelect) {
} +
- +
@if (filter.children?.length && filter.expanded) { @@ -122,7 +131,7 @@ [ngTemplateOutlet]="filterGroup.filterTemplate ?? defaultFilterLabelTemplate" > - +
} } diff --git a/src/lib/filtering/filter-card/filter-card.component.ts b/src/lib/filtering/filter-card/filter-card.component.ts index a779403..c4a9a04 100644 --- a/src/lib/filtering/filter-card/filter-card.component.ts +++ b/src/lib/filtering/filter-card/filter-card.component.ts @@ -1,5 +1,5 @@ -import { AsyncPipe, NgForOf, NgIf, NgTemplateOutlet } from '@angular/common'; -import { ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, TemplateRef } from '@angular/core'; +import { AsyncPipe, NgTemplateOutlet } from '@angular/common'; +import { ChangeDetectionStrategy, Component, effect, ElementRef, inject, input, numberAttribute, OnInit, TemplateRef } from '@angular/core'; import { MAT_CHECKBOX_DEFAULT_OPTIONS, MatCheckbox } from '@angular/material/checkbox'; import { MatIcon } from '@angular/material/icon'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @@ -23,7 +23,7 @@ const atLeastOneIsExpandable = pipe( ); @Component({ - selector: 'iqser-filter-card [primaryFiltersSlug]', + selector: 'iqser-filter-card', templateUrl: './filter-card.component.html', styleUrls: ['./filter-card.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, @@ -38,38 +38,29 @@ const atLeastOneIsExpandable = pipe( }, ], standalone: true, - imports: [ - AsyncPipe, - InputWithActionComponent, - NgTemplateOutlet, - TranslateModule, - MatIcon, - MatCheckbox, - StopPropagationDirective, - NgIf, - NgForOf, - ], + imports: [AsyncPipe, InputWithActionComponent, NgTemplateOutlet, TranslateModule, MatIcon, MatCheckbox, StopPropagationDirective], }) export class FilterCardComponent implements OnInit { - @Input() primaryFiltersSlug!: string; - @Input() fileId?: string; - @Input() actionsTemplate?: TemplateRef; - @Input() secondaryFiltersSlug = ''; - @Input() primaryFiltersLabel: string = _('filter-menu.filter-types'); - @Input() minWidth = 350; - + readonly #filterService = inject(FilterService); + readonly #elementRef = inject(ElementRef); + protected readonly searchService = inject>(SearchService); + readonly primaryFiltersSlug = input.required(); + readonly fileId = input(); + readonly actionsTemplate = input>(); + readonly secondaryFiltersSlug = input(''); + readonly primaryFiltersLabel = input(_('filter-menu.filter-types')); + readonly minWidth = input(350, { transform: numberAttribute }); primaryFilterGroup$!: Observable; secondaryFilterGroup$!: Observable; primaryFilters$!: Observable; - atLeastOneFilterIsExpandable$?: Observable; atLeastOneSecondaryFilterIsExpandable$?: Observable; - constructor( - readonly filterService: FilterService, - readonly searchService: SearchService, - private readonly _elementRef: ElementRef, - ) {} + constructor() { + effect(() => { + (this.#elementRef.nativeElement as HTMLElement).style.setProperty('--filter-card-min-width', `${this.minWidth()}px`); + }); + } private get _primaryFilters$(): Observable { return combineLatest([this.primaryFilterGroup$, this.searchService.valueChanges$]).pipe( @@ -79,39 +70,37 @@ export class FilterCardComponent implements OnInit { } ngOnInit() { - this.primaryFilterGroup$ = this.filterService.getGroup$(this.primaryFiltersSlug).pipe(shareLast()); - this.secondaryFilterGroup$ = this.filterService.getGroup$(this.secondaryFiltersSlug).pipe(shareLast()); + this.primaryFilterGroup$ = this.#filterService.getGroup$(this.primaryFiltersSlug()).pipe(shareLast()); + this.secondaryFilterGroup$ = this.#filterService.getGroup$(this.secondaryFiltersSlug()).pipe(shareLast()); this.primaryFilters$ = this._primaryFilters$; this.atLeastOneFilterIsExpandable$ = atLeastOneIsExpandable(this.primaryFilterGroup$); this.atLeastOneSecondaryFilterIsExpandable$ = atLeastOneIsExpandable(this.secondaryFilterGroup$); - - (this._elementRef.nativeElement as HTMLElement).style.setProperty('--filter-card-min-width', `${this.minWidth}px`); } filterCheckboxClicked(nestedFilter: INestedFilter, filterGroup: IFilterGroup, parent?: INestedFilter): void { - this.filterService.filterCheckboxClicked({ + this.#filterService.filterCheckboxClicked({ nestedFilter, filterGroup, parent, - primaryFiltersSlug: this.primaryFiltersSlug, + primaryFiltersSlug: this.primaryFiltersSlug(), }); - this.filterService.updateFiltersInLocalStorage(this.fileId); + this.#filterService.updateFiltersInLocalStorage(this.fileId()); } deactivatePrimaryFilters() { - this.filterService.deactivateFilters({ primaryFiltersSlug: this.primaryFiltersSlug }); - this.filterService.updateFiltersInLocalStorage(this.fileId); + this.#filterService.deactivateFilters({ primaryFiltersSlug: this.primaryFiltersSlug() }); + this.#filterService.updateFiltersInLocalStorage(this.fileId()); } activatePrimaryFilters(): void { - this.filterService.setFilters(this.primaryFiltersSlug, true); - this.filterService.updateFiltersInLocalStorage(this.fileId); + this.#filterService.setFilters(this.primaryFiltersSlug(), true); + this.#filterService.updateFiltersInLocalStorage(this.fileId()); } toggleFilterExpanded(nestedFilter: INestedFilter): void { // eslint-disable-next-line no-param-reassign nestedFilter.expanded = !nestedFilter.expanded; - this.filterService.refresh(); + this.#filterService.refresh(); } } diff --git a/src/lib/filtering/popup-filter/popup-filter.component.html b/src/lib/filtering/popup-filter/popup-filter.component.html index 98b134f..2d71e29 100644 --- a/src/lib/filtering/popup-filter/popup-filter.component.html +++ b/src/lib/filtering/popup-filter/popup-filter.component.html @@ -11,6 +11,7 @@ buttonId="{{ primaryGroup.slug }}" > } + @if (!primaryGroup.icon) { } + pruningTranslationLoaderFactory(pathPrefix), }, + { + provide: MissingTranslationHandler, + useClass: IqserMissingTranslationHandler, + }, ], }; } diff --git a/src/lib/translations/missing-translations-handler.ts b/src/lib/translations/missing-translations-handler.ts new file mode 100644 index 0000000..e78c45e --- /dev/null +++ b/src/lib/translations/missing-translations-handler.ts @@ -0,0 +1,8 @@ +import { MissingTranslationHandler, MissingTranslationHandlerParams } from '@ngx-translate/core'; + +export class IqserMissingTranslationHandler implements MissingTranslationHandler { + handle(params: MissingTranslationHandlerParams) { + const missingKey = params.key; + return `?${missingKey}?`; + } +}