Merged PR 1891: feat: implement multi-level checkbox filter with hierarchical selection

feat: implement multi-level checkbox filter with hierarchical selection

- Add support for hierarchical checkbox options with parent-child relationships
- Implement automatic child selection/deselection when parent is toggled
- Add checkbox-input-control component for individual option management
- Add isCheckboxSelected helper for determining selection states
- Extend FilterService with setInputCheckboxOptionSelected method
- Update checkbox schemas to support nested option structures
- Add comprehensive test coverage for new multi-level functionality

Ref: #5231
This commit is contained in:
Lorenz Hilpert
2025-07-25 13:49:44 +00:00
committed by Nino Righi
parent 0b4aef5f6c
commit b339a6d79f
35 changed files with 4071 additions and 1886 deletions

View File

@@ -1,105 +1,137 @@
.ui-checkbox-label {
@apply inline-flex items-center gap-4 text-isa-neutral-900 isa-text-body-2-regular;
}
.ui-checkbox-label:has(:checked) {
@apply isa-text-body-2-bold;
}
.ui-checkbox.ui-checkbox__checkbox {
@apply relative inline-flex p-3 items-center justify-center rounded-lg bg-isa-white size-6 border border-solid border-isa-neutral-900;
font-size: 1.5rem;
.ui-checkbox__icon {
@apply invisible min-w-6 size-6 text-isa-white;
}
input[type="checkbox"] {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
opacity: 0;
@apply cursor-pointer;
}
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-900 text-isa-white;
.ui-checkbox__icon {
@apply visible;
}
}
&:has(input[type="checkbox"]:disabled) {
@apply bg-isa-neutral-400 border-isa-neutral-400 cursor-default;
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-400 border-isa-neutral-400;
}
input[type="checkbox"] {
@apply cursor-default;
}
}
}
.ui-checkbox.ui-checkbox__bullet {
display: inline-flex;
padding: 0.75rem;
justify-content: center;
align-items: center;
font-size: 1.5rem;
position: relative;
@apply rounded-full bg-isa-neutral-300 size-12;
.ui-checkbox__icon {
@apply invisible size-6 text-isa-neutral-100;
}
input[type="checkbox"] {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
opacity: 0;
@apply cursor-pointer;
}
&:hover {
@apply bg-isa-neutral-400;
}
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-700;
.ui-checkbox__icon {
@apply visible;
}
&:hover {
@apply bg-isa-neutral-800;
}
}
&:has(input[type="checkbox"]:disabled) {
@apply bg-isa-neutral-400 cursor-default;
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-700;
}
input[type="checkbox"] {
@apply cursor-default;
}
}
}
.ui-checkbox-label {
@apply inline-flex items-center gap-4 text-isa-neutral-900 isa-text-body-2-regular;
}
.ui-checkbox-label:has(:checked) {
@apply isa-text-body-2-bold;
}
.ui-checkbox.ui-checkbox__checkbox {
@apply relative inline-flex p-3 items-center justify-center rounded-lg bg-isa-white size-6 border border-solid border-isa-neutral-900;
font-size: 1.5rem;
.ui-checkbox__icon {
@apply hidden min-w-6 size-6 text-isa-white;
}
input[type="checkbox"] {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
opacity: 0;
@apply cursor-pointer;
}
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-900 text-isa-white;
.ui-checkbox__icon--checked {
@apply inline-block;
}
}
&:has(input[type="checkbox"]:indeterminate) {
@apply bg-isa-neutral-900 text-isa-white;
.ui-checkbox__icon--checked {
@apply hidden;
}
.ui-checkbox__icon--indeterminate {
@apply inline-block;
}
}
&:has(input[type="checkbox"]:disabled) {
@apply bg-isa-neutral-400 border-isa-neutral-400 cursor-default;
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-400 border-isa-neutral-400;
}
&:has(input[type="checkbox"]:indeterminate) {
@apply bg-isa-neutral-400 border-isa-neutral-400;
}
input[type="checkbox"] {
@apply cursor-default;
}
}
}
.ui-checkbox.ui-checkbox__bullet {
display: inline-flex;
padding: 0.75rem;
justify-content: center;
align-items: center;
font-size: 1.5rem;
position: relative;
@apply rounded-full bg-isa-neutral-300 size-12;
.ui-checkbox__icon {
@apply hidden size-6 text-isa-neutral-100;
}
input[type="checkbox"] {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
opacity: 0;
@apply cursor-pointer;
}
&:hover {
@apply bg-isa-neutral-400;
}
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-700;
.ui-checkbox__icon--checked {
@apply flex;
}
&:hover {
@apply bg-isa-neutral-800;
}
}
&:has(input[type="checkbox"]:indeterminate) {
@apply bg-isa-neutral-700;
.ui-checkbox__icon--checked {
@apply hidden;
}
.ui-checkbox__icon--indeterminate {
@apply flex;
}
&:hover {
@apply bg-isa-neutral-800;
}
}
&:has(input[type="checkbox"]:disabled) {
@apply bg-isa-neutral-400 cursor-default;
&:has(input[type="checkbox"]:checked) {
@apply bg-isa-neutral-700;
}
input[type="checkbox"] {
@apply cursor-default;
}
}
}

