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:
parent
d1acf39a22
commit
a9d788b319
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user