> ## Documentation Index
> Fetch the complete documentation index at: https://cometchat-22654f5b-docs-rn-guide-message-privately.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Groups

> A comprehensive Angular component for displaying and managing real-time group lists with extensive customization options

## Overview

The CometChatGroups component displays a real-time, scrollable list of groups with support for search, selection modes, and extensive customization. It provides an integral search functionality, allowing you to locate any specific group swiftly and easily. For each group listed, the component displays the group's name by default, in conjunction with their avatar when available. Furthermore, it includes a group type indicator (public, private, or password-protected) and member count.

The component follows a **Manager-based Architecture** where:

* **GroupsManager** handles SDK request building and pagination
* **Component** manages state, real-time listeners, and UI rendering
* **Templates** allow extensive customization of all UI sections

### Key Features

* **Real-time Updates**: Automatic updates for group member events (join, leave, kick, ban)
* **Flexible Customization**: Extensive template projection for all UI sections
* **Selection Modes**: Support for single and multiple group selection
* **Group Type Indicators**: Visual indicators for public, private, and password-protected groups
* **Search Functionality**: Built-in search with 300ms debouncing
* **Keyboard Navigation**: Full keyboard accessibility with arrow keys and shortcuts (WCAG 2.1 Level AA compliant)
* **Context Menu**: Customizable actions for each group
* **Error Handling**: Comprehensive error handling with custom error views

<Info>
  **Live Preview** — default groups list preview.
  [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-groups-cometchat-groups--default)
</Info>

<iframe src="https://storybook.cometchat.io/angular/iframe.html?id=components-groups-cometchat-groups--default&viewMode=story&shortcuts=false&singleStory=true" className="w-full rounded-xl" loading="lazy" style={{height: "700px", border: "1px solid #e0e0e0"}} title="CometChat Groups — Default" allow="clipboard-write" />

## Keyboard Accessibility

CometChatGroups is fully keyboard accessible and meets WCAG 2.1 Level AA standards. All functionality can be accessed using only the keyboard.

### Keyboard Shortcuts

| Key              | Action                              | Context               |
| ---------------- | ----------------------------------- | --------------------- |
| `Tab`            | Navigate between UI elements        | Global                |
| `Shift + Tab`    | Navigate backwards                  | Global                |
| `↓` (Down Arrow) | Focus next group                    | When list is focused  |
| `↑` (Up Arrow)   | Focus previous group                | When list is focused  |
| `Enter`          | Select/activate focused group       | When group is focused |
| `Space`          | Toggle selection (in multiple mode) | When group is focused |
| `Escape`         | Clear search and reset focus        | When list is focused  |

### Accessibility Features

**ARIA Attributes:**

* `role="list"` on groups container
* `role="listitem"` on each group item
* `aria-label` with group name, type, and member count
* `aria-selected` indicates selected groups
* Proper `tabindex` management (roving tabindex pattern)

**Screen Reader Support:**

* Announces group details when focused
* Announces selection state changes
* Announces group type (public/private/password)
* Semantic HTML structure

**Focus Management:**

* Visible focus indicators (2px border) meeting WCAG contrast requirements
* Focus restoration after interactions
* Roving tabindex for efficient keyboard navigation

**WCAG 2.1 Compliance:**

* ✅ 2.1.1 Keyboard (Level A) - All functionality available via keyboard
* ✅ 2.1.2 No Keyboard Trap (Level A) - Users can navigate away using keyboard
* ✅ 2.4.3 Focus Order (Level A) - Logical focus order
* ✅ 2.4.7 Focus Visible (Level AA) - Visible focus indicators
* ✅ 4.1.2 Name, Role, Value (Level A) - Proper ARIA attributes

## Basic Usage

### Simple Implementation

