Compare commits
8 Commits
master
...
release-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3909614988 | ||
|
|
b8ba2191f7 | ||
|
|
25f63ef7c6 | ||
|
|
efd7e2a085 | ||
|
|
f685955f8f | ||
|
|
7edde2a01f | ||
|
|
e6e0686794 | ||
|
|
43bfeb4e1e |
@ -159,10 +159,10 @@ section.settings {
|
|||||||
transition:
|
transition:
|
||||||
width ease-in-out 0.2s,
|
width ease-in-out 0.2s,
|
||||||
min-width ease-in-out 0.2s;
|
min-width ease-in-out 0.2s;
|
||||||
|
@include common-mixins.scroll-bar;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@include common-mixins.scroll-bar;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed-wrapper {
|
.collapsed-wrapper {
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
.mat-mdc-menu-item {
|
.mat-mdc-menu-item {
|
||||||
font-size: var(--iqser-font-size);
|
font-size: var(--iqser-font-size);
|
||||||
color: var(--iqser-text);
|
color: var(--iqser-text);
|
||||||
padding: 0 26px 0 8px;
|
padding: 0 26px 0 8px !important;
|
||||||
margin: var(--iqser-menu-item-margin);
|
margin: var(--iqser-menu-item-margin);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
width: -webkit-fill-available;
|
width: -webkit-fill-available;
|
||||||
@ -58,7 +58,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.padding-left {
|
&.padding-left {
|
||||||
padding-left: 56px;
|
padding-left: 56px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @angular-eslint/prefer-on-push-component-change-detection */
|
/* eslint-disable @angular-eslint/prefer-on-push-component-change-detection */
|
||||||
import { Component, effect, ElementRef, Input, OnDestroy, OnInit, viewChild } from '@angular/core';
|
import { Component, effect, ElementRef, HostListener, Input, OnDestroy, OnInit, viewChild } from '@angular/core';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { HelpModeService } from '../help-mode.service';
|
import { HelpModeService } from '../help-mode.service';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
@ -26,9 +26,9 @@ export class HelpButtonComponent implements OnInit, OnDestroy {
|
|||||||
effect(() => {
|
effect(() => {
|
||||||
if (this.helpModeService.isHelpModeActive()) {
|
if (this.helpModeService.isHelpModeActive()) {
|
||||||
this.#helpModeHasBeenActivated = true;
|
this.#helpModeHasBeenActivated = true;
|
||||||
this.#applyActiveButtonStyles();
|
setTimeout(() => this.#applyActiveButtonStyles(), 300);
|
||||||
} else if (this.#helpModeHasBeenActivated) {
|
} else if (this.#helpModeHasBeenActivated) {
|
||||||
this.#applyInactiveButtonStyles();
|
setTimeout(() => this.#applyInactiveButtonStyles(), 300);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -42,6 +42,15 @@ export class HelpButtonComponent implements OnInit, OnDestroy {
|
|||||||
return `help-mode-button${this.dialogButton ? '-dialog' : ''}`;
|
return `help-mode-button${this.dialogButton ? '-dialog' : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get currentComponentRect() {
|
||||||
|
return this._elementRef.nativeElement.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize() {
|
||||||
|
if (this.helpModeService.isHelpModeActive()) this.#applyActiveButtonStyles();
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.dialogButton) {
|
if (this.dialogButton) {
|
||||||
const defaultButton = document.getElementById('help-mode-button') as HTMLElement;
|
const defaultButton = document.getElementById('help-mode-button') as HTMLElement;
|
||||||
@ -53,8 +62,11 @@ export class HelpButtonComponent implements OnInit, OnDestroy {
|
|||||||
if (this.dialogButton) {
|
if (this.dialogButton) {
|
||||||
const defaultButton = document.getElementById('help-mode-button') as HTMLElement;
|
const defaultButton = document.getElementById('help-mode-button') as HTMLElement;
|
||||||
defaultButton.style.removeProperty('z-index');
|
defaultButton.style.removeProperty('z-index');
|
||||||
|
|
||||||
if (!this.helpModeService.isHelpModeActive()) {
|
if (!this.helpModeService.isHelpModeActive()) {
|
||||||
document.querySelectorAll('iqser-help-button')[this.dialogButton ? 1 : 0]?.removeChild(this.helpModeButton().nativeElement);
|
const helpButtonElement = document.querySelectorAll('iqser-help-button')[this.dialogButton ? 1 : 0];
|
||||||
|
if (helpButtonElement.contains(this.helpModeButton().nativeElement))
|
||||||
|
helpButtonElement?.removeChild(this.helpModeButton().nativeElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,23 +80,17 @@ export class HelpButtonComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#applyActiveButtonStyles() {
|
#applyActiveButtonStyles() {
|
||||||
const currentComponent = this._elementRef.nativeElement;
|
this.helpModeButton().nativeElement.style.setProperty('position', 'absolute');
|
||||||
const currentComponentRect = currentComponent.getBoundingClientRect();
|
this.helpModeButton().nativeElement.style.setProperty('top', `${this.currentComponentRect.top}px`);
|
||||||
setTimeout(() => {
|
this.helpModeButton().nativeElement.style.setProperty('left', `${this.currentComponentRect.left}px`);
|
||||||
this.helpModeButton().nativeElement.style.setProperty('position', 'absolute');
|
document.body.appendChild(this.helpModeButton().nativeElement);
|
||||||
this.helpModeButton().nativeElement.style.setProperty('top', `${currentComponentRect.top}px`);
|
|
||||||
this.helpModeButton().nativeElement.style.setProperty('left', `${currentComponentRect.left}px`);
|
|
||||||
document.body.appendChild(this.helpModeButton().nativeElement);
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#applyInactiveButtonStyles() {
|
#applyInactiveButtonStyles() {
|
||||||
setTimeout(() => {
|
this.helpModeButton().nativeElement.style.setProperty('position', 'relative');
|
||||||
this.helpModeButton().nativeElement.style.setProperty('position', 'relative');
|
this.helpModeButton().nativeElement.style.setProperty('top', 'unset');
|
||||||
this.helpModeButton().nativeElement.style.setProperty('top', 'unset');
|
this.helpModeButton().nativeElement.style.setProperty('left', 'unset');
|
||||||
this.helpModeButton().nativeElement.style.setProperty('left', 'unset');
|
document.body.removeChild(this.helpModeButton().nativeElement);
|
||||||
document.body.removeChild(this.helpModeButton().nativeElement);
|
document.querySelectorAll('iqser-help-button')[this.dialogButton ? 1 : 0]?.appendChild(this.helpModeButton().nativeElement);
|
||||||
document.querySelectorAll('iqser-help-button')[this.dialogButton ? 1 : 0]?.appendChild(this.helpModeButton().nativeElement);
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { BaseHeaderConfig } from './base-config.model';
|
import { BaseHeaderConfig } from './base-config.model';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { OverlappingElement } from '../../../help-mode';
|
|
||||||
|
|
||||||
export interface ActionConfig extends BaseHeaderConfig {
|
export interface ActionConfig extends BaseHeaderConfig {
|
||||||
readonly action: ($event: MouseEvent) => void;
|
readonly action: ($event: MouseEvent) => void;
|
||||||
readonly helpModeKey?: string;
|
readonly helpModeKey?: string;
|
||||||
readonly disabled$?: Observable<boolean>;
|
readonly disabled$?: Observable<boolean>;
|
||||||
|
readonly disabled?: boolean;
|
||||||
|
readonly disableStopPropagation?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
@if (pageLabel) {
|
@if (pageLabel()) {
|
||||||
<div class="breadcrumb">{{ pageLabel }}</div>
|
<div class="breadcrumb">{{ pageLabel() }}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (filters$ | async; as filters) {
|
@if (filters$ | async; as filters) {
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<ng-content select="[slot=beforeFilters]"></ng-content>
|
<ng-content select="[slot=beforeFilters]"></ng-content>
|
||||||
@if (filters.length && searchPosition !== searchPositions.beforeFilters) {
|
@if (filters.length && searchPosition() !== searchPositions.beforeFilters) {
|
||||||
<div class="text-muted" translate="filters.filter-by"></div>
|
<div class="text-muted" translate="filters.filter-by"></div>
|
||||||
}
|
}
|
||||||
@if (searchPosition === searchPositions.beforeFilters) {
|
@if (searchPosition() === searchPositions.beforeFilters) {
|
||||||
<ng-container [ngTemplateOutlet]="searchBar"></ng-container>
|
<ng-container [ngTemplateOutlet]="searchBar"></ng-container>
|
||||||
}
|
}
|
||||||
@for (filter of filters; track trackByLabel($index, filter)) {
|
@for (filter of filters; track trackByLabel($index, filter)) {
|
||||||
@if (!filter.hide) {
|
@if (!filter.hide) {
|
||||||
<iqser-popup-filter [primaryFiltersSlug]="filter.slug" [attr.help-mode-key]="filterHelpModeKey"></iqser-popup-filter>
|
<iqser-popup-filter [primaryFiltersSlug]="filter.slug" [attr.help-mode-key]="filterHelpModeKey()"></iqser-popup-filter>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@for (filter$ of filterService.singleFilters; track filter$) {
|
@for (filter$ of filterService.singleFilters; track filter$) {
|
||||||
@ -22,13 +22,13 @@
|
|||||||
<iqser-single-filter [filter]="filter"></iqser-single-filter>
|
<iqser-single-filter [filter]="filter"></iqser-single-filter>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if (searchPosition === searchPositions.afterFilters) {
|
@if (searchPosition() === searchPositions.afterFilters) {
|
||||||
<ng-container [ngTemplateOutlet]="searchBar"></ng-container>
|
<ng-container [ngTemplateOutlet]="searchBar"></ng-container>
|
||||||
}
|
}
|
||||||
@if (!hideResetButton && (showResetFilters$ | async) === true) {
|
@if (!hideResetButton() && (showResetFilters$ | async) === true) {
|
||||||
<div
|
<div
|
||||||
(click)="resetFilters()"
|
(click)="resetFilters()"
|
||||||
[attr.help-mode-key]="'filter_' + helpModeKey + '_list'"
|
[attr.help-mode-key]="'filter_' + helpModeKey() + '_list'"
|
||||||
class="reset-filters"
|
class="reset-filters"
|
||||||
translate="reset-filters"
|
translate="reset-filters"
|
||||||
></div>
|
></div>
|
||||||
@ -36,17 +36,17 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (showCloseButton || actionConfigs || buttonConfigs || viewModeSelection) {
|
@if (showCloseButton() || actionConfigs() || buttonConfigs() || viewModeSelection()) {
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
@if (searchPosition === searchPositions.withActions) {
|
@if (searchPosition() === searchPositions.withActions) {
|
||||||
<ng-container [ngTemplateOutlet]="searchBar"></ng-container>
|
<ng-container [ngTemplateOutlet]="searchBar"></ng-container>
|
||||||
}
|
}
|
||||||
<ng-container [ngTemplateOutlet]="viewModeSelection"></ng-container>
|
<ng-container [ngTemplateOutlet]="viewModeSelection()"></ng-container>
|
||||||
@for (config of buttonConfigs; track trackByLabel($index, config)) {
|
@for (config of buttonConfigs(); track trackByLabel($index, config)) {
|
||||||
@if (!config.hide) {
|
@if (!config.hide) {
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="config.action($event)"
|
(action)="config.action($event)"
|
||||||
[buttonId]="config.label.replace('.', '-')"
|
[buttonId]="(config.label | translate).replace('.', '-')"
|
||||||
[icon]="config.icon"
|
[icon]="config.icon"
|
||||||
[label]="config.label | translate"
|
[label]="config.label | translate"
|
||||||
[type]="config.type"
|
[type]="config.type"
|
||||||
@ -55,25 +55,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
@for (config of actionConfigs; track trackByLabel($index, config)) {
|
@for (config of actionConfigs(); track trackByLabel($index, config)) {
|
||||||
@if (!config.hide) {
|
@if (!config.hide) {
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="config.action($event)"
|
(action)="config.action($event)"
|
||||||
[buttonId]="config.id"
|
[buttonId]="config.id"
|
||||||
[disabled]="config.disabled$ && (config.disabled$ | async)"
|
[disabled]="config.disabled"
|
||||||
[icon]="config.icon"
|
[icon]="config.icon"
|
||||||
[tooltip]="config.label"
|
[tooltip]="config.label | translate"
|
||||||
[attr.help-mode-key]="config.helpModeKey"
|
[attr.help-mode-key]="config.helpModeKey"
|
||||||
|
[iqserDisableStopPropagation]="config.disableStopPropagation"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<!-- Extra custom actions here -->
|
<!-- Extra custom actions here -->
|
||||||
<ng-content select="[slot=right]"></ng-content>
|
<ng-content select="[slot=right]"></ng-content>
|
||||||
@if (showCloseButton) {
|
@if (showCloseButton()) {
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
buttonId="close-view-btn"
|
buttonId="close-view-btn"
|
||||||
(action)="closeAction.emit()"
|
(action)="closeAction.emit()"
|
||||||
[class.ml-6]="actionConfigs"
|
[class.ml-6]="actionConfigs()"
|
||||||
[icon]="'iqser:close'"
|
[icon]="'iqser:close'"
|
||||||
[attr.help-mode-key]="'close_dossier'"
|
[attr.help-mode-key]="'close_dossier'"
|
||||||
[tooltip]="'common.close' | translate"
|
[tooltip]="'common.close' | translate"
|
||||||
@ -85,14 +86,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #searchBar>
|
<ng-template #searchBar>
|
||||||
@if (searchPlaceholder && searchService) {
|
@if (searchPlaceholder() && searchService) {
|
||||||
<iqser-input-with-action
|
<iqser-input-with-action
|
||||||
[inputId]="searchInputId"
|
[inputId]="searchInputId()"
|
||||||
(valueChange)="searchService.searchValue = $event"
|
(valueChange)="searchService.searchValue = $event"
|
||||||
[class.mr-8]="searchPosition === searchPositions.beforeFilters"
|
[class.mr-8]="searchPosition() === searchPositions.beforeFilters"
|
||||||
[placeholder]="searchPlaceholder"
|
[placeholder]="searchPlaceholder()"
|
||||||
[value]="searchService.valueChanges$ | async"
|
[value]="searchService.valueChanges$ | async"
|
||||||
[width]="searchWidth"
|
[width]="searchWidth()"
|
||||||
></iqser-input-with-action>
|
></iqser-input-with-action>
|
||||||
}
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
||||||
import { ChangeDetectionStrategy, Component, EventEmitter, inject, Input, Output, TemplateRef } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, computed, inject, input, output, TemplateRef } from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { combineLatest, Observable, of } from 'rxjs';
|
import { combineLatest, Observable, of } from 'rxjs';
|
||||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||||
import { CircleButtonComponent } from '../../buttons/circle-button/circle-button.component';
|
import { CircleButtonComponent } from '../../buttons';
|
||||||
import { IconButtonComponent } from '../../buttons/icon-button/icon-button.component';
|
import { IconButtonComponent } from '../../buttons';
|
||||||
import { IconButtonTypes } from '../../buttons/types/icon-button.type';
|
import { IconButtonTypes } from '../../buttons';
|
||||||
import { FilterService } from '../../filtering/filter.service';
|
import { FilterService } from '../../filtering';
|
||||||
import { IqserFiltersModule } from '../../filtering/filters.module';
|
import { IqserFiltersModule } from '../../filtering';
|
||||||
import { PopupFilterComponent } from '../../filtering/popup-filter/popup-filter.component';
|
import { PopupFilterComponent } from '../../filtering';
|
||||||
import { InputWithActionComponent } from '../../inputs/input-with-action/input-with-action.component';
|
import { InputWithActionComponent } from '../../inputs/input-with-action/input-with-action.component';
|
||||||
import { SearchService } from '../../search/search.service';
|
import { SearchService } from '../../search';
|
||||||
import { filterEach } from '../../utils/operators';
|
import { filterEach } from '../../utils';
|
||||||
import { List } from '../../utils/types/iqser-types';
|
import { List } from '../../utils';
|
||||||
import { IListable } from '../models/listable';
|
import { IListable } from '../models';
|
||||||
import { ActionConfig, ButtonConfig, SearchPosition, SearchPositions } from './models';
|
import { ActionConfig, ButtonConfig, SearchPosition, SearchPositions } from './models';
|
||||||
|
import { DisableStopPropagationDirective } from '../../directives';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'iqser-page-header',
|
selector: 'iqser-page-header',
|
||||||
@ -31,33 +32,35 @@ import { ActionConfig, ButtonConfig, SearchPosition, SearchPositions } from './m
|
|||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
InputWithActionComponent,
|
InputWithActionComponent,
|
||||||
|
DisableStopPropagationDirective,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PageHeaderComponent<T extends IListable> {
|
export class PageHeaderComponent<T extends IListable> {
|
||||||
readonly searchPositions = SearchPositions;
|
readonly searchPositions = SearchPositions;
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
|
||||||
@Input() pageLabel?: string;
|
|
||||||
@Input() searchInputId?: string;
|
|
||||||
@Input() showCloseButton = false;
|
|
||||||
@Input() hideResetButton = false;
|
|
||||||
@Input() actionConfigs?: List<ActionConfig>;
|
|
||||||
@Input() buttonConfigs?: List<ButtonConfig>;
|
|
||||||
@Input() viewModeSelection?: TemplateRef<unknown>;
|
|
||||||
@Input() searchPlaceholder?: string;
|
|
||||||
@Input() searchWidth?: number | 'full';
|
|
||||||
@Input() helpModeKey?: 'dossier' | 'document';
|
|
||||||
@Input() searchPosition: SearchPosition = SearchPositions.afterFilters;
|
|
||||||
@Output() readonly closeAction = new EventEmitter();
|
|
||||||
readonly filterService = inject(FilterService, { optional: true });
|
readonly filterService = inject(FilterService, { optional: true });
|
||||||
readonly searchService = inject<SearchService<T>>(SearchService<T>, { optional: true });
|
readonly searchService = inject<SearchService<T>>(SearchService<T>, { optional: true });
|
||||||
|
|
||||||
|
readonly pageLabel = input<string>();
|
||||||
|
readonly searchInputId = input<string>();
|
||||||
|
readonly showCloseButton = input(false);
|
||||||
|
readonly hideResetButton = input(false);
|
||||||
|
readonly actionConfigs = input<List<ActionConfig>>();
|
||||||
|
readonly buttonConfigs = input<List<ButtonConfig>>();
|
||||||
|
readonly viewModeSelection = input<TemplateRef<unknown>>();
|
||||||
|
readonly searchPlaceholder = input<string>();
|
||||||
|
readonly searchWidth = input<number | 'full'>();
|
||||||
|
readonly helpModeKey = input<'dossier' | 'document'>();
|
||||||
|
readonly searchPosition = input<SearchPosition>(SearchPositions.afterFilters);
|
||||||
|
readonly closeAction = output();
|
||||||
|
|
||||||
|
readonly filterHelpModeKey = computed(() =>
|
||||||
|
this.helpModeKey() ? (this.helpModeKey() === 'dossier' ? 'filter_dossier_list' : 'filter_documents') : '',
|
||||||
|
);
|
||||||
|
|
||||||
readonly filters$ = this.filterService?.filterGroups$.pipe(filterEach(f => !!f.icon));
|
readonly filters$ = this.filterService?.filterGroups$.pipe(filterEach(f => !!f.icon));
|
||||||
readonly showResetFilters$ = this.#showResetFilters$;
|
readonly showResetFilters$ = this.#showResetFilters$;
|
||||||
|
|
||||||
get filterHelpModeKey() {
|
|
||||||
return this.helpModeKey ? (this.helpModeKey === 'dossier' ? 'filter_dossier_list' : 'filter_documents') : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
get #showResetFilters$(): Observable<boolean> {
|
get #showResetFilters$(): Observable<boolean> {
|
||||||
if (!this.filterService) {
|
if (!this.filterService) {
|
||||||
return of(false);
|
return of(false);
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
:host cdk-virtual-scroll-viewport {
|
:host cdk-virtual-scroll-viewport {
|
||||||
height: calc(100vh - 50px - 31px - var(--iqser-top-bar-height) - 50px);
|
height: calc(100vh - 50px - 31px - var(--iqser-top-bar-height) - 50px);
|
||||||
overflow-y: hidden !important;
|
overflow-y: auto !important;
|
||||||
background-color: var(--iqser-background);
|
background-color: var(--iqser-background);
|
||||||
@include mixins.scroll-bar;
|
@include mixins.scroll-bar;
|
||||||
|
|
||||||
|
|||||||
@ -87,18 +87,17 @@ export class TableComponent<Class extends IListable<PrimaryKey>, PrimaryKey exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _setColumnsWidth(element: HTMLElement) {
|
private _setColumnsWidth(element: HTMLElement) {
|
||||||
let gridTemplateColumnsHover = '';
|
let gridTemplateColumns = '';
|
||||||
if (this.selectionEnabled) {
|
if (this.selectionEnabled) {
|
||||||
gridTemplateColumnsHover += '30px ';
|
gridTemplateColumns += '30px ';
|
||||||
}
|
}
|
||||||
for (const config of this.tableColumnConfigs) {
|
for (const config of this.tableColumnConfigs) {
|
||||||
gridTemplateColumnsHover += `${config.width || '1fr'} `;
|
gridTemplateColumns += `${config.width || '1fr'} `;
|
||||||
}
|
}
|
||||||
gridTemplateColumnsHover += this.emptyColumnWidth || '';
|
gridTemplateColumns += this.emptyColumnWidth || '';
|
||||||
const gridTemplateColumns = `${gridTemplateColumnsHover} ${SCROLLBAR_WIDTH}px`;
|
gridTemplateColumns = `${gridTemplateColumns} ${SCROLLBAR_WIDTH}px`;
|
||||||
|
|
||||||
element.style.setProperty('--gridTemplateColumns', gridTemplateColumns);
|
element.style.setProperty('--gridTemplateColumns', gridTemplateColumns);
|
||||||
element.style.setProperty('--gridTemplateColumnsHover', gridTemplateColumnsHover);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setItemSize(element: HTMLElement) {
|
private _setItemSize(element: HTMLElement) {
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
@if (_user && _user | name: namePipeOptions; as userName) {
|
@if (_user() && _user() | name: namePipeOptions(); as userName) {
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div
|
<div
|
||||||
[className]="colorClass + ' oval ' + size + (hasBorder ? ' border' : '')"
|
[className]="colorClass() + ' oval ' + size() + (hasBorder() ? ' border' : '')"
|
||||||
[matTooltipPosition]="tooltipPosition"
|
[matTooltipPosition]="tooltipPosition()"
|
||||||
[matTooltip]="showTooltip ? userName : ''"
|
[matTooltip]="showTooltip() ? userName : ''"
|
||||||
>
|
>
|
||||||
{{ _user | name: { showInitials: true } }}
|
{{ _user() | name: { showInitials: true } }}
|
||||||
</div>
|
</div>
|
||||||
@if (withName) {
|
@if (withName()) {
|
||||||
<div [class.disabled]="disabled" class="clamp-1 username" id="avatarUsername">
|
<div [class.disabled]="disabled()" class="clamp-1 username" id="avatarUsername">
|
||||||
{{ userName }}
|
{{ userName }}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject, Input, OnChanges, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { IqserUser } from '../../iqser-user.model';
|
import { IqserUser } from '../../iqser-user.model';
|
||||||
import { IqserUserService } from '../../services/iqser-user.service';
|
import { IqserUserService } from '../../services/iqser-user.service';
|
||||||
@ -7,6 +7,7 @@ import { IIqserUser } from '../../types/user.response';
|
|||||||
import { NgIf } from '@angular/common';
|
import { NgIf } from '@angular/common';
|
||||||
import { NamePipe } from '../../name.pipe';
|
import { NamePipe } from '../../name.pipe';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'iqser-initials-avatar',
|
selector: 'iqser-initials-avatar',
|
||||||
@ -16,80 +17,45 @@ import { MatTooltip } from '@angular/material/tooltip';
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgIf, NamePipe, MatTooltip],
|
imports: [NgIf, NamePipe, MatTooltip],
|
||||||
})
|
})
|
||||||
export class InitialsAvatarComponent<Interface extends IIqserUser = IIqserUser, Class extends IqserUser & Interface = IqserUser & Interface>
|
export class InitialsAvatarComponent<
|
||||||
implements OnInit, OnChanges
|
Interface extends IIqserUser = IIqserUser,
|
||||||
{
|
Class extends IqserUser & Interface = IqserUser & Interface,
|
||||||
|
> {
|
||||||
|
readonly #userService = inject<IqserUserService<Interface, Class>>(IqserUserService);
|
||||||
readonly #translateService = inject(TranslateService);
|
readonly #translateService = inject(TranslateService);
|
||||||
@Input() color = 'lightgray';
|
readonly #isSystemUser = computed(() => this._user()?.id?.toLowerCase() === 'system');
|
||||||
@Input() size: 'small' | 'large' = 'small';
|
readonly #users = toSignal(this.#userService.all$);
|
||||||
@Input() withName = false;
|
|
||||||
@Input() showYou = false;
|
|
||||||
@Input() tooltipPosition: 'below' | 'above' = 'above';
|
|
||||||
@Input() defaultValue: string = this.#translateService.instant('initials-avatar.unassigned');
|
|
||||||
@Input() showTooltip = true;
|
|
||||||
colorClass?: string;
|
|
||||||
namePipeOptions?: NamePipeOptions;
|
|
||||||
|
|
||||||
constructor(private readonly _userService: IqserUserService<Interface, Class>) {}
|
readonly color = input('lightgray');
|
||||||
|
readonly size = input<'small' | 'large'>('small');
|
||||||
|
readonly withName = input(false);
|
||||||
|
readonly showYou = input(false);
|
||||||
|
readonly tooltipPosition = input<'below' | 'above'>('above');
|
||||||
|
readonly defaultValue = input<string>(this.#translateService.instant('initials-avatar.unassigned'));
|
||||||
|
readonly showTooltip = input(true);
|
||||||
|
readonly user = input.required<Class | string>();
|
||||||
|
readonly showBorderCondition = input<<T extends Class = Class>(user: T) => boolean>(user => user.isSpecial);
|
||||||
|
|
||||||
_user?: Class;
|
readonly _user = computed(() => {
|
||||||
|
const user = this.user();
|
||||||
@Input()
|
|
||||||
set user(user: Class | string) {
|
|
||||||
if (typeof user === 'string') {
|
if (typeof user === 'string') {
|
||||||
this._user = this._userService.find(user);
|
if (user?.toLowerCase() === 'system') return this.#userService.newSystemUser();
|
||||||
} else {
|
if (!user) return undefined;
|
||||||
this._user = user;
|
return this.#users()?.find(u => u.id === user) ?? this.#userService.newDeletedUser();
|
||||||
}
|
}
|
||||||
}
|
return user;
|
||||||
|
});
|
||||||
get hasBorder(): boolean {
|
readonly isCurrentUser = computed(() => this.#userService.currentUser?.id === this._user()?.id);
|
||||||
return !!this._user && !this.isCurrentUser && this.showBorderCondition(this._user);
|
readonly hasBorder = computed(() => !!this._user() && !this.isCurrentUser() && this.showBorderCondition()(this._user()!));
|
||||||
}
|
readonly disabled = computed(() => !!this._user() && !this.#isSystemUser() && !this._user()?.hasAnyRole);
|
||||||
|
readonly colorClass = computed(() => {
|
||||||
get disabled(): boolean {
|
if (this.isCurrentUser()) return 'primary-white';
|
||||||
return !!this._user && !this.#isSystemUser && !this._user.hasAnyRole;
|
if (this.disabled()) return 'inactive';
|
||||||
}
|
if (this.color().includes('-')) return this.color();
|
||||||
|
if (this.#isSystemUser()) return 'primary-white primary';
|
||||||
get isCurrentUser(): boolean {
|
return `${this.color()}-dark`;
|
||||||
return this._userService.currentUser?.id === this._user?.id;
|
});
|
||||||
}
|
readonly namePipeOptions = computed<NamePipeOptions>(
|
||||||
|
() => ({ showYou: this.showYou(), defaultValue: this.defaultValue() }) as NamePipeOptions,
|
||||||
get #colorClass() {
|
);
|
||||||
if (this.isCurrentUser) {
|
|
||||||
return 'primary-white';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.disabled) {
|
|
||||||
return 'inactive';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.color.includes('-')) {
|
|
||||||
return this.color;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${this.color}-dark`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get #isSystemUser() {
|
|
||||||
return this._user?.id?.toLowerCase() === 'system';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input() showBorderCondition: <T extends Class = Class>(user: T) => boolean = user => user.isSpecial;
|
|
||||||
|
|
||||||
ngOnChanges(): void {
|
|
||||||
if (this.#isSystemUser) {
|
|
||||||
this.colorClass = 'primary-white primary';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.colorClass = this.#colorClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.namePipeOptions = {
|
|
||||||
showYou: this.showYou,
|
|
||||||
defaultValue: this.defaultValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,14 +164,22 @@ export abstract class IqserUserService<
|
|||||||
|
|
||||||
find(id: string): Class | undefined {
|
find(id: string): Class | undefined {
|
||||||
if (id?.toLowerCase() === 'system') {
|
if (id?.toLowerCase() === 'system') {
|
||||||
return new this._entityClass({ username: 'System' }, [], 'system');
|
return this.newSystemUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.find(id) ?? new this._entityClass({ username: 'Deleted User' }, [], 'deleted');
|
return super.find(id) ?? this.newDeletedUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
newSystemUser() {
|
||||||
|
return new this._entityClass({ username: 'System' }, [], 'system');
|
||||||
|
}
|
||||||
|
|
||||||
|
newDeletedUser() {
|
||||||
|
return new this._entityClass({ username: 'Deleted User' }, [], 'deleted');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,4 +5,5 @@ export interface ICreateUserRequest {
|
|||||||
firstName?: string;
|
firstName?: string;
|
||||||
lastName?: string;
|
lastName?: string;
|
||||||
roles?: List;
|
roles?: List;
|
||||||
|
sendSetPasswordMail?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user