View File

@@ -1,2 +1,9 @@
<ng-content select="input[type=checkbox]"></ng-content>
<ng-icon class="ui-checkbox__icon" name="isaActionCheck"></ng-icon>
<ng-content select="input[type=checkbox]"></ng-content>
<ng-icon
class="ui-checkbox__icon ui-checkbox__icon--checked"
name="isaActionCheck"
></ng-icon>
<ng-icon
class="ui-checkbox__icon ui-checkbox__icon--indeterminate"
name="isaActionMinus"
></ng-icon>

View File

@@ -1,73 +1,73 @@
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { isaActionCheck } from '@isa/icons';
/**
* Configuration options for CheckboxComponent appearance
* @readonly
*/
export const CheckboxAppearance = {
/** Renders the checkbox as a round bullet style selector */
Bullet: 'bullet',
/** Renders the checkbox as a traditional square checkbox (default) */
Checkbox: 'checkbox',
} as const;
export type CheckboxAppearance =
(typeof CheckboxAppearance)[keyof typeof CheckboxAppearance];
/**
* A customizable checkbox component that supports different visual appearances.
*
* This component provides a styled checkbox input that can be displayed either as
* a traditional checkbox or as a bullet-style (round) selector. It uses Angular signals
* for reactive state management and includes an optional checkmark icon when selected.
*
* @example
* ```html
* <!-- Default checkbox appearance -->
* <ui-checkbox>
* <input type="checkbox" />
* </ui-checkbox>
*
* <!-- Bullet appearance -->
* <ui-checkbox [appearance]="CheckboxAppearance.Bullet">
* <input type="checkbox" />
* </ui-checkbox>
* ```
*/
@Component({
selector: 'ui-checkbox',
templateUrl: './checkbox.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
imports: [NgIconComponent],
providers: [provideIcons({ isaActionCheck })],
host: {
'[class]': '["ui-checkbox", appearanceClass()]',
},
})
export class CheckboxComponent {
/**
* Controls the visual appearance of the checkbox.
* Can be either "checkbox" (default) or "bullet".
*/
appearance = input<CheckboxAppearance>(CheckboxAppearance.Checkbox);
/**
* Computes the CSS class to apply based on the current appearance setting.
*
* @returns A CSS class name string that corresponds to the current appearance
*/
appearanceClass = computed(() => {
return this.appearance() === CheckboxAppearance.Bullet
? 'ui-checkbox__bullet'
: 'ui-checkbox__checkbox';
});
}
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { isaActionCheck, isaActionMinus } from '@isa/icons';
/**
* Configuration options for CheckboxComponent appearance
* @readonly
*/
export const CheckboxAppearance = {
/** Renders the checkbox as a round bullet style selector */
Bullet: 'bullet',
/** Renders the checkbox as a traditional square checkbox (default) */
Checkbox: 'checkbox',
} as const;
export type CheckboxAppearance =
(typeof CheckboxAppearance)[keyof typeof CheckboxAppearance];
/**
* A customizable checkbox component that supports different visual appearances.
*
* This component provides a styled checkbox input that can be displayed either as
* a traditional checkbox or as a bullet-style (round) selector. It uses Angular signals
* for reactive state management and includes an optional checkmark icon when selected.
*
* @example
* ```html
* <!-- Default checkbox appearance -->
* <ui-checkbox>
* <input type="checkbox" />
* </ui-checkbox>
*
* <!-- Bullet appearance -->
* <ui-checkbox [appearance]="CheckboxAppearance.Bullet">
* <input type="checkbox" />
* </ui-checkbox>
* ```
*/
@Component({
selector: 'ui-checkbox',
templateUrl: './checkbox.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
imports: [NgIconComponent],
providers: [provideIcons({ isaActionCheck, isaActionMinus })],
host: {
'[class]': '["ui-checkbox", appearanceClass()]',
},
})
export class CheckboxComponent {
/**
* Controls the visual appearance of the checkbox.
* Can be either "checkbox" (default) or "bullet".
*/
appearance = input<CheckboxAppearance>(CheckboxAppearance.Checkbox);
/**
* Computes the CSS class to apply based on the current appearance setting.
*
* @returns A CSS class name string that corresponds to the current appearance
*/
appearanceClass = computed(() => {
return this.appearance() === CheckboxAppearance.Bullet
? 'ui-checkbox__bullet'
: 'ui-checkbox__checkbox';
});
}