```typescript expandable theme={null}
import { Component } from '@angular/core';
import { CometChatGroupsComponent } from '@cometchat/chat-uikit-angular';

@Component({
  selector: 'app-groups',
  standalone: true,
  imports: [CometChatGroupsComponent],
  template: `
    <cometchat-groups
      (itemClick)="onGroupClick($event)"
    ></cometchat-groups>
  `
})
export class GroupsComponent {
  onGroupClick(group: any): void {
    console.log('Selected group:', group);
    // Navigate to messages view or handle group selection
  }
}
```

### With Search and Group Type Indicators

```typescript theme={null}
<cometchat-groups
  [hideSearch]="false"
  [hideGroupType]="false"
  (itemClick)="onGroupClick($event)"
  (error)="onError($event)"
></cometchat-groups>
```

## Properties

### Display Control Properties

| Property                    | Type      | Default | Description                                                                                     |
| --------------------------- | --------- | ------- | ----------------------------------------------------------------------------------------------- |
| `hideSearch`                | `boolean` | `false` | Hide the search bar                                                                             |
| `hideError`                 | `boolean` | `false` | Hide error views when errors occur                                                              |
| `hideGroupType`             | `boolean` | `false` | Hide group type icon (public/private/password)                                                  |
| `showScrollbar`             | `boolean` | `false` | Show/hide scrollbar in group list                                                               |
| `disableDefaultContextMenu` | `boolean` | `true`  | When true, prevents the browser's native context menu and shows the custom context menu instead |

### Data Configuration Properties

| Property               | Type                             | Default     | Description                                            |
| ---------------------- | -------------------------------- | ----------- | ------------------------------------------------------ |
| `groupsRequestBuilder` | `CometChat.GroupsRequestBuilder` | `undefined` | Custom request builder for filtering and pagination    |
| `searchRequestBuilder` | `CometChat.GroupsRequestBuilder` | `undefined` | Custom request builder specifically for search queries |
| `activeGroup`          | `CometChat.Group`                | `undefined` | Currently active/highlighted group                     |
| `selectionMode`        | `SelectionMode`                  | `'none'`    | Selection mode: `'none'`, `'single'`, or `'multiple'`  |

### Customization Properties

| Property  | Type                                            | Default     | Description                                     |
| --------- | ----------------------------------------------- | ----------- | ----------------------------------------------- |
| `options` | `(group: CometChat.Group) => CometChatOption[]` | `undefined` | Function to provide custom context menu options |

### Template Properties

| Property       | Type                                        | Default     | Description                                                                    |
| -------------- | ------------------------------------------- | ----------- | ------------------------------------------------------------------------------ |
| `headerView`   | `TemplateRef<any>`                          | `undefined` | Custom template for entire header section                                      |
| `menuView`     | `TemplateRef<any>`                          | `undefined` | Custom template for menu area in the header (e.g., action buttons, 3-dot menu) |
| `loadingView`  | `TemplateRef<any>`                          | `undefined` | Custom template for loading state                                              |
| `emptyView`    | `TemplateRef<any>`                          | `undefined` | Custom template for empty state                                                |
| `errorView`    | `TemplateRef<any>`                          | `undefined` | Custom template for error state                                                |
| `itemView`     | `TemplateRef<{$implicit: CometChat.Group}>` | `undefined` | Custom template for entire group item                                          |
| `leadingView`  | `TemplateRef<{$implicit: CometChat.Group}>` | `undefined` | Custom template for leading section (avatar area)                              |
| `titleView`    | `TemplateRef<{$implicit: CometChat.Group}>` | `undefined` | Custom template for title section                                              |
| `subtitleView` | `TemplateRef<{$implicit: CometChat.Group}>` | `undefined` | Custom template for subtitle section                                           |
| `trailingView` | `TemplateRef<{$implicit: CometChat.Group}>` | `undefined` | Custom template for trailing section (selection controls)                      |

## Events

