Group channels by type with toggleable collapsed view in ChannelListComponent: added grouping logic, collapsed state management, updated UI with group titles, and adjusted styles for indentation.

This commit is contained in:
akastijn 2026-08-01 21:46:02 +02:00
parent d1acf39a22
commit a9d788b319
3 changed files with 62 additions and 14 deletions

View File

@ -1,19 +1,26 @@
<div class="channel-list">
<p>Channels</p>
@for (channel of channels(); track channel.type + channel.name) {
<div class="channel"
[class.selected]="selectedChannel()?.name === channel.name && selectedChannel()?.type === channel.type"
(click)="selectedChannelChange.emit(channel)">
<span class="channel-name">
@for (segment of parseColoredName(getDisplayName(channel)); track $index) {
<span [style.color]="segment.color">{{ segment.text }}</span>
}
</span>
@if (channel.unreadMessages > 0) {
<div class="notification">
<svg-circle></svg-circle>
@for (group of groupedChannels(); track group.type) {
<p class="channel-group-title" (click)="toggleGroup(group.type)">
{{ group.type }} {{ collapsedGroups().has(group.type) ? '▼' : '▲' }}
</p>
@if (!collapsedGroups().has(group.type)) {
@for (channel of group.channels; track channel.name) {
<div class="channel indented"
[class.selected]="selectedChannel()?.name === channel.name && selectedChannel()?.type === channel.type"
(click)="selectedChannelChange.emit(channel)">
<span class="channel-name">
@for (segment of parseColoredName(getDisplayName(channel)); track $index) {
<span [style.color]="segment.color">{{ segment.text }}</span>
}
</span>
@if (channel.unreadMessages > 0) {
<div class="notification">
<svg-circle></svg-circle>
</div>
}
</div>
}
</div>
}
}
</div>

View File

@ -30,4 +30,18 @@
color: #3b82f6;
}
}
.channel.indented {
padding-left: 40px;
}
.channel-group-title {
padding: 10px 20px;
color: #888;
font-size: 0.8rem;
font-weight: bold;
text-transform: uppercase;
cursor: pointer;
user-select: none;
}
}

View File

@ -1,4 +1,4 @@
import {Component, effect, inject, input, output} from '@angular/core';
import {Component, computed, effect, inject, input, output, signal} from '@angular/core';
import {ChatChannel} from '@pages/altitude/chat/objects/chat-channel.object';
import {CircleSvgComponent} from '@pages/altitude/chat/svg/circle.svg';
import {ChatService} from '@pages/altitude/chat/service/chat.service';
@ -22,6 +22,33 @@ export class ChannelListComponent {
public readonly selectedChannel = input.required<ChatChannel | null>();
public readonly selectedChannelChange = output<ChatChannel>();
protected readonly collapsedGroups = signal<Set<string>>(new Set());
protected toggleGroup(type: string) {
const next = new Set(this.collapsedGroups());
if (next.has(type)) {
next.delete(type);
} else {
next.add(type);
}
this.collapsedGroups.set(next);
}
protected readonly groupedChannels = computed(() => {
const channels = this.channels();
const groups: { type: ChatChannel['type'], channels: ChatChannel[] }[] = [];
const types: ChatChannel['type'][] = ['SERVER', 'DM', 'PARTY', 'GAC'];
for (const type of types) {
const typeChannels = channels.filter(c => c.type === type);
if (typeChannels.length > 0) {
groups.push({type, channels: typeChannels});
}
}
return groups;
});
constructor() {
effect(() => {
const chatChannels = this.channels();