src/app/core/basic-datatypes/entity/entity-block/entity-block.component.ts
Display an inline block representing an entity.
| changeDetection | ChangeDetectionStrategy.OnPush |
| selector | app-entity-block |
| imports |
FaDynamicIconComponent
TemplateTooltipDirective
DisplayImgComponent
EntityFieldViewComponent
MatProgressSpinnerModule
|
| styleUrls | ./entity-block.component.scss |
| templateUrl | ./entity-block.component.html |
Properties |
Methods |
Inputs |
| entity | |
Type : Entity
|
|
|
The entity to display directly. Takes precedence over entityId. |
|
| entityId | |
Type : string
|
|
|
If entity is not set, entityId (with prefix) is used to load the entity. |
|
| linkDisabled | |
Default value : false
|
|
| showDetailsPage |
showDetailsPage()
|
|
Returns :
void
|
| entityBlockConfig |
Type : unknown
|
Default value : computed(() => {
return this.entityResource.value()?.getConstructor()
?.toBlockDetailsAttributes;
})
|
| entityIcon |
Type : unknown
|
Default value : computed(() => {
return this.entityResource.value()?.getConstructor()?.icon || "diamond";
})
|
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
} from "@angular/core";
import { Router } from "@angular/router";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { DisplayImgComponent } from "../../../../features/file/display-img/display-img.component";
import { FaDynamicIconComponent } from "../../../common-components/fa-dynamic-icon/fa-dynamic-icon.component";
import { TemplateTooltipDirective } from "../../../common-components/template-tooltip/template-tooltip.directive";
import { DynamicComponent } from "../../../config/dynamic-components/dynamic-component.decorator";
import { EntityFieldViewComponent } from "../../../entity/entity-field-view/entity-field-view.component";
import { EntityMapperService } from "../../../entity/entity-mapper/entity-mapper.service";
import { Entity } from "../../../entity/model/entity";
import { Logging } from "../../../logging/logging.service";
import { resourceWithRetention } from "../../../../utils/resourceWithRetention";
/**
* Display an inline block representing an entity.
*/
@DynamicComponent("EntityBlock")
@Component({
selector: "app-entity-block",
templateUrl: "./entity-block.component.html",
styleUrls: ["./entity-block.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
FaDynamicIconComponent,
TemplateTooltipDirective,
DisplayImgComponent,
EntityFieldViewComponent,
MatProgressSpinnerModule,
],
})
export class EntityBlockComponent {
private entityMapper = inject(EntityMapperService);
private router = inject(Router);
/** The entity to display directly. Takes precedence over entityId. */
entity = input<Entity>();
/** If entity is not set, entityId (with prefix) is used to load the entity. */
entityId = input<string>();
linkDisabled = input(false);
entityResource = resourceWithRetention({
params: () => ({ entity: this.entity(), entityId: this.entityId() }),
loader: async ({ params: { entity, entityId } }) => {
if (entity) return entity;
if (!entityId) return undefined;
try {
return await this.entityMapper.load(
Entity.extractTypeFromId(entityId),
entityId,
);
} catch (e) {
Logging.debug("[DISPLAY_ENTITY] Could not find entity.", entityId, e);
return undefined;
}
},
});
/**
* True during initial loading when no entity value is available yet.
* Otherwise, we want to use the previous value through the resource's retention.
*/
initialLoading = computed(
() => this.entityResource.isLoading() && !this.entityResource.value(),
);
entityBlockConfig = computed(() => {
return this.entityResource.value()?.getConstructor()
?.toBlockDetailsAttributes;
});
entityIcon = computed(() => {
return this.entityResource.value()?.getConstructor()?.icon || "diamond";
});
entityColor = computed(() => {
const entity = this.entityResource.value();
if (!entity) return undefined;
const colorConfig = entity.getConstructor().color;
if (!colorConfig) return undefined;
return Entity.getColorWithConditions(entity);
});
showDetailsPage() {
const entity = this.entityResource.value();
if (this.linkDisabled() || !entity) {
return;
}
this.router.navigate([entity.getConstructor().route, entity.getId(true)]);
}
}
@if (initialLoading()) {
<span class="block-height">
<mat-spinner diameter="16" class="inline-spinner"></mat-spinner>
</span>
} @else {
<span
class="block-height truncate-text"
[class.clickable]="!linkDisabled()"
[class.inactive]="!entityResource.value()?.isActive"
(click)="showDetailsPage()"
[appTemplateTooltip]="tooltip"
[tooltipDisabled]="!entityBlockConfig()"
[title]="entityResource.value()?.toString()"
>
<app-fa-dynamic-icon
[icon]="entityIcon()"
[style.color]="entityColor()"
class="margin-right-small"
></app-fa-dynamic-icon>
{{ entityResource.value()?.toString() }}
</span>
}
<!-- Tooltip on Hover -->
<ng-template #tooltip>
<div class="tooltip-container">
@if (entityBlockConfig()?.image) {
<app-display-img
class="tooltip-photo"
[entity]="entityResource.value()"
[imgProperty]="entityBlockConfig()?.image"
></app-display-img>
}
<div>
<!-- Font-weight is applied as style to override the default -->
<h3 style="font-weight: bold">
@if (entityBlockConfig()?.title) {
<app-entity-field-view
[entity]="entityResource.value()"
[field]="entityBlockConfig()?.title"
></app-entity-field-view>
} @else {
{{ entityResource.value()?.toString() }}
}
</h3>
@for (field of entityBlockConfig()?.fields; track field) {
<app-entity-field-view
[entity]="entityResource.value()"
[field]="field"
></app-entity-field-view>
}
</div>
</div>
</ng-template>
./entity-block.component.scss
@use "variables/sizes";
@use "variables/colors";
.block-height {
line-height: sizes.$icon-block;
}
.inactive {
color: colors.$inactive;
}
.inline-spinner {
display: inline-block;
vertical-align: middle;
}
.tooltip-container {
padding: 0.5em;
display: flex;
flex-direction: row;
gap: 2em;
align-items: center;
}
.tooltip-photo ::ng-deep img {
width: 80px;
height: 80px;
object-fit: cover;
overflow: hidden;
}