| Event             | Payload Type                                  | Description                                                |
| ----------------- | --------------------------------------------- | ---------------------------------------------------------- |
| `itemClick`       | `CometChat.Group`                             | Emitted when a group item is clicked                       |
| `select`          | `{group: CometChat.Group, selected: boolean}` | Emitted when a group is selected/deselected                |
| `error`           | `CometChat.CometChatException`                | Emitted when an error occurs                               |
| `selectionChange` | `SelectionState`                              | Emitted when the selection state changes in selection mode |

## Usage Patterns

CometChatGroups supports two usage patterns for communicating the selected group to downstream components.

<Tabs>
  <Tab title="Using Service">
    When a group is selected, `ChatStateService` stores the active group. Downstream components like `cometchat-message-header`, `cometchat-message-list`, and `cometchat-message-composer` automatically subscribe to the change.

    ```typescript expandable theme={null}
    import { Component } from '@angular/core';
    import {
      CometChatGroupsComponent,
      CometChatMessageHeaderComponent,
      CometChatMessageListComponent,
      CometChatMessageComposerComponent,
    } from '@cometchat/chat-uikit-angular';

    @Component({
      selector: 'app-groups-chat',
      standalone: true,
      imports: [
        CometChatGroupsComponent,
        CometChatMessageHeaderComponent,
        CometChatMessageListComponent,
        CometChatMessageComposerComponent,
      ],
      template: `
        <div class="chat-layout">
          <cometchat-groups
            (itemClick)="onGroupClick($event)"
          ></cometchat-groups>

          <div class="chat-panel">
            <!-- Auto-subscribe to ChatStateService active group -->
            <cometchat-message-header></cometchat-message-header>
            <cometchat-message-list></cometchat-message-list>
            <cometchat-message-composer></cometchat-message-composer>
          </div>
        </div>
      `,
    })
    export class GroupsChatComponent {
      onGroupClick(group: any): void {
        // ChatStateService is updated automatically
      }
    }
    ```

    <Tip>
      This is the recommended approach. Selecting a group automatically updates all downstream message components.
    </Tip>
  </Tab>

  <Tab title="Using Props">
    Pass `[group]` directly to downstream components to override `ChatStateService` state. This is useful when you manage group selection yourself.

    ```typescript expandable theme={null}
    import { Component } from '@angular/core';
    import { CometChat } from '@cometchat/chat-sdk-javascript';
    import {
      CometChatGroupsComponent,
      CometChatMessageHeaderComponent,
      CometChatMessageListComponent,
      CometChatMessageComposerComponent,
    } from '@cometchat/chat-uikit-angular';

    @Component({
      selector: 'app-groups-chat',
      standalone: true,
      imports: [
        CometChatGroupsComponent,
        CometChatMessageHeaderComponent,
        CometChatMessageListComponent,
        CometChatMessageComposerComponent,
      ],
      template: `
        <div class="chat-layout">
          <cometchat-groups
            (itemClick)="onGroupClick($event)"
          ></cometchat-groups>

          @if (activeGroup) {
            <div class="chat-panel">
              <cometchat-message-header [group]="activeGroup"></cometchat-message-header>
              <cometchat-message-list [group]="activeGroup"></cometchat-message-list>
              <cometchat-message-composer [group]="activeGroup"></cometchat-message-composer>
            </div>
          }
        </div>
      `,
    })
    export class GroupsChatComponent {
      activeGroup: CometChat.Group | null = null;

      onGroupClick(group: CometChat.Group): void {
        this.activeGroup = group;
      }
    }
    ```

    <Note>
      When `[group]` inputs are provided, they take priority over `ChatStateService` state for that component instance.
    </Note>
  </Tab>
</Tabs>

## Advanced Usage

### Filtering Groups with Request Builder

Use the `groupsRequestBuilder` to filter groups by various criteria:

