Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
color="primary"
(click)="actionClick.emit()"
>
{{ action() }}
<div class="label">
{{ action() }}
</div>
</button>
<div class="count" hraPlainTooltip="Number of unique items per filter category">
{{ totalCount() | number }}
</div>
</div>

@if (chips().length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,71 +1,78 @@
@use '@angular/material' as mat;
@use '../../../styles/vars';
@use '../../../styles/utils';

:host {
display: block;
width: 100%;
}

.filter-container {
display: flex;
flex-direction: column;

.top-row {
.filter-container {
display: flex;
align-items: center;
color: vars.$secondary;
flex-direction: column;

.info-icon {
padding: 0.5rem;
.top-row {
display: flex;
align-items: center;
color: vars.$secondary;
}

.category-button {
flex: 0 1 auto;
min-width: fit-content;
justify-content: flex-start;
.info-icon {
padding: 0.5rem;
color: vars.$secondary;
}

.category-button {
flex: 0 1 auto;
min-width: fit-content;
width: 100%;
justify-content: flex-start;

@include mat.button-overrides(
(
text-horizontal-padding: 0.75rem,
text-label-text-font: vars.$label-medium-font,
text-label-text-size: vars.$label-medium-size,
text-label-text-tracking: vars.$label-medium-tracking,
text-label-text-weight: vars.$label-medium-weight,
)
);
}

@include mat.button-overrides(
(
text-horizontal-padding: 0.75rem,
text-label-text-font: vars.$label-medium-font,
text-label-text-size: vars.$label-medium-size,
text-label-text-tracking: vars.$label-medium-tracking,
text-label-text-weight: vars.$label-medium-weight,
)
);
.count {
color: vars.$primary;
@include utils.use-font(label, small);
}
}
}

.chips-container {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
.chips-container {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;

mat-chip {
@include mat.chips-overrides(
(
label-text-font: vars.$label-medium-font,
label-text-line-height: vars.$label-medium-line-height,
label-text-size: vars.$label-medium-size,
label-text-tracking: vars.$label-medium-tracking,
label-text-weight: vars.$label-medium-weight,
container-height: 2rem,
)
);
mat-chip {
@include mat.chips-overrides(
(
label-text-font: vars.$label-medium-font,
label-text-line-height: vars.$label-medium-line-height,
label-text-size: vars.$label-medium-size,
label-text-tracking: vars.$label-medium-tracking,
label-text-weight: vars.$label-medium-weight,
container-height: 2rem,
)
);

button[matChipRemove] {
mat-icon {
font-size: 1.25rem;
width: 1.25rem;
height: 1.25rem;
vertical-align: middle;
button[matChipRemove] {
mat-icon {
font-size: 1.25rem;
width: 1.25rem;
height: 1.25rem;
vertical-align: middle;
}
}
}
}
}

mat-divider {
margin: 0.25rem 0;
mat-divider {
margin: 0.25rem 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { booleanAttribute, ChangeDetectionStrategy, Component, input, model, output } from '@angular/core';
import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { HraCommonModule } from '@hra-ui/common';
import { ButtonsModule } from '@hra-ui/design-system/buttons';
import {
InfoButtonActionsDirective,
InfoButtonComponent,
InfoButtonTaglineDirective,
InfoButtonActionsDirective,
} from '@hra-ui/design-system/buttons/info-button';
import { PlainTooltipDirective } from '@hra-ui/design-system/tooltips/plain-tooltip';

/** A filter chip representing a selected filter option */
export interface FilterChip {
Expand All @@ -30,6 +31,7 @@ export interface FilterChip {
InfoButtonComponent,
InfoButtonTaglineDirective,
InfoButtonActionsDirective,
PlainTooltipDirective,
],
templateUrl: './filter-container.component.html',
styleUrl: './filter-container.component.scss',
Expand All @@ -39,6 +41,9 @@ export class FilterContainerComponent<T extends FilterChip> {
/** tagline for the filter category */
readonly action = input.required<string>();

/** Total count of filter options in the category */
readonly totalCount = input<number>();

/** Whether to show the info button with tooltip */
readonly showTooltip = input(false, { transform: booleanAttribute });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@
<hra-icon class="icon" fontIcon="filter_list" />
<span class="title">Filters</span>
</div>
@for (filter of filters(); track $index) {
@for (filter of filtersWithCounts(); track $index) {
<hra-filter-container
enableDivider
cdkOverlayOrigin
[action]="filter.label"
[totalCount]="filter.totalCount"
[chips]="filter.selected ?? []"
(chipsChange)="updateFilterSelection(filter, $event)"
(actionClick)="activeFilter.set(filter)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const meta: Meta = {
tagline: 'Database Headline',
description: 'Supporting text here, if needed, but make it short and straightforward',
enableClose: true,
enableTotalCount: false,
filters: FILTER_CATEGORIES,
toggleOptions: [
{ id: 'option1', label: 'Option 1' },
Expand Down Expand Up @@ -145,6 +146,10 @@ export default meta;
type Story = StoryObj;

export const Default: Story = {
args: {
enableTotalCount: true,
},

render: (args) => ({
props: args,
template: `
Expand All @@ -153,6 +158,7 @@ export const Default: Story = {
[tagline]="tagline"
[description]="description"
[enableClose]="enableClose"
[enableTotalCount]="enableTotalCount"
>
${CUSTOM_CONTROLS}
</hra-filter-menu>
Expand Down
24 changes: 24 additions & 0 deletions libs/design-system/filter-menu/src/lib/filter-menu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface FilterOptionCategory<T extends SearchListOption> {
options: T[];
/** Selected filter options */
selected?: T[];
/** Total count */
totalCount?: number;
}

/** Position of the filter menu overlay */
Expand Down Expand Up @@ -54,6 +56,9 @@ export class FilterMenuComponent<T extends SearchListOption> {
/** Whether to show the close button */
readonly enableClose = input<boolean>();

/** Whether to enable total count display */
readonly enableTotalCount = input<boolean>(false);

/** List of all filters with options */
readonly filters = model.required<FilterOptionCategory<T>[]>();

Expand All @@ -72,6 +77,14 @@ export class FilterMenuComponent<T extends SearchListOption> {
/** Current active filter id */
protected readonly activeFilterId = computed(() => this.activeFilter()?.id);

/** Filters with total counts */
protected readonly filtersWithCounts = computed(() => {
if (this.enableTotalCount()) {
return this._addTotalCounts(this.filters());
}
return this.filters();
});

/**
* Updates filters on filter selection
* @param category Filter category to update
Expand All @@ -91,4 +104,15 @@ export class FilterMenuComponent<T extends SearchListOption> {
category !== undefined && current?.id !== category.id ? current : undefined,
);
}

/** Helper function to add total counts to filters */
private _addTotalCounts(filterCategories: FilterOptionCategory<T>[]): FilterOptionCategory<T>[] {
return filterCategories.map((category) => {
const modifiedCategory = { ...category };
if (category.options) {
modifiedCategory.totalCount = category.options.reduce((sum, option) => sum + (option?.count || 0), 0);
}
return modifiedCategory;
});
}
}
Loading