diff --git a/live-editing/configs/ComboConfigGenerator.ts b/live-editing/configs/ComboConfigGenerator.ts index 74e14111ee..716b5ed8d5 100644 --- a/live-editing/configs/ComboConfigGenerator.ts +++ b/live-editing/configs/ComboConfigGenerator.ts @@ -27,10 +27,76 @@ export class ComboConfigGenerator implements IConfigGenerator { shortenComponentPathBy: '/lists/combo/' })); + // combo overview configs.push(new Config({ - additionalFiles: ['/src/app/data/heroData.ts', '/src/app/lists/combo/combo-styling/layout.scss'], appConfig: BaseAppConfig, - component: 'ComboStylingComponent' + component: 'ComboOverviewComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo type + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboTypeComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo disabled + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboDisabledComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo size + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboSizeComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo states + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboStatesComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo validation + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboValidationComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo dropdown size + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboDropdownSizeComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo search input + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboSearchInputComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo styling + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'ComboStylingComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // combo tailwind styling + configs.push(new Config({ + additionalDependencies: ['tailwindcss', '@tailwindcss/postcss'], + additionalFiles: ['.postcssrc.json'], + appConfig: BaseAppConfig, + component: 'ComboTailwindStylingComponent', + shortenComponentPathBy: '/lists/combo/' })); configs.push(new Config({ @@ -72,17 +138,67 @@ export class ComboConfigGenerator implements IConfigGenerator { shortenComponentPathBy: '/lists/combo/' })); + // simple combo overview configs.push(new Config({ - additionalFiles: ['/src/app/data/stocks-data.ts'], - additionalDependencies: ['igniteui-angular-charts', 'igniteui-angular-core'], appConfig: BaseAppConfig, - component: 'SimpleComboMainComponent' + component: 'SimpleComboOverviewComponent', + shortenComponentPathBy: '/lists/combo/' })); + // simple combo type + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'SimpleComboTypeComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo disabled configs.push(new Config({ - additionalFiles: ['/src/app/data/cities15000-regions-countries.ts'], appConfig: BaseAppConfig, - component: 'SimpleComboUsageComponent' + component: 'SimpleComboDisabledComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo size + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'SimpleComboSizeComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo states + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'SimpleComboStatesComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo validation + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'SimpleComboValidationComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo dropdown size + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'SimpleComboDropdownSizeComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo dropdown items + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'SimpleComboDropdownItemsComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo dropdown items states + configs.push(new Config({ + appConfig: BaseAppConfig, + component: 'SimpleComboDropdownItemsStatesComponent', + shortenComponentPathBy: '/lists/combo/' })); configs.push(new Config({ @@ -97,10 +213,20 @@ export class ComboConfigGenerator implements IConfigGenerator { component: 'SimpleComboRemoteComponent' })); + // simple combo styling configs.push(new Config({ - additionalFiles: ['/src/app/data/cities15000-regions-countries.ts', '/src/app/lists/combo/simple-combo-styling/layout.scss'], appConfig: BaseAppConfig, - component: 'SimpleComboStylingComponent' + component: 'SimpleComboStylingComponent', + shortenComponentPathBy: '/lists/combo/' + })); + + // simple combo tailwind styling + configs.push(new Config({ + additionalDependencies: ['tailwindcss', '@tailwindcss/postcss'], + additionalFiles: ['.postcssrc.json'], + appConfig: BaseAppConfig, + component: 'SimpleComboTailwindStylingComponent', + shortenComponentPathBy: '/lists/combo/' })); return configs; diff --git a/src/app/lists/combo/combo-disabled/combo-disabled.component.html b/src/app/lists/combo/combo-disabled/combo-disabled.component.html new file mode 100644 index 0000000000..a37714a5ae --- /dev/null +++ b/src/app/lists/combo/combo-disabled/combo-disabled.component.html @@ -0,0 +1,24 @@ +
+ + Enabled + Disabled + @for (row of rows; track $index) { + @let groupStart = $index === 2; + {{ row.label }} + @for (disabled of [false, true]; track disabled) { +
+ + +
+ } + } +
diff --git a/src/app/lists/combo/combo-disabled/combo-disabled.component.scss b/src/app/lists/combo/combo-disabled/combo-disabled.component.scss new file mode 100644 index 0000000000..267bee027b --- /dev/null +++ b/src/app/lists/combo/combo-disabled/combo-disabled.component.scss @@ -0,0 +1,35 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: center; + justify-content: center; + height: 100%; + padding: 1rem; +} + +.states { + display: grid; + grid-template-columns: max-content repeat(2, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + align-items: center; + column-gap: 1.75rem; + row-gap: 1.5rem; +} + +.group-start { + margin-top: 1.5rem; +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/combo-disabled/combo-disabled.component.ts b/src/app/lists/combo/combo-disabled/combo-disabled.component.ts new file mode 100644 index 0000000000..4a127857bb --- /dev/null +++ b/src/app/lists/combo/combo-disabled/combo-disabled.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { IgxInputGroupType } from 'igniteui-angular/input-group'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface DisabledRow { + label: string; + type: IgxInputGroupType; + value: string[]; +} + +@Component({ + selector: 'app-combo-disabled', + styleUrls: ['./combo-disabled.component.scss'], + templateUrl: './combo-disabled.component.html', + imports: [IgxComboComponent, FormsModule] +}) +export class ComboDisabledComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + public rows: DisabledRow[] = [ + { label: 'Idle', type: 'border', value: [] }, + { label: 'Filled', type: 'border', value: ['BY', 'BE'] }, + { label: 'Idle', type: 'box', value: [] }, + { label: 'Filled', type: 'box', value: ['BY', 'BE'] } + ]; +} diff --git a/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.html b/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.html new file mode 100644 index 0000000000..49f28cd3d1 --- /dev/null +++ b/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.html @@ -0,0 +1,20 @@ +
+ @for (size of sizes; track size.label) { +
+ {{ size.label }} + + +
+ } +
diff --git a/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.scss b/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.scss new file mode 100644 index 0000000000..4a423025d5 --- /dev/null +++ b/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.scss @@ -0,0 +1,48 @@ +:host { + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.sizes { + display: grid; + grid-template-columns: repeat(3, 11.25rem); + column-gap: 1rem; +} + +.size { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +.dropdown { + width: 100%; +} + +/* Only the dropdown of the combo is shown, so the input field is hidden. */ +.dropdown ::ng-deep igx-combo > igx-input-group { + display: none; +} + + +.small { + --ig-size: var(--ig-size-small); +} + +.medium { + --ig-size: var(--ig-size-medium); +} + +.large { + --ig-size: var(--ig-size-large); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} diff --git a/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.ts b/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.ts new file mode 100644 index 0000000000..9dc4370bc7 --- /dev/null +++ b/src/app/lists/combo/combo-dropdown-size/combo-dropdown-size.component.ts @@ -0,0 +1,53 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { IBaseCancelableBrowserEventArgs } from 'igniteui-angular/core'; +import { forkJoin, take } from 'rxjs'; + +interface Option { + id: number; + name: string; + group: string; +} + +interface DropdownSize { + label: string; + className: string; +} + +@Component({ + selector: 'app-combo-dropdown-size', + styleUrls: ['./combo-dropdown-size.component.scss'], + templateUrl: './combo-dropdown-size.component.html', + imports: [IgxComboComponent] +}) +export class ComboDropdownSizeComponent { + public options: Option[] = [1, 2, 3].map((id) => ({ id, name: 'Item', group: 'Header' })); + + public sizes: DropdownSize[] = [ + { label: 'Small', className: 'small' }, + { label: 'Medium', className: 'medium' }, + { label: 'Large', className: 'large' } + ]; + + private combos = viewChildren(IgxComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + + // The combos are shown open. Opening a combo moves the focus to its search input, so + // the sample is inert until the combos open and the page keeps its focus. + afterNextRender(() => { + const combos = this.combos(); + element.inert = true; + forkJoin(combos.map((combo) => combo.opened.pipe(take(1)))).subscribe(() => { + element.inert = false; + }); + combos.forEach((combo) => combo.open()); + }); + } + + // The input fields are hidden, so the dropdowns stay open and each one works on its own. + public keepOpen(event: IBaseCancelableBrowserEventArgs): void { + event.cancel = true; + } +} diff --git a/src/app/lists/combo/combo-overview/combo-overview.component.html b/src/app/lists/combo/combo-overview/combo-overview.component.html new file mode 100644 index 0000000000..075b1e708d --- /dev/null +++ b/src/app/lists/combo/combo-overview/combo-overview.component.html @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/app/lists/combo/combo-overview/combo-overview.component.scss b/src/app/lists/combo/combo-overview/combo-overview.component.scss new file mode 100644 index 0000000000..d38c5d64f5 --- /dev/null +++ b/src/app/lists/combo/combo-overview/combo-overview.component.scss @@ -0,0 +1,15 @@ +:host { + /* styles.scss asks the page for the large size; the combo keeps its own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.locations { + width: 22.5rem; +} diff --git a/src/app/lists/combo/combo-overview/combo-overview.component.ts b/src/app/lists/combo/combo-overview/combo-overview.component.ts new file mode 100644 index 0000000000..de9f27f49d --- /dev/null +++ b/src/app/lists/combo/combo-overview/combo-overview.component.ts @@ -0,0 +1,47 @@ +import { Component, ElementRef, afterNextRender, inject, viewChild } from '@angular/core'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { IgxIconComponent, IgxIconService } from 'igniteui-angular/icon'; +import { IgxLabelDirective, IgxPrefixDirective } from 'igniteui-angular/input-group'; +import { take } from 'rxjs'; + +const placeIcon = ''; + +interface City { + id: string; + name: string; + country: string; +} + +@Component({ + selector: 'app-combo-overview', + styleUrls: ['./combo-overview.component.scss'], + templateUrl: './combo-overview.component.html', + imports: [IgxComboComponent, IgxLabelDirective, IgxPrefixDirective, IgxIconComponent] +}) +export class ComboOverviewComponent { + public cities: City[] = [ + { id: 'BG01', name: 'Sofia', country: 'Country of Bulgaria' }, + { id: 'BG02', name: 'Plovdiv', country: 'Country of Bulgaria' }, + { id: 'BG03', name: 'Varna', country: 'Country of Bulgaria' }, + { id: 'BG04', name: 'Burgas', country: 'Country of Bulgaria' }, + { id: 'BG05', name: 'Pleven', country: 'Country of Bulgaria' } + ]; + + private combo = viewChild.required(IgxComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + inject(IgxIconService).addSvgIconFromText('place', placeIcon, 'material'); + + // The combo is shown open. Opening it moves the focus to its search input, so the + // sample is inert until the combo opens and the page keeps its focus. + afterNextRender(() => { + const combo = this.combo(); + element.inert = true; + combo.opened.pipe(take(1)).subscribe(() => { + element.inert = false; + }); + combo.open(); + }); + } +} diff --git a/src/app/lists/combo/combo-search-input/combo-search-input.component.html b/src/app/lists/combo/combo-search-input/combo-search-input.component.html new file mode 100644 index 0000000000..6d1a9d62cc --- /dev/null +++ b/src/app/lists/combo/combo-search-input/combo-search-input.component.html @@ -0,0 +1,16 @@ +
+ @for (state of states; track state.label) { +
+ {{ state.label }} + + +
+ } +
diff --git a/src/app/lists/combo/combo-search-input/combo-search-input.component.scss b/src/app/lists/combo/combo-search-input/combo-search-input.component.scss new file mode 100644 index 0000000000..120955598e --- /dev/null +++ b/src/app/lists/combo/combo-search-input/combo-search-input.component.scss @@ -0,0 +1,71 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.states { + display: grid; + grid-template-columns: repeat(3, 11.25rem); + grid-template-rows: repeat(2, 5.5rem); + column-gap: 1rem; + row-gap: 1rem; +} + +.state { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +.search { + width: 100%; + pointer-events: none; +} + +/* Only the search input of the combo dropdown is shown. */ +.search ::ng-deep igx-combo > igx-input-group, +.search ::ng-deep .igx-combo__add { + display: none; +} + +/* The open dropdown takes no pointer input either. */ +.search ::ng-deep .igx-overlay__content { + pointer-events: none; +} + +.search.hover ::ng-deep .igx-combo__search igx-input-group { + --idle-bottom-line-color: var(--hover-bottom-line-color); +} + +.search.disabled ::ng-deep .igx-combo__search igx-input-group { + --idle-bottom-line-color: var(--disabled-bottom-line-color); + --placeholder-color: var(--disabled-placeholder-color); +} + +.search.filled ::ng-deep .igx-combo__search igx-input-group { + --placeholder-color: var(--filled-text-color); +} + +.search.focused ::ng-deep .igx-combo__search igx-input-group { + --idle-bottom-line-color: var(--focused-bottom-line-color); + --hover-bottom-line-color: var(--focused-bottom-line-color); +} + +/* the input group shows its two pixels thick focused line */ +.search.focused ::ng-deep .igx-combo__search .igx-input-group__bundle::after { + scale: 1; +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} diff --git a/src/app/lists/combo/combo-search-input/combo-search-input.component.ts b/src/app/lists/combo/combo-search-input/combo-search-input.component.ts new file mode 100644 index 0000000000..56a2806670 --- /dev/null +++ b/src/app/lists/combo/combo-search-input/combo-search-input.component.ts @@ -0,0 +1,40 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxComboComponent } from 'igniteui-angular/combo'; + +interface SearchState { + label: string; + className?: string; + text?: string; +} + +@Component({ + selector: 'app-combo-search-input', + styleUrls: ['./combo-search-input.component.scss'], + templateUrl: './combo-search-input.component.html', + imports: [IgxComboComponent] +}) +export class ComboSearchInputComponent { + // The search input states come from the pointer and the keyboard, so the `hover`, + // `disabled`, `filled` and `focused` classes show them on the display-only combos. + public states: SearchState[] = [ + { label: 'Idle' }, + { label: 'Idle & Hover', className: 'hover' }, + { label: 'Disabled', className: 'disabled' }, + { label: 'Filled', className: 'filled', text: 'Input text' }, + { label: 'Filled & Hover', className: 'filled hover', text: 'Input text' }, + { label: 'Focused', className: 'filled focused', text: 'Input text' } + ]; + + private combos = viewChildren(IgxComboComponent); + + constructor() { + // The search inputs are display-only, so the sample is inert. Opening a combo moves the + // focus to its search input, and the inert sample also keeps the focus on the page. + inject>(ElementRef).nativeElement.inert = true; + + // The combos are shown open. + afterNextRender(() => { + this.combos().forEach((combo) => combo.open()); + }); + } +} diff --git a/src/app/lists/combo/combo-size/combo-size.component.html b/src/app/lists/combo/combo-size/combo-size.component.html new file mode 100644 index 0000000000..7677e706e0 --- /dev/null +++ b/src/app/lists/combo/combo-size/combo-size.component.html @@ -0,0 +1,22 @@ +
+ + @for (size of sizes; track size.label) { + {{ size.label }} + } + @for (type of types; track type.label) { + {{ type.label }} + @for (size of sizes; track size.label) { + + + } + } +
diff --git a/src/app/lists/combo/combo-size/combo-size.component.scss b/src/app/lists/combo/combo-size/combo-size.component.scss new file mode 100644 index 0000000000..a89cfd43a3 --- /dev/null +++ b/src/app/lists/combo/combo-size/combo-size.component.scss @@ -0,0 +1,44 @@ +:host { + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.sizes { + display: grid; + grid-template-columns: max-content repeat(3, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + grid-template-rows: auto repeat(2, 17rem); + align-items: start; + column-gap: 1.75rem; + row-gap: 1rem; +} + +.type-label { + padding-top: 0.75rem; +} + +.small { + --ig-size: var(--ig-size-small); +} + +.medium { + --ig-size: var(--ig-size-medium); +} + +.large { + --ig-size: var(--ig-size-large); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/combo-size/combo-size.component.ts b/src/app/lists/combo/combo-size/combo-size.component.ts new file mode 100644 index 0000000000..0a0c4af1fa --- /dev/null +++ b/src/app/lists/combo/combo-size/combo-size.component.ts @@ -0,0 +1,62 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { IgxInputGroupType } from 'igniteui-angular/input-group'; +import { forkJoin, take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface ComboSize { + label: string; + className: string; +} + +interface ComboType { + label: string; + type: IgxInputGroupType; +} + +@Component({ + selector: 'app-combo-size', + styleUrls: ['./combo-size.component.scss'], + templateUrl: './combo-size.component.html', + imports: [IgxComboComponent] +}) +export class ComboSizeComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + public sizes: ComboSize[] = [ + { label: 'Small', className: 'small' }, + { label: 'Medium', className: 'medium' }, + { label: 'Large', className: 'large' } + ]; + + public types: ComboType[] = [ + { label: 'Border Type', type: 'border' }, + { label: 'Box Type', type: 'box' } + ]; + + private combos = viewChildren(IgxComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + + // The combos are shown open. Opening a combo moves the focus to its search input, so + // the sample is inert until the combos open and the page keeps its focus. + afterNextRender(() => { + const combos = this.combos(); + element.inert = true; + forkJoin(combos.map((combo) => combo.opened.pipe(take(1)))).subscribe(() => { + element.inert = false; + }); + combos.forEach((combo) => combo.open()); + }); + } +} diff --git a/src/app/lists/combo/combo-states/combo-states.component.html b/src/app/lists/combo/combo-states/combo-states.component.html new file mode 100644 index 0000000000..e67c8074b9 --- /dev/null +++ b/src/app/lists/combo/combo-states/combo-states.component.html @@ -0,0 +1,19 @@ +
+ @for (state of states; track state.label) { +
+ {{ state.label }} + + +
+ } +
diff --git a/src/app/lists/combo/combo-states/combo-states.component.scss b/src/app/lists/combo/combo-states/combo-states.component.scss new file mode 100644 index 0000000000..1e7cbd02a2 --- /dev/null +++ b/src/app/lists/combo/combo-states/combo-states.component.scss @@ -0,0 +1,69 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.states { + display: grid; + grid-template-columns: repeat(3, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + column-gap: 1.75rem; + row-gap: 1.25rem; +} + +.state { + display: flex; + flex-direction: column; + min-width: 0; + align-items: center; + gap: 0.5rem; +} + +.state-combo { + width: 100%; + pointer-events: none; +} + +/* The open dropdown takes no pointer input either. */ +.state-combo ::ng-deep .igx-overlay__content { + pointer-events: none; +} + +/* The states belong to the combo's own input group, not to the search input in its dropdown. */ +.state-combo.hover > ::ng-deep igx-input-group { + --border-color: var(--hover-border-color); + --idle-bottom-line-color: var(--hover-bottom-line-color); +} + +.state-combo.focused > ::ng-deep igx-input-group { + --border-color: var(--focused-border-color); + --idle-bottom-line-color: var(--focused-bottom-line-color); +} + +.state-combo.focused > ::ng-deep igx-input-group .igx-input-group__bundle { + box-shadow: inset 0 0 0 0.125rem var(--focused-border-color); +} + +.state-combo.focused > ::ng-deep igx-input-group .igx-input-group__toggle-button { + color: var(--toggle-button-foreground-focus); + background: var(--toggle-button-background-focus--border); +} + +.state-combo.focused > ::ng-deep igx-input-group .igx-input-group__clear-button { + color: var(--clear-button-foreground-focus); + background: var(--clear-button-background-focus); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} diff --git a/src/app/lists/combo/combo-states/combo-states.component.ts b/src/app/lists/combo/combo-states/combo-states.component.ts new file mode 100644 index 0000000000..6c2e8dc297 --- /dev/null +++ b/src/app/lists/combo/combo-states/combo-states.component.ts @@ -0,0 +1,60 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { OverlaySettings } from 'igniteui-angular/core'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface ComboState { + label: string; + className?: string; + value: string[]; + open?: boolean; +} + +@Component({ + selector: 'app-combo-states', + styleUrls: ['./combo-states.component.scss'], + templateUrl: './combo-states.component.html', + imports: [IgxComboComponent, FormsModule] +}) +export class ComboStatesComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + // Hover and focus come from the pointer and the keyboard, so the `hover` and `focused` + // classes show them on the display-only combos. + public states: ComboState[] = [ + { label: 'Idle', value: [] }, + { label: 'Idle & Hover', className: 'hover', value: [] }, + { label: 'Filled', value: ['BY', 'BE'] }, + { label: 'Filled & Hover', className: 'hover', value: ['BY', 'BE'] }, + { label: 'Focused', className: 'focused', value: ['BY', 'BE'] }, + { label: 'Focused (Dropdown On)', className: 'focused', value: ['BY', 'BE'], open: true } + ]; + + // The open dropdown is display-only, so a click elsewhere does not close it. + public overlaySettings: OverlaySettings = { closeOnOutsideClick: false }; + + private combos = viewChildren(IgxComboComponent); + + constructor() { + // The combos are display-only, so the sample is inert. Opening a combo moves the focus + // to its search input, and the inert sample also keeps the focus on the page. + inject>(ElementRef).nativeElement.inert = true; + + // The last combo is shown with its dropdown open. + afterNextRender(() => { + this.combos() + .filter((_, index) => this.states[index].open) + .forEach((combo) => combo.open()); + }); + } +} diff --git a/src/app/lists/combo/combo-styling/combo-styling.component.html b/src/app/lists/combo/combo-styling/combo-styling.component.html index 36303918d3..0b437c6d1e 100644 --- a/src/app/lists/combo/combo-styling/combo-styling.component.html +++ b/src/app/lists/combo/combo-styling/combo-styling.component.html @@ -1,10 +1,11 @@ -
- - - - -
- - + + diff --git a/src/app/lists/combo/combo-styling/combo-styling.component.scss b/src/app/lists/combo/combo-styling/combo-styling.component.scss index 2348955c2f..6feace04af 100644 --- a/src/app/lists/combo/combo-styling/combo-styling.component.scss +++ b/src/app/lists/combo/combo-styling/combo-styling.component.scss @@ -1,23 +1,45 @@ -@use "layout.scss"; -@use "igniteui-angular/theming" as *; +@use "sass:meta"; +@include meta.load-css("igniteui-angular/lib/core/styles/themes/presets/igniteui-bootstrap-light"); -$custom-drop-down-theme: drop-down-theme( - $background-color: #57a5cd, -); +app-combo-styling { + /* styles.scss asks the page for the large size; the combo keeps its own + default medium size here. */ + --ig-size: var(--ig-size-medium); -$custom-combo-theme: combo-theme( - $search-separator-border-color: #1d3743, - $toggle-button-background: #57a5cd, -); + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} -$custom-checkbox-theme: checkbox-theme( - $border-radius: 10px, - $fill-color: #1d3743, - $empty-color: #1d3743, -); +.styled-combo { + --ig-font-family: "Titillium Web", sans-serif; + --ig-input-group-border-color: #1377d5; + --ig-input-group-hover-border-color: #1377d5; + --ig-input-group-focused-border-color: #1377d5; + --ig-input-group-filled-text-color: #212121; + --ig-input-group-focused-bottom-line-color: #007ccf; + --ig-combo-toggle-button-background: #e0e0e0; + --ig-combo-toggle-button-background-focus: #e0e0e0; + --ig-combo-toggle-button-background-focus--border: #e0e0e0; + --ig-combo-toggle-button-foreground: #212121; + --ig-combo-toggle-button-foreground-focus: #212121; + --ig-combo-toggle-button-foreground-filled: #212121; + --ig-drop-down-background-color: #e6f2ff; + --ig-drop-down-header-text-color: #005dcc; + --ig-drop-down-item-text-color: #212121; + --ig-drop-down-hover-item-background: #cce4ff; + --ig-drop-down-focused-item-background: #cce4ff; + --ig-drop-down-selected-item-background: #4ca7e3; + --ig-drop-down-selected-item-text-color: #000; + --ig-drop-down-selected-hover-item-background: #4ca7e3; + --ig-drop-down-selected-hover-item-text-color: #000; + --ig-drop-down-selected-focus-item-background: #4ca7e3; + --ig-drop-down-selected-focus-item-text-color: #000; + --ig-checkbox-empty-color: #005dcc; + --ig-checkbox-fill-color: #005dcc; + --ig-checkbox-tick-color: #fff; -:host { - @include tokens($custom-drop-down-theme); - @include tokens($custom-combo-theme); - @include tokens($custom-checkbox-theme); + width: 17.4375rem; } diff --git a/src/app/lists/combo/combo-styling/combo-styling.component.ts b/src/app/lists/combo/combo-styling/combo-styling.component.ts index 63ebea26f0..ee30b02a4e 100644 --- a/src/app/lists/combo/combo-styling/combo-styling.component.ts +++ b/src/app/lists/combo/combo-styling/combo-styling.component.ts @@ -1,34 +1,44 @@ -import { Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core'; -import { IComboSelectionChangingEventArgs, IgxComboComponent } from 'igniteui-angular/combo'; -import { IgxToastComponent } from 'igniteui-angular/toast'; -import { VerticalAlignment } from 'igniteui-angular/core'; -import { getHeroWeaponsData, IHeroWeapon } from '../../../data/heroData'; +import { Component, ElementRef, ViewEncapsulation, afterNextRender, inject, viewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} @Component({ - selector: 'app-combo', + selector: 'app-combo-styling', + encapsulation: ViewEncapsulation.None, styleUrls: ['./combo-styling.component.scss'], templateUrl: './combo-styling.component.html', - imports: [IgxComboComponent, IgxToastComponent] + imports: [IgxComboComponent, FormsModule] }) -export class ComboStylingComponent implements OnInit { - elem = inject(ElementRef); +export class ComboStylingComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; - @ViewChild('loadToast', { read: IgxToastComponent, static: true }) - public loadToast: IgxToastComponent; + public selection = ['BY']; - public weaponsData: IHeroWeapon[] = []; - - public ngOnInit() { - this.weaponsData = getHeroWeaponsData(); - } + private combo = viewChild.required(IgxComboComponent); - public setWeaponsLimit(event: IComboSelectionChangingEventArgs) { - this.loadToast.positionSettings.verticalDirection = VerticalAlignment.Middle; - // this.loadToast.autoHide = true; + constructor() { + const element = inject>(ElementRef).nativeElement; - if (event.newValue.length > 2) { - event.cancel = true; - this.loadToast.open('Choose only two items'); - } + // The combo is shown open. Opening it moves the focus to its search input, so the + // sample is inert until the combo opens and the page keeps its focus. + afterNextRender(() => { + const combo = this.combo(); + element.inert = true; + combo.opened.pipe(take(1)).subscribe(() => { + element.inert = false; + }); + combo.open(); + }); } } diff --git a/src/app/lists/combo/combo-styling/layout.scss b/src/app/lists/combo/combo-styling/layout.scss deleted file mode 100644 index 82f1579d5d..0000000000 --- a/src/app/lists/combo/combo-styling/layout.scss +++ /dev/null @@ -1,3 +0,0 @@ -.control-wrapper { - padding: 8px; -} \ No newline at end of file diff --git a/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.html b/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.html index 184c269959..259e0133ff 100644 --- a/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.html +++ b/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.html @@ -1,10 +1,11 @@ -
- - - - -
- - + + diff --git a/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.scss b/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.scss index 99f78fbca7..69822a7ff4 100644 --- a/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.scss +++ b/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.scss @@ -1,3 +1,24 @@ -.control-wrapper { - padding: 8px; +@use "sass:meta"; +@import url("https://use.typekit.net/bng2zgw.css"); +@include meta.load-css("igniteui-angular/lib/core/styles/themes/presets/igniteui-bootstrap-light"); + +app-combo-tailwind-styling { + /* The combo palette that the utilities in the template read. */ + --color-combo-border: #a9b8cc; + --color-combo-toggle: #e5ebf3; + --color-combo-surface: #f3f6fa; + --color-combo-selected: #d2dbe8; + --color-combo-accent: #7d91ab; + --color-combo-header: #005dcc; + --color-combo-ink: #212121; + + /* styles.scss asks the page for the large size; the combo keeps its own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; } diff --git a/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.ts b/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.ts index 0bb9922864..35fc05d1f4 100644 --- a/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.ts +++ b/src/app/lists/combo/combo-tailwind-styling/combo-tailwind-styling.component.ts @@ -1,34 +1,44 @@ -import { Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core'; -import { IComboSelectionChangingEventArgs, IgxComboComponent } from 'igniteui-angular/combo'; -import { IgxToastComponent } from 'igniteui-angular/toast'; -import { VerticalAlignment } from 'igniteui-angular/core'; -import { getHeroWeaponsData, IHeroWeapon } from '../../../data/heroData'; +import { Component, ElementRef, ViewEncapsulation, afterNextRender, inject, viewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} @Component({ selector: 'app-combo-tailwind-styling', + encapsulation: ViewEncapsulation.None, styleUrls: ['./combo-tailwind-styling.component.scss'], templateUrl: './combo-tailwind-styling.component.html', - imports: [IgxComboComponent, IgxToastComponent] + imports: [IgxComboComponent, FormsModule] }) -export class ComboTailwindStylingComponent implements OnInit { - elem = inject(ElementRef); +export class ComboTailwindStylingComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; - @ViewChild('loadToast', { read: IgxToastComponent, static: true }) - public loadToast: IgxToastComponent; + public selection = ['BY']; - public weaponsData: IHeroWeapon[] = []; - - public ngOnInit() { - this.weaponsData = getHeroWeaponsData(); - } + private combo = viewChild.required(IgxComboComponent); - public setWeaponsLimit(event: IComboSelectionChangingEventArgs) { - this.loadToast.positionSettings.verticalDirection = VerticalAlignment.Middle; - // this.loadToast.autoHide = true; + constructor() { + const element = inject>(ElementRef).nativeElement; - if (event.newValue.length > 2) { - event.cancel = true; - this.loadToast.open('Choose only two items'); - } + // The combo is shown open. Opening it moves the focus to its search input, so the + // sample is inert until the combo opens and the page keeps its focus. + afterNextRender(() => { + const combo = this.combo(); + element.inert = true; + combo.opened.pipe(take(1)).subscribe(() => { + element.inert = false; + }); + combo.open(); + }); } } diff --git a/src/app/lists/combo/combo-template/combo-template.component.html b/src/app/lists/combo/combo-template/combo-template.component.html index 0c0f50cb65..568828e30d 100644 --- a/src/app/lists/combo/combo-template/combo-template.component.html +++ b/src/app/lists/combo/combo-template/combo-template.component.html @@ -1,42 +1,103 @@ +
+ + @if (templates.item) { + + + + {{ destination[key] }} — {{ destination.country }} · {{ destination.id }} · {{ destination.offset }} + + + } + @if (templates.headerItem) { + + + + {{ header[key] }} + {{ citiesIn(header[key]) }} cities + + + } + @if (templates.header) { + +
+ Destinations by region + +
+
+ } + @if (templates.footer) { + + + + } + @if (templates.empty) { + + + + No city matches "{{ combo.searchValue }}". Add it below, or try another spelling. + + + } + @if (templates.add) { + + + + } + @if (templates.toggleIcon) { + + + + } + @if (templates.clearIcon) { + + + + } + @if (templates.input) { + + } + @if (templates.input) { + + } + @if (templates.input) { + + } + @if (templates.input) { + {{ combo.selection.length }} of {{ destinations.length }} destinations selected + } +
- - - pin_drop - -
- {{ display[key] }} - - {{ display.region }} -
-
+
+ Selection + {{ selectedNames.length ? selectedNames.join(', ') : 'No items selected' }} +
+
- -
Region: {{ display[key] }}
-
+
+ @for (template of templateSwitches; track template.name) { + {{ template.label }} + } +
- -
State - Region
-
- - - - - - - - - - - {{ collapsed ? 'expand_more' : 'expand_less'}} - - - - cancel - - - - No available states - - + +
diff --git a/src/app/lists/combo/combo-template/combo-template.component.scss b/src/app/lists/combo/combo-template/combo-template.component.scss index 52d3d93413..b1fb810603 100644 --- a/src/app/lists/combo/combo-template/combo-template.component.scss +++ b/src/app/lists/combo/combo-template/combo-template.component.scss @@ -1,18 +1,110 @@ -.combo { - margin: 8px; - width: 430px; +:host { + /* styles.scss asks the page for the large size; the combo keeps its own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + /* the combo keeps its width and stays in the center of a sample that has room for it: the + columns on its two sides share the free space equally, and the switches take the one after + the combo */ + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 27rem) minmax(max-content, 1fr); + align-items: center; + height: 100%; + padding: 2rem 1rem; +} + +/* the open dropdown covers the Selection list and ends below it, so the area has the height of + the combo with its dropdown, and the two stay in the middle of the sample */ +.destinations-area { + display: flex; + flex-direction: column; + grid-column: 2; + gap: 1.5rem; + height: 21.5rem; +} + +.selection { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0 1.25rem; +} + +.templates { + display: flex; + flex-direction: column; + grid-column: 3; + gap: 1.625rem; + justify-self: end; + margin-inline-start: 1.5rem; +} + +.destination, +.region { + display: inline-flex; + align-items: center; + gap: 0.5rem; +} + +.flag { + width: 1.25rem; +} + +.region { + width: 100%; +} + +.region igx-icon { + --size: 1.125rem; +} + +.region-count { + margin-inline-start: auto; + font-weight: 400; +} + +.list-header, +.list-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + padding: 0.25rem 0.5rem 0.25rem 1rem; +} + +.empty-list { + display: flex; + align-items: flex-start; + gap: 0.5rem; + width: 100%; + text-align: start; +} + +.empty-list igx-icon { + --size: 1.25rem; + + flex: none; +} + +.add-destination { + width: 100%; +} + +.overlay-outlet { + position: absolute; } -.header-item { - text-transform: capitalize; +// the combo insets and centers its empty and add content; the design keeps them at the start of the list +:host ::ng-deep .igx-combo__add { + align-items: stretch; + padding: 1rem 0.75rem; } -.header-class, .footer-class { - text-align: center; - padding: 4px; - color: var(--ig-secondary-500); +:host ::ng-deep .igx-combo__empty { + justify-content: flex-start; + padding: 0; } -.empty-class { - margin: 8px; +:host ::ng-deep .igx-combo__add-item .igx-button--flat { + margin-inline-start: -0.75rem; } diff --git a/src/app/lists/combo/combo-template/combo-template.component.ts b/src/app/lists/combo/combo-template/combo-template.component.ts index aa9278e971..fd7ea91ec3 100644 --- a/src/app/lists/combo/combo-template/combo-template.component.ts +++ b/src/app/lists/combo/combo-template/combo-template.component.ts @@ -1,22 +1,102 @@ -import { ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'; -import { localData } from './local-data'; -import { IgxComboAddItemDirective, IgxComboClearIconDirective, IgxComboComponent, IgxComboEmptyDirective, IgxComboFooterDirective, IgxComboHeaderDirective, IgxComboHeaderItemDirective, IgxComboItemDirective, IgxComboToggleIconDirective } from 'igniteui-angular/combo'; -import { IgxLabelDirective, IgxPrefixDirective } from 'igniteui-angular/input-group'; -import { IgxIconComponent } from 'igniteui-angular/icon'; +import { Component, ElementRef, afterNextRender, inject, viewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IGX_COMBO_DIRECTIVES, IComboItemAdditionEvent, IgxComboComponent } from 'igniteui-angular/combo'; +import { IgxOverlayOutletDirective, SortingDirection } from 'igniteui-angular/core'; +import { IgxSwitchComponent } from 'igniteui-angular/switch'; +import { IgxIconComponent, IgxIconService } from 'igniteui-angular/icon'; +import { IgxBadgeComponent } from 'igniteui-angular/badge'; import { IgxButtonDirective } from 'igniteui-angular/directives'; +import { take } from 'rxjs'; +import { Destination, destinations } from './local-data'; + +const icons: Record = { + place: '', + public: '', + flight: '', + close: '', + add: '', + location_searching: '' +}; + +type TemplateName = 'item' | 'headerItem' | 'header' | 'footer' | 'empty' | 'add' | 'toggleIcon' | 'clearIcon' | 'input'; + +interface TemplateSwitch { + name: TemplateName; + label: string; +} @Component({ selector: 'app-combo-template', styleUrls: ['./combo-template.component.scss'], templateUrl: './combo-template.component.html', - imports: [IgxComboComponent, IgxLabelDirective, IgxPrefixDirective, IgxIconComponent, IgxComboItemDirective, IgxComboHeaderItemDirective, IgxComboHeaderDirective, IgxComboFooterDirective, IgxComboAddItemDirective, IgxButtonDirective, IgxComboToggleIconDirective, IgxComboClearIconDirective, IgxComboEmptyDirective] + imports: [IGX_COMBO_DIRECTIVES, IgxSwitchComponent, IgxIconComponent, IgxBadgeComponent, IgxButtonDirective, IgxOverlayOutletDirective, FormsModule] }) -export class ComboTemplateComponent implements OnInit { - cdr = inject(ChangeDetectorRef); +export class ComboTemplateComponent { + public readonly SortingDirection = SortingDirection; + + public destinations: Destination[] = destinations; + + public templateSwitches: TemplateSwitch[] = [ + { name: 'item', label: 'Item template' }, + { name: 'headerItem', label: 'Header Item template' }, + { name: 'header', label: 'Header template' }, + { name: 'footer', label: 'Footer template' }, + { name: 'empty', label: 'Empty template' }, + { name: 'add', label: 'Add template' }, + { name: 'toggleIcon', label: 'Toggle Icon template' }, + { name: 'clearIcon', label: 'Clear Icon template' }, + { name: 'input', label: 'Templated input' } + ]; + + // which templates the switches have turned on + public templates: Record = { + item: false, + headerItem: false, + header: false, + footer: false, + empty: false, + add: false, + toggleIcon: false, + clearIcon: false, + input: false + }; + + // the `id` values of the selected destinations + public selected: string[] = []; - public lData: any[]; + private combo = viewChild.required(IgxComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + const iconService = inject(IgxIconService); + + for (const [name, svg] of Object.entries(icons)) { + iconService.addSvgIconFromText(name, svg, 'material'); + } + + // The combo is shown open when the sample loads. Opening it moves the focus to its search + // input, so the sample is inert until the combo opens and the page keeps its focus. + afterNextRender(() => { + const combo = this.combo(); + element.inert = true; + combo.opened.pipe(take(1)).subscribe(() => { + element.inert = false; + }); + combo.open(); + }); + } + + public get selectedNames(): string[] { + return this.destinations.filter((d) => this.selected.includes(d.id)).map((d) => d.name); + } + + public citiesIn(region: string): number { + return this.destinations.filter((d) => d.region === region).length; + } - public ngOnInit() { - this.lData = localData; + // a custom value becomes a destination whose `id` and `name` hold the search text + public onAddition(event: IComboItemAdditionEvent): void { + event.addedItem.country = 'Custom destination'; + event.addedItem.offset = '—'; } } diff --git a/src/app/lists/combo/combo-template/local-data.ts b/src/app/lists/combo/combo-template/local-data.ts index a229ef33ae..c39a52e61e 100644 --- a/src/app/lists/combo/combo-template/local-data.ts +++ b/src/app/lists/combo/combo-template/local-data.ts @@ -1,34 +1,30 @@ -/* eslint-disable quote-props */ -/* eslint-disable @typescript-eslint/naming-convention */ -const division = { - 'New England 01': ['Connecticut', 'Maine', 'Massachusetts'], - 'New England 02': ['New Hampshire', 'Rhode Island', 'Vermont'], - // tslint:disable-next-line:object-literal-sort-keys - 'Mid-Atlantic': ['New Jersey', 'New York', 'Pennsylvania'], - 'East North Central 02': ['Michigan', 'Ohio', 'Wisconsin'], - 'East North Central 01': ['Illinois', 'Indiana'], - 'West North Central 01': ['Missouri', 'Nebraska', 'North Dakota', 'South Dakota'], - 'West North Central 02': ['Iowa', 'Kansas', 'Minnesota'], - 'South Atlantic 01': ['Delaware', 'Florida', 'Georgia', 'Maryland'], - 'South Atlantic 02': ['North Carolina', 'South Carolina', 'Virginia', - 'District of Columbia', 'West Virginia'], - 'East South Central 01': ['Alabama', 'Kentucky'], - 'East South Central 02': ['Mississippi', 'Tennessee'], - 'West South Central': ['Arkansas', 'Louisiana', 'Oklahome', 'Texas'], - 'Mountain': ['Arizona', 'Colorado', 'Idaho', 'Montana', 'Nevada', 'New Mexico', 'Utah', 'Wyoming'], - 'Pacific 01': ['Alaska', 'California'], - 'Pacific 02': ['Hawaii', 'Oregon', 'Washington'] -}; - -const localData: any[] = []; -const keys = Object.keys(division); -for (const key of keys) { - division[key].map((e) => { - localData.push({ - field: e, - region: key.substring(0, key.length - 3) - }); - }); +export interface Destination { + id: string; + name: string; + country: string; + countryCode: string; + region: string; + offset: string; } -export { localData }; +export const destinations: Destination[] = [ + { id: 'SOF', name: 'Sofia', country: 'Bulgaria', countryCode: 'BG', region: 'Europe', offset: 'UTC+2' }, + { id: 'LON', name: 'London', country: 'United Kingdom', countryCode: 'GB', region: 'Europe', offset: 'UTC+0' }, + { id: 'PAR', name: 'Paris', country: 'France', countryCode: 'FR', region: 'Europe', offset: 'UTC+1' }, + { id: 'BER', name: 'Berlin', country: 'Germany', countryCode: 'DE', region: 'Europe', offset: 'UTC+1' }, + { id: 'MAD', name: 'Madrid', country: 'Spain', countryCode: 'ES', region: 'Europe', offset: 'UTC+1' }, + { id: 'ROM', name: 'Rome', country: 'Italy', countryCode: 'IT', region: 'Europe', offset: 'UTC+1' }, + { id: 'CPT', name: 'Cape Town', country: 'South Africa', countryCode: 'ZA', region: 'Africa', offset: 'UTC+2' }, + { id: 'NBO', name: 'Nairobi', country: 'Kenya', countryCode: 'KE', region: 'Africa', offset: 'UTC+3' }, + { id: 'CAI', name: 'Cairo', country: 'Egypt', countryCode: 'EG', region: 'Africa', offset: 'UTC+2' }, + { id: 'RAK', name: 'Marrakesh', country: 'Morocco', countryCode: 'MA', region: 'Africa', offset: 'UTC+1' }, + { id: 'TYO', name: 'Tokyo', country: 'Japan', countryCode: 'JP', region: 'Asia', offset: 'UTC+9' }, + { id: 'SIN', name: 'Singapore', country: 'Singapore', countryCode: 'SG', region: 'Asia', offset: 'UTC+8' }, + { id: 'BKK', name: 'Bangkok', country: 'Thailand', countryCode: 'TH', region: 'Asia', offset: 'UTC+7' }, + { id: 'DXB', name: 'Dubai', country: 'United Arab Emirates', countryCode: 'AE', region: 'Asia', offset: 'UTC+4' }, + { id: 'NYC', name: 'New York', country: 'United States', countryCode: 'US', region: 'Americas', offset: 'UTC-5' }, + { id: 'YTO', name: 'Toronto', country: 'Canada', countryCode: 'CA', region: 'Americas', offset: 'UTC-5' }, + { id: 'MEX', name: 'Mexico City', country: 'Mexico', countryCode: 'MX', region: 'Americas', offset: 'UTC-6' }, + { id: 'SAO', name: 'São Paulo', country: 'Brazil', countryCode: 'BR', region: 'Americas', offset: 'UTC-3' }, + { id: 'BUE', name: 'Buenos Aires', country: 'Argentina', countryCode: 'AR', region: 'Americas', offset: 'UTC-3' } +]; diff --git a/src/app/lists/combo/combo-type/combo-type.component.html b/src/app/lists/combo/combo-type/combo-type.component.html new file mode 100644 index 0000000000..5242e11b6c --- /dev/null +++ b/src/app/lists/combo/combo-type/combo-type.component.html @@ -0,0 +1,21 @@ +
+ @for (type of types; track type.label) { +
+ {{ type.label }} + + + + + + +
+ } +
diff --git a/src/app/lists/combo/combo-type/combo-type.component.scss b/src/app/lists/combo/combo-type/combo-type.component.scss new file mode 100644 index 0000000000..f45cb9a3d9 --- /dev/null +++ b/src/app/lists/combo/combo-type/combo-type.component.scss @@ -0,0 +1,34 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.types { + display: grid; + grid-template-columns: repeat(2, minmax(0, 22.5rem)); + width: 100%; + justify-content: center; + column-gap: 3.75rem; +} + +.type { + display: flex; + flex-direction: column; + min-width: 0; + gap: 0.75rem; +} + +.type-label { + justify-self: center; + text-align: center; + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} diff --git a/src/app/lists/combo/combo-type/combo-type.component.ts b/src/app/lists/combo/combo-type/combo-type.component.ts new file mode 100644 index 0000000000..dbe0a06be9 --- /dev/null +++ b/src/app/lists/combo/combo-type/combo-type.component.ts @@ -0,0 +1,51 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { IgxIconComponent, IgxIconService } from 'igniteui-angular/icon'; +import { IgxInputGroupType, IgxLabelDirective, IgxPrefixDirective } from 'igniteui-angular/input-group'; +import { forkJoin, take } from 'rxjs'; + +const languageIcon = ''; + +interface Option { + id: number; + name: string; + group: string; +} + +interface ComboType { + label: string; + type: IgxInputGroupType; +} + +@Component({ + selector: 'app-combo-type', + styleUrls: ['./combo-type.component.scss'], + templateUrl: './combo-type.component.html', + imports: [IgxComboComponent, IgxLabelDirective, IgxPrefixDirective, IgxIconComponent] +}) +export class ComboTypeComponent { + public options: Option[] = [1, 2, 3, 4, 5].map((id) => ({ id, name: 'Item', group: 'Header' })); + + public types: ComboType[] = [ + { label: 'Border Type', type: 'border' }, + { label: 'Box Type', type: 'box' } + ]; + + private combos = viewChildren(IgxComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + inject(IgxIconService).addSvgIconFromText('language', languageIcon, 'material'); + + // The combos are shown open. Opening a combo moves the focus to its search input, so + // the sample is inert until the combos open and the page keeps its focus. + afterNextRender(() => { + const combos = this.combos(); + element.inert = true; + forkJoin(combos.map((combo) => combo.opened.pipe(take(1)))).subscribe(() => { + element.inert = false; + }); + combos.forEach((combo) => combo.open()); + }); + } +} diff --git a/src/app/lists/combo/combo-validation/combo-validation.component.html b/src/app/lists/combo/combo-validation/combo-validation.component.html new file mode 100644 index 0000000000..c81767a8f2 --- /dev/null +++ b/src/app/lists/combo/combo-validation/combo-validation.component.html @@ -0,0 +1,24 @@ +
+ + @for (state of states; track state.label) { + {{ state.label }} + } + @for (type of types; track type.label) { + {{ type.label }} + @for (state of states; track state.label) { + + @if (state.helperText) { + {{ state.helperText }} + } + + } + } +
diff --git a/src/app/lists/combo/combo-validation/combo-validation.component.scss b/src/app/lists/combo/combo-validation/combo-validation.component.scss new file mode 100644 index 0000000000..f9f40dcc31 --- /dev/null +++ b/src/app/lists/combo/combo-validation/combo-validation.component.scss @@ -0,0 +1,56 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: center; + justify-content: center; + height: 100%; + padding: 1rem; +} + +.states { + display: grid; + grid-template-columns: max-content repeat(3, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + align-items: start; + column-gap: 1.75rem; + row-gap: 1rem; +} + +.type-label { + padding-top: 0.75rem; +} + +.state-combo { + pointer-events: none; +} + +/* The Material theme leaves the bottom line of a valid box combo uncolored, so the + success combos color it with the success color. */ +@container style(--ig-theme: material) { + .state-combo.success > ::ng-deep igx-input-group .igx-input-group__bundle::before { + border-block-end-color: var(--success-secondary-color); + } +} + +.state-combo.warn > ::ng-deep igx-input-group { + --border-color: var(--warning-secondary-color); + --idle-bottom-line-color: var(--warning-secondary-color); +} + +.state-combo.warn igx-hint { + color: var(--warning-secondary-color); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/combo-validation/combo-validation.component.ts b/src/app/lists/combo/combo-validation/combo-validation.component.ts new file mode 100644 index 0000000000..d182430dd1 --- /dev/null +++ b/src/app/lists/combo/combo-validation/combo-validation.component.ts @@ -0,0 +1,70 @@ +import { ChangeDetectorRef, Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxComboComponent } from 'igniteui-angular/combo'; +import { IgxHintDirective, IgxInputGroupType, IgxInputState } from 'igniteui-angular/input-group'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface ValidationState { + label: string; + className: string; + state?: IgxInputState; + helperText?: string; +} + +interface ComboType { + label: string; + type: IgxInputGroupType; +} + +@Component({ + selector: 'app-combo-validation', + styleUrls: ['./combo-validation.component.scss'], + templateUrl: './combo-validation.component.html', + imports: [IgxComboComponent, IgxHintDirective] +}) +export class ComboValidationComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + // The combo has built-in valid and invalid states, so the `warn` class shows the + // warning state on the display-only combos. + public states: ValidationState[] = [ + { label: 'Success', className: 'success', state: IgxInputState.VALID }, + { label: 'Warn', className: 'warn', helperText: 'Additional fees might be included' }, + { label: 'Error', className: 'error', state: IgxInputState.INVALID, helperText: 'Please select a different region' } + ]; + + public types: ComboType[] = [ + { label: 'Border Type', type: 'border' }, + { label: 'Box Type', type: 'box' } + ]; + + private combos = viewChildren(IgxComboComponent); + private cdr = inject(ChangeDetectorRef); + + constructor() { + // The combos are display-only, so the sample is inert. + inject>(ElementRef).nativeElement.inert = true; + + afterNextRender(() => { + for (const combo of this.combos()) { + combo.select(['BY', 'BE']); + } + this.setStates(); + }); + } + + private setStates() { + this.combos().forEach((combo, index) => { + combo.valid = this.states[index % this.states.length].state ?? IgxInputState.INITIAL; + }); + this.cdr.markForCheck(); + } +} diff --git a/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.html b/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.html new file mode 100644 index 0000000000..32223685b7 --- /dev/null +++ b/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.html @@ -0,0 +1,23 @@ +
+ + Enabled + Disabled + @for (row of rows; track $index) { + @let groupStart = $index === 2; + {{ row.label }} + @for (disabled of [false, true]; track disabled) { +
+ + +
+ } + } +
diff --git a/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.scss b/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.scss new file mode 100644 index 0000000000..267bee027b --- /dev/null +++ b/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.scss @@ -0,0 +1,35 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: center; + justify-content: center; + height: 100%; + padding: 1rem; +} + +.states { + display: grid; + grid-template-columns: max-content repeat(2, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + align-items: center; + column-gap: 1.75rem; + row-gap: 1.5rem; +} + +.group-start { + margin-top: 1.5rem; +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.ts b/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.ts new file mode 100644 index 0000000000..b65ac52166 --- /dev/null +++ b/src/app/lists/combo/simple-combo-disabled/simple-combo-disabled.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { IgxInputGroupType } from 'igniteui-angular/input-group'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface DisabledRow { + label: string; + type: IgxInputGroupType; + value?: string; +} + +@Component({ + selector: 'app-simple-combo-disabled', + styleUrls: ['./simple-combo-disabled.component.scss'], + templateUrl: './simple-combo-disabled.component.html', + imports: [IgxSimpleComboComponent, FormsModule] +}) +export class SimpleComboDisabledComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + public rows: DisabledRow[] = [ + { label: 'Idle', type: 'border', value: undefined }, + { label: 'Filled', type: 'border', value: 'BY' }, + { label: 'Idle', type: 'box', value: undefined }, + { label: 'Filled', type: 'box', value: 'BY' } + ]; +} diff --git a/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.html b/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.html new file mode 100644 index 0000000000..6e2a68341a --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.html @@ -0,0 +1,24 @@ + +
+ + @for (state of states; track state.label) { + {{ state.label }} + } + @for (row of rows; track row.label) { + {{ row.label }} + @for (state of row.states; track state.label) { + + + } + } +
diff --git a/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.scss b/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.scss new file mode 100644 index 0000000000..d60697af04 --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.scss @@ -0,0 +1,79 @@ +:host { + /* styles.scss asks the page for the large size; the items keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.items { + display: grid; + grid-template-columns: max-content repeat(4, minmax(0, 11.25rem)); + width: 100%; + justify-content: center; + align-items: center; + column-gap: 1.875rem; + row-gap: 1rem; +} + +/* The second row is further from the first one than the column labels are. */ +.items > :nth-child(n + 11) { + margin-top: 2rem; +} + +/* The list floats, so the element takes the height of its one medium item. */ +.dropdown { + width: 100%; + min-width: 0; + height: 2rem; + pointer-events: none; +} + +/* Only the dropdown of the combo is shown, so the input field is hidden. */ +.dropdown ::ng-deep igx-simple-combo > igx-input-group { + display: none; +} + +/* The states use the colors of the drop-down theme. */ +.dropdown.hover ::ng-deep .igx-drop-down__item { + background: var(--hover-item-background); + color: var(--hover-item-text-color); +} + +.dropdown.focus ::ng-deep .igx-drop-down__item { + background: var(--focused-item-background); + color: var(--focused-item-text-color); +} + +.dropdown.disabled ::ng-deep .igx-drop-down__item { + background: var(--disabled-item-background); + color: var(--disabled-item-text-color); +} + +.dropdown.hover ::ng-deep .igx-drop-down__item--selected { + background: var(--selected-hover-item-background); + color: var(--selected-hover-item-text-color); +} + +.dropdown.focus ::ng-deep .igx-drop-down__item--selected { + background: var(--selected-focus-item-background); + color: var(--selected-focus-item-text-color); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +.state-label:not(.state-column) { + justify-self: end; +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.ts b/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.ts new file mode 100644 index 0000000000..186e2d8a7f --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component.ts @@ -0,0 +1,64 @@ +import { Component, afterNextRender, viewChildren } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ConnectedPositioningStrategy, IBaseCancelableBrowserEventArgs, VerticalAlignment } from 'igniteui-angular/core'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; + +interface Option { + id: number; + name: string; +} + +interface ItemState { + label: string; + className?: string; +} + +interface ItemRow { + label: string; + value?: number; + states: ItemState[]; +} + +@Component({ + selector: 'app-simple-combo-dropdown-items-states', + styleUrls: ['./simple-combo-dropdown-items-states.component.scss'], + templateUrl: './simple-combo-dropdown-items-states.component.html', + imports: [IgxSimpleComboComponent, FormsModule] +}) +export class SimpleComboDropdownItemsStatesComponent { + // The dropdown opens at the top of the element around the combo, where the hidden input field is. + public positionStrategy = new ConnectedPositioningStrategy({ + verticalStartPoint: VerticalAlignment.Top, + verticalDirection: VerticalAlignment.Bottom + }); + + public options: Option[] = [{ id: 1, name: 'Item' }]; + + // Hover, focus, and disabled come from the pointer, the keyboard, and the data, so the + // `hover`, `focus`, and `disabled` classes show them on the display-only items. + public states: ItemState[] = [ + { label: 'Idle' }, + { label: 'Hover', className: 'hover' }, + { label: 'Focus', className: 'focus' }, + { label: 'Disabled', className: 'disabled' } + ]; + + public rows: ItemRow[] = [ + { label: 'Unselected', states: this.states }, + { label: 'Selected', value: 1, states: this.states.slice(0, 3) } + ]; + + private combos = viewChildren(IgxSimpleComboComponent); + + constructor() { + // The dropdowns are shown open. + afterNextRender(() => { + this.combos().forEach((combo) => combo.open()); + }); + } + + // The input fields are hidden, so the dropdowns stay open. + public keepOpen(event: IBaseCancelableBrowserEventArgs): void { + event.cancel = true; + } +} diff --git a/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.html b/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.html new file mode 100644 index 0000000000..372ab527f5 --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.html @@ -0,0 +1,23 @@ +
+ + @for (size of sizes; track size.label) { + {{ size.label }} + } + @for (row of rows; track row.label) { + {{ row.label }} + @for (size of sizes; track size.label) { + + + } + } +
diff --git a/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.scss b/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.scss new file mode 100644 index 0000000000..b0d4e07445 --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.scss @@ -0,0 +1,82 @@ +:host { + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.items { + display: grid; + grid-template-columns: max-content repeat(3, minmax(0, 11.25rem)); + width: 100%; + justify-content: center; + align-items: start; + column-gap: 1.875rem; + row-gap: 1rem; +} + +/* The second row is further from the first one than the column labels are. */ +.items > :nth-child(n + 9) { + margin-top: 1.5rem; +} + +.dropdown { + width: 100%; + min-width: 0; +} + +/* Only the dropdown of the combo is shown, so the input field is hidden. */ +.dropdown ::ng-deep igx-simple-combo > igx-input-group { + display: none; +} + +/* The header row shows only the group header. */ +.header-only ::ng-deep igx-combo-item[role="option"] { + display: none; +} + +/* The scroll track keeps the height of the hidden option too, so the header row leaves it out of + the layout and the list takes the height of the header in every theme and size. */ +.header-only ::ng-deep .igx-virtual-scroll__track { + display: contents; +} + +.header-only ::ng-deep .igx-virtual-scroll__content { + position: static; +} + +/* The list floats, so the element takes the height of its one item in each size. */ +.small { + --ig-size: var(--ig-size-small); + + height: 1.75rem; +} + +.medium { + --ig-size: var(--ig-size-medium); + + height: 2rem; +} + +.large { + --ig-size: var(--ig-size-large); + + height: 2.5rem; +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +/* The row labels are centered on the small items. */ +.state-label:not(.state-column) { + justify-self: end; + line-height: 1.75rem; +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.ts b/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.ts new file mode 100644 index 0000000000..1aa24853a6 --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component.ts @@ -0,0 +1,70 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { ConnectedPositioningStrategy, IBaseCancelableBrowserEventArgs, VerticalAlignment } from 'igniteui-angular/core'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { forkJoin, take } from 'rxjs'; + +interface Option { + id: number; + name: string; + group?: string; +} + +interface ItemRow { + label: string; + className: string; + groupKey?: string; + data: Option[]; +} + +interface DropdownSize { + label: string; + className: string; +} + +@Component({ + selector: 'app-simple-combo-dropdown-items', + styleUrls: ['./simple-combo-dropdown-items.component.scss'], + templateUrl: './simple-combo-dropdown-items.component.html', + imports: [IgxSimpleComboComponent] +}) +export class SimpleComboDropdownItemsComponent { + // The dropdown opens at the top of the element around the combo, where the hidden input field is. + public positionStrategy = new ConnectedPositioningStrategy({ + verticalStartPoint: VerticalAlignment.Top, + verticalDirection: VerticalAlignment.Bottom + }); + + public sizes: DropdownSize[] = [ + { label: 'Small', className: 'small' }, + { label: 'Medium', className: 'medium' }, + { label: 'Large', className: 'large' } + ]; + + // The header row hides its option, so only the group header shows. + public rows: ItemRow[] = [ + { label: 'Header', className: 'header-only', groupKey: 'group', data: [{ id: 1, name: 'Item', group: 'Header' }] }, + { label: 'Item', className: 'item-only', data: [{ id: 1, name: 'Item' }] } + ]; + + private combos = viewChildren(IgxSimpleComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + + // The combos are shown open. Opening a combo moves the focus to its list, so + // the sample is inert until the combos open and the page keeps its focus. + afterNextRender(() => { + const combos = this.combos(); + element.inert = true; + forkJoin(combos.map((combo) => combo.opened.pipe(take(1)))).subscribe(() => { + element.inert = false; + }); + combos.forEach((combo) => combo.open()); + }); + } + + // The input fields are hidden, so the dropdowns stay open and each one works on its own. + public keepOpen(event: IBaseCancelableBrowserEventArgs): void { + event.cancel = true; + } +} diff --git a/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.html b/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.html new file mode 100644 index 0000000000..56dccb41c8 --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.html @@ -0,0 +1,19 @@ +
+ @for (size of sizes; track size.label) { +
+ {{ size.label }} + + +
+ } +
diff --git a/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.scss b/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.scss new file mode 100644 index 0000000000..57f75a5bce --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.scss @@ -0,0 +1,48 @@ +:host { + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.sizes { + display: grid; + grid-template-columns: repeat(3, 11.25rem); + column-gap: 1rem; +} + +.size { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +.dropdown { + width: 100%; +} + +/* Only the dropdown of the combo is shown, so the input field is hidden. */ +.dropdown ::ng-deep igx-simple-combo > igx-input-group { + display: none; +} + + +.small { + --ig-size: var(--ig-size-small); +} + +.medium { + --ig-size: var(--ig-size-medium); +} + +.large { + --ig-size: var(--ig-size-large); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} diff --git a/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.ts b/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.ts new file mode 100644 index 0000000000..dd47db9451 --- /dev/null +++ b/src/app/lists/combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component.ts @@ -0,0 +1,53 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { IBaseCancelableBrowserEventArgs } from 'igniteui-angular/core'; +import { forkJoin, take } from 'rxjs'; + +interface Option { + id: number; + name: string; + group: string; +} + +interface DropdownSize { + label: string; + className: string; +} + +@Component({ + selector: 'app-simple-combo-dropdown-size', + styleUrls: ['./simple-combo-dropdown-size.component.scss'], + templateUrl: './simple-combo-dropdown-size.component.html', + imports: [IgxSimpleComboComponent] +}) +export class SimpleComboDropdownSizeComponent { + public options: Option[] = [1, 2, 3].map((id) => ({ id, name: 'Item', group: 'Header' })); + + public sizes: DropdownSize[] = [ + { label: 'Small', className: 'small' }, + { label: 'Medium', className: 'medium' }, + { label: 'Large', className: 'large' } + ]; + + private combos = viewChildren(IgxSimpleComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + + // The combos are shown open. Opening a combo moves the focus to its list, so + // the sample is inert until the combos open and the page keeps its focus. + afterNextRender(() => { + const combos = this.combos(); + element.inert = true; + forkJoin(combos.map((combo) => combo.opened.pipe(take(1)))).subscribe(() => { + element.inert = false; + }); + combos.forEach((combo) => combo.open()); + }); + } + + // The input fields are hidden, so the dropdowns stay open and each one works on its own. + public keepOpen(event: IBaseCancelableBrowserEventArgs): void { + event.cancel = true; + } +} diff --git a/src/app/lists/combo/simple-combo-main/simple-combo-main.component.html b/src/app/lists/combo/simple-combo-main/simple-combo-main.component.html deleted file mode 100644 index 5aabfc2d6a..0000000000 --- a/src/app/lists/combo/simple-combo-main/simple-combo-main.component.html +++ /dev/null @@ -1,27 +0,0 @@ -
-
- - - @for (range of ranges; track range) { - - } - -
- -
diff --git a/src/app/lists/combo/simple-combo-main/simple-combo-main.component.scss b/src/app/lists/combo/simple-combo-main/simple-combo-main.component.scss deleted file mode 100644 index 2c02a3581b..0000000000 --- a/src/app/lists/combo/simple-combo-main/simple-combo-main.component.scss +++ /dev/null @@ -1,15 +0,0 @@ -.sample-wrapper { - padding: 1rem 1rem 6rem 1rem; - width: 100%; - height: 100%; -} -.combo-section { - display: flex; - justify-content: space-between; - margin-bottom: 3rem; -} - -.input-container { - flex-grow: 1; - max-width: 300px; -} diff --git a/src/app/lists/combo/simple-combo-main/simple-combo-main.component.ts b/src/app/lists/combo/simple-combo-main/simple-combo-main.component.ts deleted file mode 100644 index a5c86e96de..0000000000 --- a/src/app/lists/combo/simple-combo-main/simple-combo-main.component.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { IgRect } from 'igniteui-angular-core'; -import { StockData, updatedStockData } from '../../../data/stocks-data'; -import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; -import { IgxButtonGroupComponent } from 'igniteui-angular/button-group'; -import { IgxButtonDirective } from 'igniteui-angular/directives'; -import { FormsModule } from '@angular/forms'; - -import { IgxFinancialChartCoreModule, IgxFinancialChartModule } from 'igniteui-angular-charts'; - -@Component({ - selector: 'app-simple-combo-main', - templateUrl: 'simple-combo-main.component.html', - styleUrls: ['simple-combo-main.component.scss'], - imports: [IgxSimpleComboComponent, FormsModule, IgxButtonGroupComponent, IgxButtonDirective, IgxFinancialChartModule, IgxFinancialChartCoreModule] -}) -export class SimpleComboMainComponent implements OnInit { - public data: StockData[] = updatedStockData; - public selectedRange: string; - public trendLineType: string; - public trendLineTypes: string[] = [ - 'CubicFit', - 'LinearFit', - 'QuinticFit', - 'QuarticFit', - 'ExponentialFit', - 'PowerLawFit', - 'LogarithmicFit', - 'CumulativeAverage', - 'ExponentialAverage', - 'SimpleAverage', - 'ModifiedAverage', - 'WeightedAverage', - 'None' - ]; - public windowRect: IgRect = { top: 0, left: 0, width: 1, height: 1 }; - public ranges: string[] = ['1M', '3M', '6M', 'YTD', '1Y', 'ALL']; - - public rangeButtonClick(range: string) { - this.selectedRange = range; - let startDate = new Date(Date.now()); - switch (range) { - case '1M': - startDate.setMonth(startDate.getMonth() - 1); - break; - case '3M': - startDate.setMonth(startDate.getMonth() - 3); - break; - case '6M': - startDate.setMonth(startDate.getMonth() - 6); - break; - case 'YTD': - startDate = new Date(startDate.getFullYear(), 0, 1); - break; - case '1Y': - startDate.setFullYear(startDate.getFullYear() - 1); - break; - case 'ALL': - startDate = this.data[0].time; - break; - } - this.windowRect = this.getWindowRect(startDate); - } - - public ngOnInit(): void { - this.selectedRange = this.ranges[0]; - this.trendLineType = this.trendLineTypes[0]; - this.rangeButtonClick(this.selectedRange); - } - - private getWindowRect(startDate: Date): IgRect { - const startDateIndex = this.data.findIndex(d => d.time > startDate); - return { - top: 0, - left: startDateIndex / this.data.length, - width: 1 - startDateIndex / this.data.length, - height: 1 - }; - } -} diff --git a/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.html b/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.html new file mode 100644 index 0000000000..56412b28bd --- /dev/null +++ b/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.html @@ -0,0 +1,11 @@ + + + diff --git a/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.scss b/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.scss new file mode 100644 index 0000000000..3fe352fe48 --- /dev/null +++ b/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.scss @@ -0,0 +1,15 @@ +:host { + /* styles.scss asks the page for the large size; the combo keeps its own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.country { + width: 22.5rem; +} diff --git a/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.ts b/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.ts new file mode 100644 index 0000000000..452a8b7537 --- /dev/null +++ b/src/app/lists/combo/simple-combo-overview/simple-combo-overview.component.ts @@ -0,0 +1,44 @@ +import { Component, ElementRef, afterNextRender, inject, viewChild } from '@angular/core'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { IgxLabelDirective } from 'igniteui-angular/input-group'; +import { take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} + +@Component({ + selector: 'app-simple-combo-overview', + styleUrls: ['./simple-combo-overview.component.scss'], + templateUrl: './simple-combo-overview.component.html', + imports: [IgxSimpleComboComponent, IgxLabelDirective] +}) +export class SimpleComboOverviewComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' }, + { id: 'HR', name: 'Croatia', region: 'Europe' }, + { id: 'CZ', name: 'Czechia', region: 'Europe' }, + { id: 'DK', name: 'Denmark', region: 'Europe' } + ]; + + private combo = viewChild.required(IgxSimpleComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + + // The combo is shown open. Opening it moves the focus to its list, so the + // sample is inert until the combo opens and the page keeps its focus. + afterNextRender(() => { + const combo = this.combo(); + element.inert = true; + combo.opened.pipe(take(1)).subscribe(() => { + element.inert = false; + }); + combo.open(); + }); + } +} diff --git a/src/app/lists/combo/simple-combo-size/simple-combo-size.component.html b/src/app/lists/combo/simple-combo-size/simple-combo-size.component.html new file mode 100644 index 0000000000..23d29512ad --- /dev/null +++ b/src/app/lists/combo/simple-combo-size/simple-combo-size.component.html @@ -0,0 +1,21 @@ +
+ + @for (size of sizes; track size.label) { + {{ size.label }} + } + @for (type of types; track type.label) { + {{ type.label }} + @for (size of sizes; track size.label) { + + + } + } +
diff --git a/src/app/lists/combo/simple-combo-size/simple-combo-size.component.scss b/src/app/lists/combo/simple-combo-size/simple-combo-size.component.scss new file mode 100644 index 0000000000..a89cfd43a3 --- /dev/null +++ b/src/app/lists/combo/simple-combo-size/simple-combo-size.component.scss @@ -0,0 +1,44 @@ +:host { + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.sizes { + display: grid; + grid-template-columns: max-content repeat(3, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + grid-template-rows: auto repeat(2, 17rem); + align-items: start; + column-gap: 1.75rem; + row-gap: 1rem; +} + +.type-label { + padding-top: 0.75rem; +} + +.small { + --ig-size: var(--ig-size-small); +} + +.medium { + --ig-size: var(--ig-size-medium); +} + +.large { + --ig-size: var(--ig-size-large); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/simple-combo-size/simple-combo-size.component.ts b/src/app/lists/combo/simple-combo-size/simple-combo-size.component.ts new file mode 100644 index 0000000000..0f1324b698 --- /dev/null +++ b/src/app/lists/combo/simple-combo-size/simple-combo-size.component.ts @@ -0,0 +1,62 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { IgxInputGroupType } from 'igniteui-angular/input-group'; +import { forkJoin, take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface ComboSize { + label: string; + className: string; +} + +interface ComboType { + label: string; + type: IgxInputGroupType; +} + +@Component({ + selector: 'app-simple-combo-size', + styleUrls: ['./simple-combo-size.component.scss'], + templateUrl: './simple-combo-size.component.html', + imports: [IgxSimpleComboComponent] +}) +export class SimpleComboSizeComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + public sizes: ComboSize[] = [ + { label: 'Small', className: 'small' }, + { label: 'Medium', className: 'medium' }, + { label: 'Large', className: 'large' } + ]; + + public types: ComboType[] = [ + { label: 'Border Type', type: 'border' }, + { label: 'Box Type', type: 'box' } + ]; + + private combos = viewChildren(IgxSimpleComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + + // The combos are shown open. Opening a combo moves the focus to its list, so + // the sample is inert until the combos open and the page keeps its focus. + afterNextRender(() => { + const combos = this.combos(); + element.inert = true; + forkJoin(combos.map((combo) => combo.opened.pipe(take(1)))).subscribe(() => { + element.inert = false; + }); + combos.forEach((combo) => combo.open()); + }); + } +} diff --git a/src/app/lists/combo/simple-combo-states/simple-combo-states.component.html b/src/app/lists/combo/simple-combo-states/simple-combo-states.component.html new file mode 100644 index 0000000000..b4e11c1240 --- /dev/null +++ b/src/app/lists/combo/simple-combo-states/simple-combo-states.component.html @@ -0,0 +1,18 @@ +
+ @for (state of states; track state.label) { +
+ {{ state.label }} + + +
+ } +
diff --git a/src/app/lists/combo/simple-combo-states/simple-combo-states.component.scss b/src/app/lists/combo/simple-combo-states/simple-combo-states.component.scss new file mode 100644 index 0000000000..1e7cbd02a2 --- /dev/null +++ b/src/app/lists/combo/simple-combo-states/simple-combo-states.component.scss @@ -0,0 +1,69 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.states { + display: grid; + grid-template-columns: repeat(3, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + column-gap: 1.75rem; + row-gap: 1.25rem; +} + +.state { + display: flex; + flex-direction: column; + min-width: 0; + align-items: center; + gap: 0.5rem; +} + +.state-combo { + width: 100%; + pointer-events: none; +} + +/* The open dropdown takes no pointer input either. */ +.state-combo ::ng-deep .igx-overlay__content { + pointer-events: none; +} + +/* The states belong to the combo's own input group, not to the search input in its dropdown. */ +.state-combo.hover > ::ng-deep igx-input-group { + --border-color: var(--hover-border-color); + --idle-bottom-line-color: var(--hover-bottom-line-color); +} + +.state-combo.focused > ::ng-deep igx-input-group { + --border-color: var(--focused-border-color); + --idle-bottom-line-color: var(--focused-bottom-line-color); +} + +.state-combo.focused > ::ng-deep igx-input-group .igx-input-group__bundle { + box-shadow: inset 0 0 0 0.125rem var(--focused-border-color); +} + +.state-combo.focused > ::ng-deep igx-input-group .igx-input-group__toggle-button { + color: var(--toggle-button-foreground-focus); + background: var(--toggle-button-background-focus--border); +} + +.state-combo.focused > ::ng-deep igx-input-group .igx-input-group__clear-button { + color: var(--clear-button-foreground-focus); + background: var(--clear-button-background-focus); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} diff --git a/src/app/lists/combo/simple-combo-states/simple-combo-states.component.ts b/src/app/lists/combo/simple-combo-states/simple-combo-states.component.ts new file mode 100644 index 0000000000..cfd514d2bc --- /dev/null +++ b/src/app/lists/combo/simple-combo-states/simple-combo-states.component.ts @@ -0,0 +1,67 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { OverlaySettings } from 'igniteui-angular/core'; +import { take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface ComboState { + label: string; + className?: string; + value?: string; + open?: boolean; +} + +@Component({ + selector: 'app-simple-combo-states', + styleUrls: ['./simple-combo-states.component.scss'], + templateUrl: './simple-combo-states.component.html', + imports: [IgxSimpleComboComponent, FormsModule] +}) +export class SimpleComboStatesComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + // Hover and focus come from the pointer and the keyboard, so the `hover` and `focused` + // classes show them on the display-only combos. + public states: ComboState[] = [ + { label: 'Idle' }, + { label: 'Idle & Hover', className: 'hover' }, + { label: 'Filled', value: 'BY' }, + { label: 'Filled & Hover', className: 'hover', value: 'BY' }, + { label: 'Focused', className: 'focused', value: 'BY' }, + { label: 'Focused (Dropdown On)', className: 'focused', value: 'BY', open: true } + ]; + + // The open dropdown is display-only, so a click elsewhere does not close it. + public overlaySettings: OverlaySettings = { closeOnOutsideClick: false }; + + private combos = viewChildren(IgxSimpleComboComponent); + + constructor() { + // The combos are display-only, so the sample is inert. Opening a combo moves the focus + // to its list, and the inert sample also keeps the focus on the page. + inject>(ElementRef).nativeElement.inert = true; + + // The last combo is shown with its dropdown open. The bound value filters its list by the + // text of the selected item, so the filter is cleared once the dropdown opens. + afterNextRender(() => { + this.combos() + .filter((_, index) => this.states[index].open) + .forEach((combo) => { + combo.opened.pipe(take(1)).subscribe(() => { + combo.filterValue = ''; + }); + combo.open(); + }); + }); + } +} diff --git a/src/app/lists/combo/simple-combo-styling/layout.scss b/src/app/lists/combo/simple-combo-styling/layout.scss deleted file mode 100644 index f01b3ab9dc..0000000000 --- a/src/app/lists/combo/simple-combo-styling/layout.scss +++ /dev/null @@ -1,4 +0,0 @@ -.sample-wrapper { - width: 430px; - margin: 16px; -} diff --git a/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.html b/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.html index c07b0d5d04..a8375c778a 100644 --- a/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.html +++ b/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.html @@ -1,8 +1,10 @@ -
-
- - -
-
+ + diff --git a/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.scss b/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.scss index 597944a0b7..ea3135e7a0 100644 --- a/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.scss +++ b/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.scss @@ -1,29 +1,42 @@ -@use "layout.scss"; -@use "igniteui-angular/theming" as *; +@use "sass:meta"; +@include meta.load-css("igniteui-angular/lib/core/styles/themes/presets/igniteui-bootstrap-light"); -$custom-simple-combo-theme: combo-theme( - $empty-list-background: #1a5214 -); +app-simple-combo-styling { + /* styles.scss asks the page for the large size; the combo keeps its own + default medium size here. */ + --ig-size: var(--ig-size-medium); -$custom-drop-down-theme: drop-down-theme( - $background-color: #d9f5d6, - $header-text-color: #1a5214, - $item-text-color: #1a5214, - - $focused-item-background: #72da67, - $focused-item-text-color: #1a5214, - $hover-item-background: #a0e698, - $hover-item-text-color: #1a5214, + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} - $selected-item-background: #a0e698, - $selected-item-text-color: #1a5214, - $selected-hover-item-background: #72da67, - $selected-hover-item-text-color: #1a5214, - $selected-focus-item-background: #72da67, - $selected-focus-item-text-color: #1a5214, -); +.styled-combo { + --ig-font-family: "Titillium Web", sans-serif; + --ig-input-group-border-color: #1377d5; + --ig-input-group-hover-border-color: #1377d5; + --ig-input-group-focused-border-color: #1377d5; + --ig-input-group-filled-text-color: #212121; + --ig-input-group-focused-bottom-line-color: #007ccf; + --ig-combo-toggle-button-background: #e0e0e0; + --ig-combo-toggle-button-background-focus: #e0e0e0; + --ig-combo-toggle-button-background-focus--border: #e0e0e0; + --ig-combo-toggle-button-foreground: #212121; + --ig-combo-toggle-button-foreground-focus: #212121; + --ig-combo-toggle-button-foreground-filled: #212121; + --ig-drop-down-background-color: #e6f2ff; + --ig-drop-down-header-text-color: #005dcc; + --ig-drop-down-item-text-color: #212121; + --ig-drop-down-hover-item-background: #cce4ff; + --ig-drop-down-focused-item-background: #cce4ff; + --ig-drop-down-selected-item-background: #4ca7e3; + --ig-drop-down-selected-item-text-color: #000; + --ig-drop-down-selected-hover-item-background: #4ca7e3; + --ig-drop-down-selected-hover-item-text-color: #000; + --ig-drop-down-selected-focus-item-background: #4ca7e3; + --ig-drop-down-selected-focus-item-text-color: #000; -:host { - @include tokens($custom-simple-combo-theme); - @include tokens($custom-drop-down-theme); + width: 17.4375rem; } diff --git a/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.ts b/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.ts index b28e790883..d1b66c0f10 100644 --- a/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.ts +++ b/src/app/lists/combo/simple-combo-styling/simple-combo-styling.component.ts @@ -1,19 +1,46 @@ -import { Component, ElementRef, OnInit, inject } from '@angular/core'; -import { City, getCitiesByPopulation } from '../../../data/cities15000-regions-countries'; +import { Component, ElementRef, ViewEncapsulation, afterNextRender, inject, viewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} @Component({ selector: 'app-simple-combo-styling', - templateUrl: 'simple-combo-styling.component.html', - styleUrls: ['simple-combo-styling.component.scss'], - imports: [IgxSimpleComboComponent] + encapsulation: ViewEncapsulation.None, + styleUrls: ['./simple-combo-styling.component.scss'], + templateUrl: './simple-combo-styling.component.html', + imports: [IgxSimpleComboComponent, FormsModule] }) -export class SimpleComboStylingComponent implements OnInit { - element = inject(ElementRef); +export class SimpleComboStylingComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + public selection = 'BY'; + + private combo = viewChild.required(IgxSimpleComboComponent); - public cities: City[]; + constructor() { + const element = inject>(ElementRef).nativeElement; - ngOnInit(): void { - this.cities = getCitiesByPopulation(10000000); + // The combo is shown open. Opening it moves the focus to its list, so the sample is inert + // until the combo opens and the page keeps its focus. The bound value filters the list by + // the text of the selected item, so the filter is cleared once the dropdown opens. + afterNextRender(() => { + const combo = this.combo(); + element.inert = true; + combo.opened.pipe(take(1)).subscribe(() => { + element.inert = false; + combo.filterValue = ''; + }); + combo.open(); + }); } } diff --git a/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.html b/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.html new file mode 100644 index 0000000000..245fdc03b8 --- /dev/null +++ b/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.html @@ -0,0 +1,10 @@ + + diff --git a/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.scss b/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.scss new file mode 100644 index 0000000000..1043be4618 --- /dev/null +++ b/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.scss @@ -0,0 +1,24 @@ +@use "sass:meta"; +@import url("https://use.typekit.net/bng2zgw.css"); +@include meta.load-css("igniteui-angular/lib/core/styles/themes/presets/igniteui-bootstrap-light"); + +app-simple-combo-tailwind-styling { + /* The combo palette that the utilities in the template read. */ + --color-combo-border: #a9b8cc; + --color-combo-toggle: #e5ebf3; + --color-combo-surface: #f3f6fa; + --color-combo-selected: #d2dbe8; + --color-combo-accent: #7d91ab; + --color-combo-header: #005dcc; + --color-combo-ink: #212121; + + /* styles.scss asks the page for the large size; the combo keeps its own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} diff --git a/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.ts b/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.ts new file mode 100644 index 0000000000..4372e44921 --- /dev/null +++ b/src/app/lists/combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component.ts @@ -0,0 +1,46 @@ +import { Component, ElementRef, ViewEncapsulation, afterNextRender, inject, viewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { take } from 'rxjs'; + +interface Country { + id: string; + name: string; + region: string; +} + +@Component({ + selector: 'app-simple-combo-tailwind-styling', + encapsulation: ViewEncapsulation.None, + styleUrls: ['./simple-combo-tailwind-styling.component.scss'], + templateUrl: './simple-combo-tailwind-styling.component.html', + imports: [IgxSimpleComboComponent, FormsModule] +}) +export class SimpleComboTailwindStylingComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + public selection = 'BY'; + + private combo = viewChild.required(IgxSimpleComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + + // The combo is shown open. Opening it moves the focus to its list, so the sample is inert + // until the combo opens and the page keeps its focus. The bound value filters the list by + // the text of the selected item, so the filter is cleared once the dropdown opens. + afterNextRender(() => { + const combo = this.combo(); + element.inert = true; + combo.opened.pipe(take(1)).subscribe(() => { + element.inert = false; + combo.filterValue = ''; + }); + combo.open(); + }); + } +} diff --git a/src/app/lists/combo/simple-combo-type/simple-combo-type.component.html b/src/app/lists/combo/simple-combo-type/simple-combo-type.component.html new file mode 100644 index 0000000000..1d1dd98c7d --- /dev/null +++ b/src/app/lists/combo/simple-combo-type/simple-combo-type.component.html @@ -0,0 +1,20 @@ +
+ @for (type of types; track type.label) { +
+ {{ type.label }} + + + + + + +
+ } +
diff --git a/src/app/lists/combo/simple-combo-type/simple-combo-type.component.scss b/src/app/lists/combo/simple-combo-type/simple-combo-type.component.scss new file mode 100644 index 0000000000..f45cb9a3d9 --- /dev/null +++ b/src/app/lists/combo/simple-combo-type/simple-combo-type.component.scss @@ -0,0 +1,34 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: flex-start; + justify-content: center; + height: 100%; + padding: 2rem 1rem; +} + +.types { + display: grid; + grid-template-columns: repeat(2, minmax(0, 22.5rem)); + width: 100%; + justify-content: center; + column-gap: 3.75rem; +} + +.type { + display: flex; + flex-direction: column; + min-width: 0; + gap: 0.75rem; +} + +.type-label { + justify-self: center; + text-align: center; + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} diff --git a/src/app/lists/combo/simple-combo-type/simple-combo-type.component.ts b/src/app/lists/combo/simple-combo-type/simple-combo-type.component.ts new file mode 100644 index 0000000000..18f7450a35 --- /dev/null +++ b/src/app/lists/combo/simple-combo-type/simple-combo-type.component.ts @@ -0,0 +1,51 @@ +import { Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { IgxIconComponent, IgxIconService } from 'igniteui-angular/icon'; +import { IgxInputGroupType, IgxLabelDirective, IgxPrefixDirective } from 'igniteui-angular/input-group'; +import { forkJoin, take } from 'rxjs'; + +const languageIcon = ''; + +interface Option { + id: number; + name: string; + group: string; +} + +interface ComboType { + label: string; + type: IgxInputGroupType; +} + +@Component({ + selector: 'app-simple-combo-type', + styleUrls: ['./simple-combo-type.component.scss'], + templateUrl: './simple-combo-type.component.html', + imports: [IgxSimpleComboComponent, IgxLabelDirective, IgxPrefixDirective, IgxIconComponent] +}) +export class SimpleComboTypeComponent { + public options: Option[] = [1, 2, 3, 4, 5].map((id) => ({ id, name: 'Item', group: 'Header' })); + + public types: ComboType[] = [ + { label: 'Border Type', type: 'border' }, + { label: 'Box Type', type: 'box' } + ]; + + private combos = viewChildren(IgxSimpleComboComponent); + + constructor() { + const element = inject>(ElementRef).nativeElement; + inject(IgxIconService).addSvgIconFromText('language', languageIcon, 'material'); + + // The combos are shown open. Opening a combo moves the focus to its list, so + // the sample is inert until the combos open and the page keeps its focus. + afterNextRender(() => { + const combos = this.combos(); + element.inert = true; + forkJoin(combos.map((combo) => combo.opened.pipe(take(1)))).subscribe(() => { + element.inert = false; + }); + combos.forEach((combo) => combo.open()); + }); + } +} diff --git a/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.html b/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.html deleted file mode 100644 index 0e722240a3..0000000000 --- a/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
- - -
- @if (!!selectedCity) { -
-
Selected City's Id:
-
{{ selectedCity }}
-
- } -
diff --git a/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.scss b/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.scss deleted file mode 100644 index c9fbf094c5..0000000000 --- a/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.scss +++ /dev/null @@ -1,9 +0,0 @@ -.sample-wrapper { - width: 430px; - margin: 16px; -} - -.combo-section { - width: 100%; - margin-bottom: 16px; -} diff --git a/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.ts b/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.ts deleted file mode 100644 index 1a87c034c9..0000000000 --- a/src/app/lists/combo/simple-combo-usage/simple-combo-usage.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { City, getCitiesByPopulation } from '../../../data/cities15000-regions-countries'; -import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; -import { FormsModule } from '@angular/forms'; - - -@Component({ - selector: 'app-simple-combo-usage', - templateUrl: 'simple-combo-usage.component.html', - styleUrls: ['simple-combo-usage.component.scss'], - imports: [IgxSimpleComboComponent, FormsModule] -}) -export class SimpleComboUsageComponent implements OnInit { - public cities: City[]; - public selectedCity: number; - - public ngOnInit(): void { - this.cities = getCitiesByPopulation(10000000); - this.selectedCity = this.cities[0].id; - } -} diff --git a/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.html b/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.html new file mode 100644 index 0000000000..8f6f0da221 --- /dev/null +++ b/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.html @@ -0,0 +1,23 @@ +
+ + @for (state of states; track state.label) { + {{ state.label }} + } + @for (type of types; track type.label) { + {{ type.label }} + @for (state of states; track state.label) { + + @if (state.helperText) { + {{ state.helperText }} + } + + } + } +
diff --git a/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.scss b/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.scss new file mode 100644 index 0000000000..f9f40dcc31 --- /dev/null +++ b/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.scss @@ -0,0 +1,56 @@ +:host { + /* styles.scss asks the page for the large size; the combos keep their own + default medium size here. */ + --ig-size: var(--ig-size-medium); + + display: flex; + align-items: center; + justify-content: center; + height: 100%; + padding: 1rem; +} + +.states { + display: grid; + grid-template-columns: max-content repeat(3, minmax(0, 17.4375rem)); + width: 100%; + justify-content: center; + align-items: start; + column-gap: 1.75rem; + row-gap: 1rem; +} + +.type-label { + padding-top: 0.75rem; +} + +.state-combo { + pointer-events: none; +} + +/* The Material theme leaves the bottom line of a valid box combo uncolored, so the + success combos color it with the success color. */ +@container style(--ig-theme: material) { + .state-combo.success > ::ng-deep igx-input-group .igx-input-group__bundle::before { + border-block-end-color: var(--success-secondary-color); + } +} + +.state-combo.warn > ::ng-deep igx-input-group { + --border-color: var(--warning-secondary-color); + --idle-bottom-line-color: var(--warning-secondary-color); +} + +.state-combo.warn igx-hint { + color: var(--warning-secondary-color); +} + +.state-label { + font: 400 13px / 20px "Aktiv Grotesk", sans-serif; + letter-spacing: 0.3px; + color: var(--ig-gray-600); +} + +.state-column { + justify-self: center; +} diff --git a/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.ts b/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.ts new file mode 100644 index 0000000000..52e6add482 --- /dev/null +++ b/src/app/lists/combo/simple-combo-validation/simple-combo-validation.component.ts @@ -0,0 +1,70 @@ +import { ChangeDetectorRef, Component, ElementRef, afterNextRender, inject, viewChildren } from '@angular/core'; +import { IgxSimpleComboComponent } from 'igniteui-angular/simple-combo'; +import { IgxHintDirective, IgxInputGroupType, IgxInputState } from 'igniteui-angular/input-group'; + +interface Country { + id: string; + name: string; + region: string; +} + +interface ValidationState { + label: string; + className: string; + state?: IgxInputState; + helperText?: string; +} + +interface ComboType { + label: string; + type: IgxInputGroupType; +} + +@Component({ + selector: 'app-simple-combo-validation', + styleUrls: ['./simple-combo-validation.component.scss'], + templateUrl: './simple-combo-validation.component.html', + imports: [IgxSimpleComboComponent, IgxHintDirective] +}) +export class SimpleComboValidationComponent { + public countries: Country[] = [ + { id: 'BY', name: 'Belarus', region: 'Europe' }, + { id: 'BE', name: 'Belgium', region: 'Europe' }, + { id: 'BG', name: 'Bulgaria', region: 'Europe' } + ]; + + // The combo has built-in valid and invalid states, so the `warn` class shows the + // warning state on the display-only combos. + public states: ValidationState[] = [ + { label: 'Success', className: 'success', state: IgxInputState.VALID }, + { label: 'Warn', className: 'warn', helperText: 'Additional fees might be included' }, + { label: 'Error', className: 'error', state: IgxInputState.INVALID, helperText: 'Please select a different region' } + ]; + + public types: ComboType[] = [ + { label: 'Border Type', type: 'border' }, + { label: 'Box Type', type: 'box' } + ]; + + private combos = viewChildren(IgxSimpleComboComponent); + private cdr = inject(ChangeDetectorRef); + + constructor() { + // The combos are display-only, so the sample is inert. + inject>(ElementRef).nativeElement.inert = true; + + afterNextRender(() => { + for (const combo of this.combos()) { + combo.select('BY'); + } + this.setStates(); + }); + } + + private setStates() { + this.combos().forEach((combo, index) => { + combo.valid = this.states[index % this.states.length].state ?? IgxInputState.INITIAL; + }); + this.cdr.markForCheck(); + } +} diff --git a/src/app/lists/lists-routes-data.ts b/src/app/lists/lists-routes-data.ts index e2d25c0a23..8bcd96109e 100644 --- a/src/app/lists/lists-routes-data.ts +++ b/src/app/lists/lists-routes-data.ts @@ -10,8 +10,24 @@ export const listsRoutesData = { 'combo-tailwind-styling': { displayName: 'Combo Tailwind Styling', parentName: 'Combo' }, 'combo-template': { displayName: 'Templating Combo', parentName: 'Combo' }, 'combo-overlay': { displayName: 'Combo Overlay', parentName: 'Combo' }, - 'simple-combo-main': { displayName: 'Main Simple Combo', parentName: 'Combo'}, - 'simple-combo-usage': { displayName: 'Usage Simple Combo', parentName: 'Combo'}, + 'combo-overview': { displayName: 'Combo Overview', parentName: 'Combo' }, + 'combo-type': { displayName: 'Combo Type', parentName: 'Combo' }, + 'combo-disabled': { displayName: 'Combo Disabled', parentName: 'Combo' }, + 'combo-size': { displayName: 'Combo Size', parentName: 'Combo' }, + 'combo-states': { displayName: 'Combo States', parentName: 'Combo' }, + 'combo-validation': { displayName: 'Combo Validation', parentName: 'Combo' }, + 'combo-dropdown-size': { displayName: 'Combo Dropdown Size', parentName: 'Combo' }, + 'combo-search-input': { displayName: 'Combo Search Input', parentName: 'Combo' }, + 'simple-combo-overview': { displayName: 'Simple Combo Overview', parentName: 'Combo' }, + 'simple-combo-type': { displayName: 'Simple Combo Type', parentName: 'Combo' }, + 'simple-combo-disabled': { displayName: 'Simple Combo Disabled', parentName: 'Combo' }, + 'simple-combo-size': { displayName: 'Simple Combo Size', parentName: 'Combo' }, + 'simple-combo-states': { displayName: 'Simple Combo States', parentName: 'Combo' }, + 'simple-combo-validation': { displayName: 'Simple Combo Validation', parentName: 'Combo' }, + 'simple-combo-dropdown-size': { displayName: 'Simple Combo Dropdown Size', parentName: 'Combo' }, + 'simple-combo-dropdown-items': { displayName: 'Simple Combo Dropdown Items', parentName: 'Combo' }, + 'simple-combo-dropdown-items-states': { displayName: 'Simple Combo Dropdown Items States', parentName: 'Combo' }, + 'simple-combo-tailwind-styling': { displayName: 'Simple Combo Tailwind Styling', parentName: 'Combo' }, 'simple-combo-cascading': { displayName: 'Cascading Simple Combo', parentName: 'Combo'}, 'simple-combo-styling': { displayName: 'Simple Combo Styling', parentName: 'Combo'}, 'simple-combo-remote': { displayName: 'Remote Simple Combo', parentName: 'Combo'}, diff --git a/src/app/lists/lists.routes.ts b/src/app/lists/lists.routes.ts index 3036dba6b7..c93da432aa 100644 --- a/src/app/lists/lists.routes.ts +++ b/src/app/lists/lists.routes.ts @@ -8,6 +8,14 @@ import { ComboStylingComponent } from './combo/combo-styling/combo-styling.compo import { ComboTailwindStylingComponent } from './combo/combo-tailwind-styling/combo-tailwind-styling.component'; import { ComboValueKeyComponent } from './combo/combo-valuekey/combo-valuekey.component'; import { ComboTemplateComponent } from './combo/combo-template/combo-template.component'; +import { ComboOverviewComponent } from './combo/combo-overview/combo-overview.component'; +import { ComboTypeComponent } from './combo/combo-type/combo-type.component'; +import { ComboDisabledComponent } from './combo/combo-disabled/combo-disabled.component'; +import { ComboSizeComponent } from './combo/combo-size/combo-size.component'; +import { ComboStatesComponent } from './combo/combo-states/combo-states.component'; +import { ComboValidationComponent } from './combo/combo-validation/combo-validation.component'; +import { ComboDropdownSizeComponent } from './combo/combo-dropdown-size/combo-dropdown-size.component'; +import { ComboSearchInputComponent } from './combo/combo-search-input/combo-search-input.component'; import { ListChatSampleComponent } from './list/list-chat-sample/list-chat-sample.component'; import { ListSample1Component } from './list/list-sample-1/list-sample-1.component'; import { ListSample2Component } from './list/list-sample-2/list-sample-2.component'; @@ -24,8 +32,16 @@ import { TreeBasicSampleComponent } from './tree/tree-basic-sample/tree-basic-sa import { TreeAdvancedSampleComponent } from './tree/tree-advanced-sample/tree-advanced-sample.component'; import { TreeStylingComponent } from './tree/tree-styling/tree-styling.component'; import { TreeTailwindStylingComponent } from './tree/tree-tailwind-styling/tree-tailwind-styling.component'; -import { SimpleComboMainComponent } from './combo/simple-combo-main/simple-combo-main.component'; -import { SimpleComboUsageComponent } from './combo/simple-combo-usage/simple-combo-usage.component'; +import { SimpleComboOverviewComponent } from './combo/simple-combo-overview/simple-combo-overview.component'; +import { SimpleComboTypeComponent } from './combo/simple-combo-type/simple-combo-type.component'; +import { SimpleComboDisabledComponent } from './combo/simple-combo-disabled/simple-combo-disabled.component'; +import { SimpleComboSizeComponent } from './combo/simple-combo-size/simple-combo-size.component'; +import { SimpleComboStatesComponent } from './combo/simple-combo-states/simple-combo-states.component'; +import { SimpleComboValidationComponent } from './combo/simple-combo-validation/simple-combo-validation.component'; +import { SimpleComboDropdownSizeComponent } from './combo/simple-combo-dropdown-size/simple-combo-dropdown-size.component'; +import { SimpleComboDropdownItemsComponent } from './combo/simple-combo-dropdown-items/simple-combo-dropdown-items.component'; +import { SimpleComboDropdownItemsStatesComponent } from './combo/simple-combo-dropdown-items-states/simple-combo-dropdown-items-states.component'; +import { SimpleComboTailwindStylingComponent } from './combo/simple-combo-tailwind-styling/simple-combo-tailwind-styling.component'; import { SimpleComboCascadingComponent } from './combo/simple-combo-cascading/simple-combo-cascading.component'; import { SimpleComboStylingComponent } from './combo/simple-combo-styling/simple-combo-styling.component'; import { ListItemSelectionComponent } from './list/list-item-selection/list-item-selection.component'; @@ -78,14 +94,94 @@ export const ListsRoutes: Routes = [ path: 'combo-overlay' }, { - component: SimpleComboMainComponent, - data: listsRoutesData['simple-combo-main'], - path: 'simple-combo-main' + component: ComboOverviewComponent, + data: listsRoutesData['combo-overview'], + path: 'combo-overview' }, { - component: SimpleComboUsageComponent, - data: listsRoutesData['simple-combo-usage'], - path: 'simple-combo-usage' + component: ComboTypeComponent, + data: listsRoutesData['combo-type'], + path: 'combo-type' + }, + { + component: ComboDisabledComponent, + data: listsRoutesData['combo-disabled'], + path: 'combo-disabled' + }, + { + component: ComboSizeComponent, + data: listsRoutesData['combo-size'], + path: 'combo-size' + }, + { + component: ComboStatesComponent, + data: listsRoutesData['combo-states'], + path: 'combo-states' + }, + { + component: ComboValidationComponent, + data: listsRoutesData['combo-validation'], + path: 'combo-validation' + }, + { + component: ComboDropdownSizeComponent, + data: listsRoutesData['combo-dropdown-size'], + path: 'combo-dropdown-size' + }, + { + component: ComboSearchInputComponent, + data: listsRoutesData['combo-search-input'], + path: 'combo-search-input' + }, + { + component: SimpleComboOverviewComponent, + data: listsRoutesData['simple-combo-overview'], + path: 'simple-combo-overview' + }, + { + component: SimpleComboTypeComponent, + data: listsRoutesData['simple-combo-type'], + path: 'simple-combo-type' + }, + { + component: SimpleComboDisabledComponent, + data: listsRoutesData['simple-combo-disabled'], + path: 'simple-combo-disabled' + }, + { + component: SimpleComboSizeComponent, + data: listsRoutesData['simple-combo-size'], + path: 'simple-combo-size' + }, + { + component: SimpleComboStatesComponent, + data: listsRoutesData['simple-combo-states'], + path: 'simple-combo-states' + }, + { + component: SimpleComboValidationComponent, + data: listsRoutesData['simple-combo-validation'], + path: 'simple-combo-validation' + }, + { + component: SimpleComboDropdownSizeComponent, + data: listsRoutesData['simple-combo-dropdown-size'], + path: 'simple-combo-dropdown-size' + }, + { + component: SimpleComboDropdownItemsComponent, + data: listsRoutesData['simple-combo-dropdown-items'], + path: 'simple-combo-dropdown-items' + }, + { + component: SimpleComboDropdownItemsStatesComponent, + data: listsRoutesData['simple-combo-dropdown-items-states'], + path: 'simple-combo-dropdown-items-states' + }, + { + component: SimpleComboTailwindStylingComponent, + data: listsRoutesData['simple-combo-tailwind-styling'], + path: 'simple-combo-tailwind-styling' }, { component: SimpleComboCascadingComponent,