```typescript expandable theme={null}
import { Component, OnInit } from '@angular/core';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import { CometChatGroupsComponent } from '@cometchat/chat-uikit-angular';

@Component({
  selector: 'app-filtered-groups',
  standalone: true,
  imports: [CometChatGroupsComponent],
  template: `
    <cometchat-groups
      [groupsRequestBuilder]="groupsBuilder"
      (itemClick)="onGroupClick($event)"
    ></cometchat-groups>
  `
})
export class FilteredGroupsComponent implements OnInit {
  groupsBuilder!: CometChat.GroupsRequestBuilder;

  ngOnInit(): void {
    // Show only joined groups with limit of 20
    this.groupsBuilder = new CometChat.GroupsRequestBuilder()
      .setLimit(20)
      .joinedOnly(true);
  }

  onGroupClick(group: CometChat.Group): void {
    console.log('Group clicked:', group);
  }
}
```

### GroupsRequestBuilder Methods

| Method             | Type       | Description                                                |
| ------------------ | ---------- | ---------------------------------------------------------- |
| `setLimit`         | `number`   | Sets the number of groups fetched per request (pagination) |
| `setSearchKeyword` | `string`   | Fetches groups matching the search string                  |
| `joinedOnly`       | `boolean`  | Fetches only groups the logged-in user has joined          |
| `setTags`          | `string[]` | Fetches groups with specified tags                         |
| `withTags`         | `boolean`  | Includes tag information in response                       |
| `setGUIDs`         | `string[]` | Fetches specific groups by GUIDs                           |

### Selection Modes

Enable single or multiple group selection:

```typescript expandable theme={null}
import { Component } from '@angular/core';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import { CometChatGroupsComponent, SelectionMode } from '@cometchat/chat-uikit-angular';

@Component({
  selector: 'app-selectable-groups',
  standalone: true,
  imports: [CometChatGroupsComponent],
  template: `
    <div class="group-selection">
      @if (selectedGroups.length > 0) {
        <div class="toolbar">
          <span>{{ selectedGroups.length }} groups selected</span>
          <button (click)="performAction()">Perform Action</button>
        </div>
      }
      
      <cometchat-groups
        [selectionMode]="selectionMode"
        (select)="onGroupSelect($event)"
        (itemClick)="onGroupClick($event)"
      ></cometchat-groups>
    </div>
  `
})
export class SelectableGroupsComponent {
  selectionMode = SelectionMode.multiple;
  selectedGroups: CometChat.Group[] = [];

  onGroupSelect(event: { group: CometChat.Group; selected: boolean }): void {
    if (event.selected) {
      this.selectedGroups.push(event.group);
    } else {
      this.selectedGroups = this.selectedGroups.filter(
        g => g.getGuid() !== event.group.getGuid()
      );
    }
  }

  onGroupClick(group: CometChat.Group): void {
    console.log('Group clicked:', group);
  }

  performAction(): void {
    console.log('Performing action on groups:', this.selectedGroups);
  }
}
```

### Custom Context Menu Options

Provide custom actions for each group:

```typescript expandable theme={null}
import { Component } from '@angular/core';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import { CometChatGroupsComponent, CometChatOption } from '@cometchat/chat-uikit-angular';

@Component({
  selector: 'app-custom-menu-groups',
  standalone: true,
  imports: [CometChatGroupsComponent],
  template: `
    <cometchat-groups
      [options]="getCustomOptions"
      (itemClick)="onGroupClick($event)"
    ></cometchat-groups>
  `
})
export class CustomMenuGroupsComponent {
  getCustomOptions = (group: CometChat.Group): CometChatOption[] => {
    return [
      {
        id: 'open',
        title: 'Open Group',
        iconURL: 'assets/chat-icon.svg',
        onClick: () => this.openGroup(group)
      },
      {
        id: 'members',
        title: 'View Members',
        iconURL: 'assets/members-icon.svg',
        onClick: () => this.viewMembers(group)
      },
      {
        id: 'leave',
        title: 'Leave Group',
        iconURL: 'assets/leave-icon.svg',
        onClick: () => this.leaveGroup(group)
      }
    ];
  };

  openGroup(group: CometChat.Group): void {
    console.log('Opening group:', group.getName());
  }

  viewMembers(group: CometChat.Group): void {
    console.log('Viewing members of:', group.getName());
  }

  leaveGroup(group: CometChat.Group): void {
    console.log('Leaving group:', group.getName());
  }

  onGroupClick(group: CometChat.Group): void {
    console.log('Group clicked:', group);
  }
}
```

