Angular 19 component library for notifications & communication: - ntf-bell: notification bell with badge count and shake animation - ntf-feed / ntf-feed-item: real-time notification feed with grouping - ntf-center: full notification center with category filter tabs - ntf-inbox / ntf-inbox-item: two-column inbox with search and detail - ntf-comment / ntf-thread: comment threads with replies and reactions - ntf-mention-input: text input with @mention autocomplete - ntf-empty-state: empty state placeholder - ntf-item-def: custom template directive for notification items Includes signal-based services, SCSS design tokens with dark mode, utility functions, and full TypeScript type definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
92 lines
3.2 KiB
HTML
92 lines
3.2 KiB
HTML
<div class="ntf-comment">
|
|
<!-- Avatar -->
|
|
<div class="ntf-comment__avatar">
|
|
@if (comment().authorAvatar) {
|
|
<img [src]="comment().authorAvatar" [alt]="comment().authorName" class="ntf-comment__avatar-img" />
|
|
} @else {
|
|
<div class="ntf-comment__avatar-placeholder">
|
|
{{ comment().authorName.charAt(0).toUpperCase() }}
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<!-- Body -->
|
|
<div class="ntf-comment__body">
|
|
<!-- Header -->
|
|
<div class="ntf-comment__header">
|
|
<span class="ntf-comment__author">{{ comment().authorName }}</span>
|
|
<span class="ntf-comment__time">{{ timeDisplay() }}</span>
|
|
@if (editedDisplay()) {
|
|
<span class="ntf-comment__edited">({{ editedDisplay() }})</span>
|
|
}
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
@if (isEditing()) {
|
|
<div class="ntf-comment__edit">
|
|
<textarea
|
|
class="ntf-comment__edit-input"
|
|
[ngModel]="editContent()"
|
|
(ngModelChange)="editContent.set($event)"
|
|
rows="3"
|
|
></textarea>
|
|
<div class="ntf-comment__edit-actions">
|
|
<button class="ntf-comment__edit-btn ntf-comment__edit-btn--cancel" (click)="cancelEdit()">
|
|
Cancel
|
|
</button>
|
|
<button class="ntf-comment__edit-btn ntf-comment__edit-btn--save" (click)="saveEdit()">
|
|
Save
|
|
</button>
|
|
</div>
|
|
</div>
|
|
} @else {
|
|
<div class="ntf-comment__content" [innerHTML]="comment().content"></div>
|
|
}
|
|
|
|
<!-- Reactions -->
|
|
@if (comment().reactions?.length && allowReactions()) {
|
|
<div class="ntf-comment__reactions">
|
|
@for (reaction of comment().reactions; track reaction.emoji) {
|
|
<button
|
|
class="ntf-comment__reaction"
|
|
[class.ntf-comment__reaction--active]="reaction.reacted"
|
|
(click)="onReact(reaction.emoji)"
|
|
>
|
|
<span>{{ reaction.emoji }}</span>
|
|
<span class="ntf-comment__reaction-count">{{ reaction.count }}</span>
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<!-- Footer actions -->
|
|
@if (!isEditing()) {
|
|
<div class="ntf-comment__footer">
|
|
<button class="ntf-comment__footer-btn" (click)="onReply()">Reply</button>
|
|
@if (allowReactions()) {
|
|
<button class="ntf-comment__footer-btn" (click)="onReact('👍')">React</button>
|
|
}
|
|
@if (isOwner()) {
|
|
<div class="ntf-comment__more">
|
|
<button class="ntf-comment__footer-btn" (click)="toggleActions()">
|
|
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14">
|
|
<circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/>
|
|
</svg>
|
|
</button>
|
|
@if (showActions()) {
|
|
<div class="ntf-comment__more-menu">
|
|
@if (allowEdit()) {
|
|
<button class="ntf-comment__more-item" (click)="startEdit()">Edit</button>
|
|
}
|
|
@if (allowDelete()) {
|
|
<button class="ntf-comment__more-item ntf-comment__more-item--danger" (click)="onDelete()">Delete</button>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|