diff --git a/packages/components-dev/dropdown/module.ts b/packages/components-dev/dropdown/module.ts
index d937a2617..d14cd0595 100644
--- a/packages/components-dev/dropdown/module.ts
+++ b/packages/components-dev/dropdown/module.ts
@@ -17,6 +17,7 @@ import {
DropdownOverviewExample,
DropdownRecursiveTemplateExample,
DropdownWithFilterExample,
+ DropdownWithFooterExample,
DropdownXPositionExample
} from 'packages/docs-examples/components/dropdown';
import { DevThemeToggle } from '../theme-toggle';
@@ -33,9 +34,13 @@ import { DevThemeToggle } from '../theme-toggle';
DropdownOpenByArrowDownExample,
DropdownRecursiveTemplateExample,
DropdownWithFilterExample,
+ DropdownWithFooterExample,
DropdownXPositionExample
],
template: `
+
+
+
diff --git a/packages/components/code-block/__screenshots__/01-light.png b/packages/components/code-block/__screenshots__/01-light.png
index f0f3a49cd..6a341eef0 100644
Binary files a/packages/components/code-block/__screenshots__/01-light.png and b/packages/components/code-block/__screenshots__/01-light.png differ
diff --git a/packages/components/dropdown/__screenshots__/01-dark.png b/packages/components/dropdown/__screenshots__/01-dark.png
index e117b9baf..6fc46c36b 100644
Binary files a/packages/components/dropdown/__screenshots__/01-dark.png and b/packages/components/dropdown/__screenshots__/01-dark.png differ
diff --git a/packages/components/dropdown/__screenshots__/01-light.png b/packages/components/dropdown/__screenshots__/01-light.png
index 522270eff..8448c8431 100644
Binary files a/packages/components/dropdown/__screenshots__/01-light.png and b/packages/components/dropdown/__screenshots__/01-light.png differ
diff --git a/packages/components/dropdown/dropdown.component.ts b/packages/components/dropdown/dropdown.component.ts
index bbd8f2612..322bc51ce 100644
--- a/packages/components/dropdown/dropdown.component.ts
+++ b/packages/components/dropdown/dropdown.component.ts
@@ -58,6 +58,13 @@ import {
})
export class KbqDropdownStaticContent {}
+/** Footer that is rendered below the dropdown options panel. */
+@Directive({
+ selector: '[kbqDropdownFooter], kbq-dropdown-footer',
+ host: { class: 'kbq-dropdown-footer' }
+})
+export class KbqDropdownFooter {}
+
@Component({
selector: 'kbq-dropdown',
imports: [],
diff --git a/packages/components/dropdown/dropdown.en.md b/packages/components/dropdown/dropdown.en.md
index b47eecf75..fadb4b77b 100644
--- a/packages/components/dropdown/dropdown.en.md
+++ b/packages/components/dropdown/dropdown.en.md
@@ -10,6 +10,12 @@ Add the `progress` attribute to `kbq-dropdown-item` to show a loading shimmer on
+### Footer
+
+You can place auxiliary elements in the footer: [buttons](en/components/button), [links](en/components/link), hints.
+
+
+
### Open by Arrow Down ↓
diff --git a/packages/components/dropdown/dropdown.html b/packages/components/dropdown/dropdown.html
index 7ed08d4f6..ac94e355c 100644
--- a/packages/components/dropdown/dropdown.html
+++ b/packages/components/dropdown/dropdown.html
@@ -15,6 +15,7 @@
+
diff --git a/packages/components/dropdown/dropdown.module.ts b/packages/components/dropdown/dropdown.module.ts
index ca5ea1978..485b824a5 100644
--- a/packages/components/dropdown/dropdown.module.ts
+++ b/packages/components/dropdown/dropdown.module.ts
@@ -4,7 +4,7 @@ import { KbqIconModule } from '@koobiq/components/icon';
import { KbqDropdownContent } from './dropdown-content.directive';
import { KbqDropdownItem } from './dropdown-item.component';
import { KBQ_DROPDOWN_SCROLL_STRATEGY_FACTORY_PROVIDER, KbqDropdownTrigger } from './dropdown-trigger.directive';
-import { KbqDropdown, KbqDropdownStaticContent } from './dropdown.component';
+import { KbqDropdown, KbqDropdownFooter, KbqDropdownStaticContent } from './dropdown.component';
@NgModule({
imports: [
@@ -14,7 +14,8 @@ import { KbqDropdown, KbqDropdownStaticContent } from './dropdown.component';
KbqDropdown,
KbqDropdownItem,
KbqDropdownTrigger,
- KbqDropdownContent
+ KbqDropdownContent,
+ KbqDropdownFooter
],
providers: [KBQ_DROPDOWN_SCROLL_STRATEGY_FACTORY_PROVIDER],
exports: [
@@ -22,7 +23,8 @@ import { KbqDropdown, KbqDropdownStaticContent } from './dropdown.component';
KbqDropdownItem,
KbqDropdownTrigger,
KbqDropdownContent,
- KbqDropdownStaticContent
+ KbqDropdownStaticContent,
+ KbqDropdownFooter
]
})
export class KbqDropdownModule {}
diff --git a/packages/components/dropdown/dropdown.ru.md b/packages/components/dropdown/dropdown.ru.md
index 0ba612860..6f7ed0a32 100644
--- a/packages/components/dropdown/dropdown.ru.md
+++ b/packages/components/dropdown/dropdown.ru.md
@@ -10,6 +10,12 @@
+### Футер
+
+В нижнем колонтитуле можно разместить вспомогательные элементы: [кнопки](ru/components/button), [ссылки](ru/components/link), подсказки.
+
+
+
### Открытие по стрелке вниз ↓
diff --git a/packages/components/dropdown/dropdown.scss b/packages/components/dropdown/dropdown.scss
index d959a7dfd..c1f29bb98 100644
--- a/packages/components/dropdown/dropdown.scss
+++ b/packages/components/dropdown/dropdown.scss
@@ -45,7 +45,6 @@
border-radius: var(--kbq-dropdown-size-container-border-radius);
box-sizing: border-box;
- padding: var(--kbq-size-xxs);
// Prevent users from interacting with the panel while it's animating. Note that
// people won't be able to click through it, because the overlay pane will catch the click.
@@ -86,6 +85,13 @@
.kbq-dropdown__content {
display: flex;
flex-direction: column;
+
+ padding: var(--kbq-size-xxs);
+}
+
+.kbq-dropdown-footer {
+ @extend %kbq-select-footer;
+ border-color: var(--kbq-line-contrast-less);
}
@include theme.kbq-dropdown-theme();
diff --git a/packages/components/dropdown/dropdown.spec.ts b/packages/components/dropdown/dropdown.spec.ts
index 3c8de9232..bec4586ea 100644
--- a/packages/components/dropdown/dropdown.spec.ts
+++ b/packages/components/dropdown/dropdown.spec.ts
@@ -1196,6 +1196,27 @@ describe('KbqDropdown', () => {
});
});
+ describe('footer', () => {
+ it('should project footer content into the panel, separate from the options content', () => {
+ const fixture = createComponent(FooterDropdown, [], []);
+
+ fixture.detectChanges();
+ fixture.componentInstance.trigger().open();
+ fixture.detectChanges();
+
+ const content = overlayContainerElement.querySelector('.kbq-dropdown__content') as HTMLElement;
+ const footer = overlayContainerElement.querySelector('.kbq-dropdown-footer') as HTMLElement;
+
+ expect(content.textContent).toContain('Item');
+ expect(footer).toBeTruthy();
+ expect(footer.textContent!.trim()).toBe('Footer content');
+
+ // The footer must render outside the scrollable options content, not inside it.
+ expect(content.contains(footer)).toBe(false);
+ expect(content.textContent).not.toContain('Footer content');
+ });
+ });
+
describe('nested dropdown', () => {
let fixture: ComponentFixture;
let instance: NestedDropdown;
@@ -2068,6 +2089,20 @@ class SimpleDropdown {
backdropClass: string;
}
+@Component({
+ imports: [KbqDropdownModule],
+ template: `
+
+
+
+ Footer content
+
+ `
+})
+class FooterDropdown {
+ readonly trigger = viewChild.required(KbqDropdownTrigger);
+}
+
@Component({
imports: [KbqDropdownModule],
template: `
diff --git a/packages/components/dropdown/e2e.ts b/packages/components/dropdown/e2e.ts
index 2a1604149..14f79b8c4 100644
--- a/packages/components/dropdown/e2e.ts
+++ b/packages/components/dropdown/e2e.ts
@@ -71,6 +71,8 @@ import { KbqDropdownModule } from './dropdown.module';
+
+ Dropdown footer
@@ -89,6 +91,7 @@ import { KbqDropdownModule } from './dropdown.module';
Disabled item
Caption
+ Dropdown footer
@@ -105,7 +108,7 @@ import { KbqDropdownModule } from './dropdown.module';
styles: `
:host {
display: flex;
- height: 500px;
+ height: 600px;
width: 1020px;
padding: var(--kbq-size-s);
}
diff --git a/packages/docs-examples/components/dropdown/dropdown-with-footer/dropdown-with-footer-example.ts b/packages/docs-examples/components/dropdown/dropdown-with-footer/dropdown-with-footer-example.ts
new file mode 100644
index 000000000..7f066adce
--- /dev/null
+++ b/packages/docs-examples/components/dropdown/dropdown-with-footer/dropdown-with-footer-example.ts
@@ -0,0 +1,65 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { KbqButtonModule } from '@koobiq/components/button';
+import { KbqDropdownModule } from '@koobiq/components/dropdown';
+import { KbqIconModule } from '@koobiq/components/icon';
+import { KbqLinkModule } from '@koobiq/components/link';
+
+/**
+ * @title Dropdown with footer
+ */
+@Component({
+ selector: 'dropdown-with-footer-example',
+ imports: [KbqDropdownModule, KbqButtonModule, KbqLinkModule, KbqIconModule],
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+ GitHub
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ styles: `
+ :host {
+ height: 200px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .example-submenu-footer {
+ font-size: var(--kbq-typography-text-compact-font-size);
+ color: var(--kbq-foreground-contrast-secondary);
+ }
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DropdownWithFooterExample {}
diff --git a/packages/docs-examples/components/dropdown/index.ts b/packages/docs-examples/components/dropdown/index.ts
index 092c6323d..14e80cab1 100644
--- a/packages/docs-examples/components/dropdown/index.ts
+++ b/packages/docs-examples/components/dropdown/index.ts
@@ -8,6 +8,7 @@ import { DropdownOpenByArrowDownExample } from './dropdown-open-by-arrow-down/dr
import { DropdownOverviewExample } from './dropdown-overview/dropdown-overview-example';
import { DropdownRecursiveTemplateExample } from './dropdown-recursive-template/dropdown-recursive-template-example';
import { DropdownWithFilterExample } from './dropdown-with-filter/dropdown-with-filter-example';
+import { DropdownWithFooterExample } from './dropdown-with-footer/dropdown-with-footer-example';
import { DropdownXPositionExample } from './dropdown-x-position/dropdown-x-position-example';
export {
@@ -20,12 +21,14 @@ export {
DropdownOverviewExample,
DropdownRecursiveTemplateExample,
DropdownWithFilterExample,
+ DropdownWithFooterExample,
DropdownXPositionExample
};
const EXAMPLES = [
DropdownNestedExample,
DropdownOverviewExample,
+ DropdownWithFooterExample,
DropdownNavigationWrapExample,
DropdownDisabledExample,
DropdownItemProgressExample,
diff --git a/tools/public_api_guard/components/dropdown.api.md b/tools/public_api_guard/components/dropdown.api.md
index 79676bca8..d676d25af 100644
--- a/tools/public_api_guard/components/dropdown.api.md
+++ b/tools/public_api_guard/components/dropdown.api.md
@@ -125,7 +125,7 @@ export class KbqDropdown implements AfterContentInit, KbqDropdownPanel, OnInit,
get yPosition(): KbqDropdownPositionY;
set yPosition(value: KbqDropdownPositionY);
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}
@@ -164,6 +164,14 @@ export interface KbqDropdownDefaultOptions {
yPosition: KbqDropdownPositionY;
}
+// @public
+export class KbqDropdownFooter {
+ // (undocumented)
+ static ɵdir: i0.ɵɵDirectiveDeclaration;
+ // (undocumented)
+ static ɵfac: i0.ɵɵFactoryDeclaration;
+}
+
// @public
export class KbqDropdownItem implements KbqTitleTextRef, IFocusableOption, AfterViewInit, OnDestroy {
checkDisabled(event: Event): void;
@@ -210,7 +218,7 @@ export class KbqDropdownModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration;
// (undocumented)
- static ɵmod: i0.ɵɵNgModuleDeclaration;
+ static ɵmod: i0.ɵɵNgModuleDeclaration;
}
// @public