## Customization with Templates

### Custom Subtitle View

Customize the subtitle section to show member count and group type:

```typescript expandable theme={null}
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import { CometChatGroupsComponent } from '@cometchat/chat-uikit-angular';

@Component({
  selector: 'app-custom-subtitle',
  standalone: true,
  imports: [CommonModule, CometChatGroupsComponent],
  template: `
    <cometchat-groups
      [subtitleView]="customSubtitle"
      (itemClick)="onGroupClick($event)"
    >
      <ng-template #customSubtitle let-group>
        <div class="custom-subtitle">
          <span class="members">
            👥 {{ group.getMembersCount() }} members
          </span>
          <span class="type" [class]="group.getType()">
            {{ getGroupTypeLabel(group) }}
          </span>
        </div>
      </ng-template>
    </cometchat-groups>
  `,
  styles: [`
    .custom-subtitle {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 12px;
    }
    .members {
      color: #666;
    }
    .type {
      padding: 2px 6px;
      border-radius: 4px;
      font-size: 10px;
      font-weight: 500;
    }
    .type.public { background: #E8F5E9; color: #4CAF50; }
    .type.private { background: #FFF3E0; color: #FF9800; }
    .type.password { background: #FFEBEE; color: #F44336; }
  `]
})
export class CustomSubtitleComponent {
  getGroupTypeLabel(group: CometChat.Group): string {
    const type = group.getType();
    switch (type) {
      case CometChat.GROUP_TYPE.PUBLIC: return '🌐 Public';
      case CometChat.GROUP_TYPE.PRIVATE: return '🔒 Private';
      case CometChat.GROUP_TYPE.PASSWORD: return '🔑 Password';
      default: return type;
    }
  }

  onGroupClick(group: CometChat.Group): void {
    console.log('Group clicked:', group);
  }
}
```

### Custom Empty and Error States

Provide custom views for empty and error states:

```typescript expandable theme={null}
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CometChatGroupsComponent } from '@cometchat/chat-uikit-angular';

@Component({
  selector: 'app-custom-states',
  standalone: true,
  imports: [CommonModule, CometChatGroupsComponent],
  template: `
    <cometchat-groups
      [emptyView]="customEmpty"
      [errorView]="customError"
      (itemClick)="onGroupClick($event)"
      (error)="onError($event)"
    >
      <ng-template #customEmpty>
        <div class="custom-empty-state">
          <img src="assets/no-groups.svg" alt="No groups" />
          <h3>No Groups Found</h3>
          <p>Create a new group or join an existing one</p>
          <button (click)="createGroup()">Create Group</button>
        </div>
      </ng-template>

      <ng-template #customError>
        <div class="custom-error-state">
          <img src="assets/error-icon.svg" alt="Error" />
          <h3>Something went wrong</h3>
          <p>We couldn't load the groups list</p>
          <button (click)="retryLoading()">Try Again</button>
        </div>
      </ng-template>
    </cometchat-groups>
  `
})
export class CustomStatesComponent {
  createGroup(): void {
    console.log('Creating new group');
  }

  retryLoading(): void {
    console.log('Retrying to load groups');
  }

  onGroupClick(group: any): void {
    console.log('Group clicked:', group);
  }

  onError(error: any): void {
    console.error('Error:', error);
  }
}
```

## Styling with CSS Variables

The CometChatGroups component uses CSS variables for comprehensive theming:

```css expandable theme={null}
cometchat-groups {
  /* Background colors */
  --cometchat-background-color-01: #ffffff;
  --cometchat-background-color-02: #f5f5f5;
  --cometchat-background-color-03: #e8e8e8;
  
  /* Text colors */
  --cometchat-text-color-primary: #141414;
  --cometchat-text-color-secondary: #727272;
  
  /* Border colors */
  --cometchat-border-color-light: #e8e8e8;
  
  /* Primary color */
  --cometchat-primary-color: #6852D6;
  
  /* Typography */
  --cometchat-font-heading2-bold: 700 20px/24px Roboto;
  --cometchat-font-body-regular: 400 14px/16.8px Roboto;
  --cometchat-font-caption1-medium: 500 12px/14.4px Roboto;
  
  /* Spacing */
  --cometchat-spacing-1: 4px;
  --cometchat-spacing-2: 8px;
  --cometchat-spacing-3: 12px;
  --cometchat-spacing-4: 16px;
  
  /* Border radius */
  --cometchat-radius-2: 8px;
  --cometchat-radius-max: 1000px;
}
```

### Dark Theme Example

```css theme={null}
.dark-theme cometchat-groups {
  --cometchat-background-color-01: #1a1a1a;
  --cometchat-background-color-02: #2a2a2a;
  --cometchat-background-color-03: #333333;
  --cometchat-text-color-primary: #ffffff;
  --cometchat-text-color-secondary: #cccccc;
  --cometchat-border-color-light: #444444;
}
```

## Real-Time Features

### Automatic Updates

The component automatically updates in real-time for:

* **Member Events**: Join, leave, kick, ban events are reflected immediately
* **Group Updates**: Name, icon, and description changes
* **Connection Recovery**: Automatically refreshes the list when connection is re-established

### Event Subscriptions

The component subscribes to the following events:

```typescript expandable theme={null}
// SDK GroupListener events
CometChat.GroupListener.onGroupMemberJoined(action, joinedUser, joinedGroup)
CometChat.GroupListener.onGroupMemberLeft(action, leftUser, leftGroup)
CometChat.GroupListener.onGroupMemberKicked(action, kickedUser, kickedBy, kickedFrom)
CometChat.GroupListener.onGroupMemberBanned(action, bannedUser, bannedBy, bannedFrom)
CometChat.GroupListener.onMemberAddedToGroup(action, addedBy, addedUser, addedTo)

// CometChatGroupEvents
CometChatGroupEvents.ccGroupCreated
CometChatGroupEvents.ccGroupDeleted
CometChatGroupEvents.ccGroupLeft
CometChatGroupEvents.ccGroupMemberAdded

// Connection events
CometChat.ConnectionListener.onConnected()
```

## Error Handling

### Built-in Error Handling

The component includes comprehensive error handling:

```typescript theme={null}
<cometchat-groups
  [hideError]="false"
  (error)="handleError($event)"
  (itemClick)="onGroupClick($event)"
></cometchat-groups>
```

```typescript expandable theme={null}
handleError(error: CometChat.CometChatException): void {
  console.error('Groups error:', error);
  
  if (error.code === 'NETWORK_ERROR') {
    this.showToast('Network error. Please check your connection.');
  } else if (error.code === 'AUTH_ERROR') {
    this.showToast('Authentication error. Please log in again.');
  } else {
    this.showToast('An error occurred. Please try again.');
  }
}
```

<Info>
  For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatgroups).
</Info>

## Best Practices

<Tip>
  Use `groupsRequestBuilder` to limit the initial fetch size for better performance. The default limit is 30 groups per page.
</Tip>

<Warning>
  Always handle the `error` event to provide feedback to users when something goes wrong.
</Warning>

<Info>
  The component uses `OnPush` change detection strategy for optimal performance. If you're using custom templates with external state, ensure proper change detection triggering.
</Info>

<Tip>
  Use `hideGroupType` to simplify the UI if your application only uses one type of group.
</Tip>

<Warning>
  When using custom context menu options, ensure the `onClick` handlers are properly bound to avoid `this` context issues. Use arrow functions.
</Warning>

## Complete Example

Here's a comprehensive example combining multiple features:

```typescript expandable theme={null}
import { Component, OnInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CometChat } from '@cometchat/chat-sdk-javascript';
import { 
  CometChatGroupsComponent,
  SelectionMode,
  CometChatOption
} from '@cometchat/chat-uikit-angular';
import { Subject, takeUntil } from 'rxjs';

@Component({
  selector: 'app-groups-demo',
  standalone: true,
  imports: [CommonModule, CometChatGroupsComponent],
  template: `
    <div class="groups-container">
      @if (selectedGroups.length > 0) {
        <div class="toolbar">
          <span>{{ selectedGroups.length }} selected</span>
          <button (click)="clearSelection()">Clear</button>
        </div>
      }

      <cometchat-groups
        [groupsRequestBuilder]="groupsBuilder"
        [selectionMode]="selectionMode"
        [options]="getCustomOptions"
        [subtitleView]="customSubtitle"
        (itemClick)="onGroupClick($event)"
        (select)="onGroupSelect($event)"
        (error)="handleError($event)"
        (empty)="handleEmpty()"
      >
        <ng-template #customSubtitle let-group>
          <div class="group-subtitle">
            <span>{{ group.getMembersCount() }} members</span>
            <span class="type">{{ getGroupTypeLabel(group) }}</span>
          </div>
        </ng-template>
      </cometchat-groups>
    </div>
  `,
  styles: [`
    .groups-container {
      height: 100vh;
      display: flex;
      flex-direction: column;
    }
    .toolbar {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 12px 16px;
      background-color: #f5f5f5;
      border-bottom: 1px solid #e8e8e8;
    }
    .group-subtitle {
      display: flex;
      gap: 8px;
      font-size: 12px;
      color: #666;
    }
    .type {
      color: #6852D6;
    }
  `]
})
export class GroupsDemoComponent implements OnInit, OnDestroy {
  groupsBuilder!: CometChat.GroupsRequestBuilder;
  selectionMode = SelectionMode.multiple;
  selectedGroups: CometChat.Group[] = [];
  
  private destroy$ = new Subject<void>();

  ngOnInit(): void {
    this.groupsBuilder = new CometChat.GroupsRequestBuilder()
      .setLimit(30)
      .joinedOnly(true);
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  getCustomOptions = (group: CometChat.Group): CometChatOption[] => {
    return [
      {
        id: 'open',
        title: 'Open',
        iconURL: 'assets/chat.svg',
        onClick: () => this.openGroup(group)
      },
      {
        id: 'leave',
        title: 'Leave',
        iconURL: 'assets/leave.svg',
        onClick: () => this.leaveGroup(group)
      }
    ];
  };

  getGroupTypeLabel(group: CometChat.Group): string {
    const type = group.getType();
    switch (type) {
      case CometChat.GROUP_TYPE.PUBLIC: return 'Public';
      case CometChat.GROUP_TYPE.PRIVATE: return 'Private';
      case CometChat.GROUP_TYPE.PASSWORD: return 'Password';
      default: return type;
    }
  }

  onGroupClick(group: CometChat.Group): void {
    console.log('Group clicked:', group.getName());
  }

  onGroupSelect(event: { group: CometChat.Group; selected: boolean }): void {
    if (event.selected) {
      this.selectedGroups.push(event.group);
    } else {
      this.selectedGroups = this.selectedGroups.filter(
        g => g.getGuid() !== event.group.getGuid()
      );
    }
  }

  openGroup(group: CometChat.Group): void {
    console.log('Opening group:', group.getName());
  }

  leaveGroup(group: CometChat.Group): void {
    console.log('Leaving group:', group.getName());
  }

  clearSelection(): void {
    this.selectedGroups = [];
  }

  handleError(error: CometChat.CometChatException): void {
    console.error('Error:', error);
  }

  handleEmpty(): void {
    console.log('No groups found');
  }
}
```
