Split chat component into smaller components
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
<!-- eslint-disable @angular-eslint/template/button-has-type, @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus, @angular-eslint/template/prefer-ngsrc -->
|
||||
@if (lightboxAttachment()) {
|
||||
<div
|
||||
class="fixed inset-0 z-[100] flex items-center justify-center bg-black/80 backdrop-blur-sm"
|
||||
(click)="closeLightbox()"
|
||||
(contextmenu)="openImageContextMenu($event, lightboxAttachment()!)"
|
||||
(keydown.escape)="closeLightbox()"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="relative max-h-[90vh] max-w-[90vw]"
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<img
|
||||
[src]="lightboxAttachment()!.objectUrl"
|
||||
[alt]="lightboxAttachment()!.filename"
|
||||
class="max-h-[90vh] max-w-[90vw] rounded-lg object-contain shadow-2xl"
|
||||
(contextmenu)="openImageContextMenu($event, lightboxAttachment()!); $event.stopPropagation()"
|
||||
/>
|
||||
<div class="absolute right-3 top-3 flex gap-2">
|
||||
<button
|
||||
(click)="downloadAttachment(lightboxAttachment()!)"
|
||||
class="rounded-lg bg-black/60 p-2 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
title="Download"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideDownload"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
(click)="closeLightbox()"
|
||||
class="rounded-lg bg-black/60 p-2 text-white backdrop-blur-sm transition-colors hover:bg-black/80"
|
||||
title="Close"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideX"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="absolute bottom-3 left-3 right-3 flex items-center justify-between">
|
||||
<div class="rounded-lg bg-black/60 px-3 py-1.5 backdrop-blur-sm">
|
||||
<span class="text-sm text-white">{{ lightboxAttachment()!.filename }}</span>
|
||||
<span class="ml-2 text-xs text-white/60">{{ formatBytes(lightboxAttachment()!.size) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (imageContextMenu()) {
|
||||
<app-context-menu
|
||||
[x]="imageContextMenu()!.positionX"
|
||||
[y]="imageContextMenu()!.positionY"
|
||||
(closed)="closeImageContextMenu()"
|
||||
>
|
||||
<button
|
||||
(click)="copyImageToClipboard(imageContextMenu()!.attachment); closeImageContextMenu()"
|
||||
class="context-menu-item-icon"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideCopy"
|
||||
class="h-4 w-4 text-muted-foreground"
|
||||
/>
|
||||
Copy Image
|
||||
</button>
|
||||
<button
|
||||
(click)="downloadAttachment(imageContextMenu()!.attachment); closeImageContextMenu()"
|
||||
class="context-menu-item-icon"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideDownload"
|
||||
class="h-4 w-4 text-muted-foreground"
|
||||
/>
|
||||
Save Image
|
||||
</button>
|
||||
</app-context-menu>
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
output
|
||||
} from '@angular/core';
|
||||
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||
import {
|
||||
lucideCopy,
|
||||
lucideDownload,
|
||||
lucideX
|
||||
} from '@ng-icons/lucide';
|
||||
import { Attachment } from '../../../../../core/services/attachment.service';
|
||||
import { ContextMenuComponent } from '../../../../../shared';
|
||||
import { ChatMessageImageContextMenuEvent } from '../../models/chat-messages.models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chat-message-overlays',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
NgIcon,
|
||||
ContextMenuComponent
|
||||
],
|
||||
viewProviders: [
|
||||
provideIcons({
|
||||
lucideCopy,
|
||||
lucideDownload,
|
||||
lucideX
|
||||
})
|
||||
],
|
||||
templateUrl: './chat-message-overlays.component.html',
|
||||
host: {
|
||||
style: 'display: contents;'
|
||||
}
|
||||
})
|
||||
export class ChatMessageOverlaysComponent {
|
||||
readonly lightboxAttachment = input<Attachment | null>(null);
|
||||
readonly imageContextMenu = input<ChatMessageImageContextMenuEvent | null>(null);
|
||||
|
||||
readonly lightboxClosed = output();
|
||||
readonly contextMenuClosed = output();
|
||||
readonly downloadRequested = output<Attachment>();
|
||||
readonly copyRequested = output<Attachment>();
|
||||
readonly imageContextMenuRequested = output<ChatMessageImageContextMenuEvent>();
|
||||
|
||||
closeLightbox(): void {
|
||||
this.lightboxClosed.emit();
|
||||
}
|
||||
|
||||
closeImageContextMenu(): void {
|
||||
this.contextMenuClosed.emit();
|
||||
}
|
||||
|
||||
downloadAttachment(attachment: Attachment): void {
|
||||
this.downloadRequested.emit(attachment);
|
||||
}
|
||||
|
||||
copyImageToClipboard(attachment: Attachment): void {
|
||||
this.copyRequested.emit(attachment);
|
||||
}
|
||||
|
||||
openImageContextMenu(event: MouseEvent, attachment: Attachment): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.imageContextMenuRequested.emit({
|
||||
positionX: event.clientX,
|
||||
positionY: event.clientY,
|
||||
attachment
|
||||
});
|
||||
}
|
||||
|
||||
formatBytes(bytes: number): string {
|
||||
const units = [
|
||||
'B',
|
||||
'KB',
|
||||
'MB',
|
||||
'GB'
|
||||
];
|
||||
|
||||
let size = bytes;
|
||||
let unitIndex = 0;
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user