From afe02b7c67e21cdc1e4f3a4d6f43417deb8f5dd2 Mon Sep 17 00:00:00 2001 From: maksim-karatkevich Date: Thu, 17 Sep 2020 12:54:03 +0300 Subject: [PATCH] feat(badge): dot mode (#2479) --- src/app/playground-components.ts | 12 +++++ .../actions/_actions.component.theme.scss | 5 +- .../components/actions/action.component.scss | 2 + .../components/actions/actions.component.ts | 35 ++++++++++--- .../badge/_badge.component.theme.scss | 5 ++ .../components/badge/badge.component.scss | 5 ++ .../theme/components/badge/badge.component.ts | 35 +++++++++++-- .../components/menu/menu-item.component.html | 10 ++++ .../theme/components/menu/menu.component.scss | 4 ++ .../theme/components/menu/menu.component.ts | 6 ++- .../theme/components/menu/menu.module.ts | 7 ++- .../theme/components/menu/menu.service.ts | 8 +++ .../sidebar/_sidebar.component.theme.scss | 2 +- .../tabset/_tabset.component.theme.scss | 18 +++++++ .../components/tabset/tabset.component.ts | 17 ++++++- .../theme/styles/themes/_mapping.scss | 5 ++ .../action/action-dot-mode.component.html | 26 ++++++++++ .../action/action-dot-mode.component.ts | 15 ++++++ .../action/action-routing.module.ts | 5 ++ .../with-layout/action/action.module.ts | 2 + .../menu/menu-badge.component.html | 4 ++ .../menu/menu-badge.component.scss | 9 ++++ .../with-layout/menu/menu-badge.component.ts | 51 +++++++++++++++++++ .../with-layout/menu/menu-routing.module.ts | 5 ++ .../with-layout/menu/menu.module.ts | 2 + .../tabset/tabset-badge.component.html | 6 +++ 26 files changed, 284 insertions(+), 17 deletions(-) create mode 100644 src/playground/with-layout/action/action-dot-mode.component.html create mode 100644 src/playground/with-layout/action/action-dot-mode.component.ts create mode 100644 src/playground/with-layout/menu/menu-badge.component.html create mode 100644 src/playground/with-layout/menu/menu-badge.component.scss create mode 100644 src/playground/with-layout/menu/menu-badge.component.ts diff --git a/src/app/playground-components.ts b/src/app/playground-components.ts index 84d5261097..a97497f4d2 100644 --- a/src/app/playground-components.ts +++ b/src/app/playground-components.ts @@ -69,6 +69,12 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [ component: 'ActionWidthComponent', name: 'Action Width', }, + { + path: 'action-dot-mode.component', + link: '/action/action-dot-mode.component', + component: 'ActionDotModeComponent', + name: 'Action Dot Mode', + }, ], }, { @@ -724,6 +730,12 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [ component: 'MenuLinkParamsComponent', name: 'Menu Link Params', }, + { + path: 'menu-badge.component', + link: '/menu/menu-badge.component', + component: 'MenuBadgeComponent', + name: 'Menu Badge', + }, { path: 'menu-service.component', link: '/menu/menu-service.component', diff --git a/src/framework/theme/components/actions/_actions.component.theme.scss b/src/framework/theme/components/actions/_actions.component.theme.scss index 6ab78171c1..c18cb204c5 100644 --- a/src/framework/theme/components/actions/_actions.component.theme.scss +++ b/src/framework/theme/components/actions/_actions.component.theme.scss @@ -25,14 +25,15 @@ } } + nb-action { $divider: nb-theme(actions-divider-width) nb-theme(actions-divider-style) nb-theme(actions-divider-color); @include nb-ltr(border-left, $divider); @include nb-rtl(border-right, $divider); &:first-child { - @include nb-ltr(border-left, none!important); - @include nb-rtl(border-right, none!important); + @include nb-ltr(border-left, none !important); + @include nb-rtl(border-right, none !important); } nb-icon { diff --git a/src/framework/theme/components/actions/action.component.scss b/src/framework/theme/components/actions/action.component.scss index e41747df64..3e12cccefb 100644 --- a/src/framework/theme/components/actions/action.component.scss +++ b/src/framework/theme/components/actions/action.component.scss @@ -25,6 +25,8 @@ } a.icon-container { + position: relative; + &:hover, &:focus { text-decoration: none; } diff --git a/src/framework/theme/components/actions/actions.component.ts b/src/framework/theme/components/actions/actions.component.ts index d4580bb667..0cb9ea9cfd 100644 --- a/src/framework/theme/components/actions/actions.component.ts +++ b/src/framework/theme/components/actions/actions.component.ts @@ -25,12 +25,14 @@ import { NbIconConfig } from '../icon/icon.component'; [title]="title" *ngIf="link"> + + + + + + + + - - - `, }) export class NbActionComponent { @@ -93,6 +99,20 @@ export class NbActionComponent { protected _disabled: boolean = false; static ngAcceptInputType_disabled: NbBooleanInput; + /** + * Use badge dot mode + * @type boolean + */ + @Input() + get badgeDot(): boolean { + return this._badgeDot; + } + set badgeDot(value: boolean) { + this._badgeDot = convertToBoolProperty(value); + } + protected _badgeDot: boolean; + static ngAcceptInputType_badgeDot: NbBooleanInput; + /** * Badge text to display * @type string @@ -154,6 +174,9 @@ export class NbActionComponent { * and we can set it to full a width of a parent component * @stacked-example(Full Width, action/action-width.component) * + * Action dot mode + * @stacked-example(Action badge in dot mode, action/action-dot-mode.component) + * * @styles * * actions-background-color: diff --git a/src/framework/theme/components/badge/_badge.component.theme.scss b/src/framework/theme/components/badge/_badge.component.theme.scss index 390ff7af3a..1ca87df9cb 100644 --- a/src/framework/theme/components/badge/_badge.component.theme.scss +++ b/src/framework/theme/components/badge/_badge.component.theme.scss @@ -12,6 +12,11 @@ font-weight: nb-theme(badge-text-font-weight); line-height: nb-theme(badge-text-line-height); padding: nb-theme(badge-padding); + + &.dot-mode { + padding: nb-theme(badge-dot-mode-padding); + border-radius: nb-theme(badge-dot-mode-border-radius); + } } @each $status in nb-get-statuses() { diff --git a/src/framework/theme/components/badge/badge.component.scss b/src/framework/theme/components/badge/badge.component.scss index eb46dc3910..c079c09a0a 100644 --- a/src/framework/theme/components/badge/badge.component.scss +++ b/src/framework/theme/components/badge/badge.component.scss @@ -29,6 +29,11 @@ left: 0; } +:host(.position-center) { + top: 50%; + transform: translateY(-50%); +} + :host(.position-start) { @include nb-ltr(left, 0); @include nb-rtl(right, 0); diff --git a/src/framework/theme/components/badge/badge.component.ts b/src/framework/theme/components/badge/badge.component.ts index ce96210b83..ffdc3a29be 100644 --- a/src/framework/theme/components/badge/badge.component.ts +++ b/src/framework/theme/components/badge/badge.component.ts @@ -7,12 +7,18 @@ import { Component, HostBinding, Input } from '@angular/core'; import { NbComponentStatus } from '../component-status'; -import { emptyStatusWarning } from '../helpers'; +import { convertToBoolProperty, emptyStatusWarning } from '../helpers'; -export type NbBadgePhysicalPosition = 'top left' | 'top right' | 'bottom left' | 'bottom right'; -export type NbBadgeLogicalPosition = 'top start' | 'top end' | 'bottom start' | 'bottom end'; +export type NbBadgePhysicalPosition = 'top left' | 'top right' | 'bottom left' | 'bottom right' | 'center right' | 'center left'; +export type NbBadgeLogicalPosition = 'top start' | 'top end' | 'bottom start' | 'bottom end' | 'center start'| 'center end'; export type NbBadgePosition = NbBadgePhysicalPosition | NbBadgeLogicalPosition; +export interface NbBadge { + text?: string; + position?: NbBadgePosition; + status?: NbComponentStatus; + dotMode?: boolean; +} /** * Badge is a simple labeling component. @@ -79,9 +85,9 @@ export type NbBadgePosition = NbBadgePhysicalPosition | NbBadgeLogicalPosition; @Component({ selector: 'nb-badge', styleUrls: ['./badge.component.scss'], - template: `{{text}}`, + template: `{{dotMode ? '' : text}}`, }) -export class NbBadgeComponent { +export class NbBadgeComponent implements NbBadge { /** * Text to display @@ -107,6 +113,20 @@ export class NbBadgeComponent { protected _defaultPosition: NbBadgePosition = 'top right'; protected _position: NbBadgePosition = this._defaultPosition; + /** + * Shows badge as a dot. No text is shown. + * @type boolean + */ + @Input() + @HostBinding('class.dot-mode') + get dotMode(): boolean { + return this._dotMode; + } + set dotMode(value: boolean) { + this._dotMode = convertToBoolProperty(value); + } + protected _dotMode: boolean; + /** * Badge status (adds specific styles): * 'basic', 'primary', 'info', 'success', 'warning', 'danger', 'control' @@ -188,4 +208,9 @@ export class NbBadgeComponent { get end(): boolean { return this.position.includes('end'); } + + @HostBinding('class.position-center') + get center(): boolean { + return this.position.includes('center'); + } } diff --git a/src/framework/theme/components/menu/menu-item.component.html b/src/framework/theme/components/menu/menu-item.component.html index a828f9bc6e..58f32582f1 100644 --- a/src/framework/theme/components/menu/menu-item.component.html +++ b/src/framework/theme/components/menu/menu-item.component.html @@ -14,6 +14,7 @@ (click)="onItemClick(menuItem);"> {{ menuItem.title }} + {{ menuItem.title }} + {{ menuItem.title }} + {{ menuItem.title }} + + + + + + diff --git a/src/framework/theme/components/menu/menu.component.scss b/src/framework/theme/components/menu/menu.component.scss index 174a3e8f4e..e35c30e3e4 100644 --- a/src/framework/theme/components/menu/menu.component.scss +++ b/src/framework/theme/components/menu/menu.component.scss @@ -26,6 +26,10 @@ } } + .menu-item nb-badge { + position: static; + } + .menu-group span { display: flex; } diff --git a/src/framework/theme/components/menu/menu.component.ts b/src/framework/theme/components/menu/menu.component.ts index 246eccf021..2e5ac73832 100644 --- a/src/framework/theme/components/menu/menu.component.ts +++ b/src/framework/theme/components/menu/menu.component.ts @@ -20,7 +20,7 @@ import { isPlatformBrowser } from '@angular/common'; import { Router, NavigationEnd } from '@angular/router'; import { BehaviorSubject, Subject } from 'rxjs'; import { takeUntil, filter, map } from 'rxjs/operators'; -import { NbMenuInternalService, NbMenuItem, NbMenuBag, NbMenuService } from './menu.service'; +import { NbMenuInternalService, NbMenuItem, NbMenuBag, NbMenuService, NbMenuBadgeConfig } from './menu.service'; import { convertToBoolProperty, NbBooleanInput } from '../helpers'; import { NB_WINDOW } from '../../theme.options'; import { animate, state, style, transition, trigger } from '@angular/animations'; @@ -44,6 +44,7 @@ export enum NbToggleStates { }) export class NbMenuItemComponent implements DoCheck, AfterViewInit, OnDestroy { @Input() menuItem = null; + @Input() badge: NbMenuBadgeConfig; @Output() hoverItem = new EventEmitter(); @Output() toggleSubMenu = new EventEmitter(); @@ -156,6 +157,8 @@ export class NbMenuItemComponent implements DoCheck, AfterViewInit, OnDestroy { * Autocollapse menu example * @stacked-example(Autocollapse Menu, menu/menu-autocollapse.component) * + * Menu badge + * @stacked-example(Menu item badge, menu/menu-badge.component) * * @styles * @@ -211,6 +214,7 @@ export class NbMenuItemComponent implements DoCheck, AfterViewInit, OnDestroy {
  • (1); const submenuToggle$ = new ReplaySubject(1); const collapseAll$ = new ReplaySubject<{ tag: string }>(1); +export type NbMenuBadgeConfig = Omit; + // TODO: check if we need both URL and LINK /** * @@ -59,6 +62,11 @@ export class NbMenuItem { * @type {boolean} */ expanded?: boolean; + /** + * Badge component + * @type {boolean} + */ + badge?: NbMenuBadgeConfig; /** * Children items * @type {List} diff --git a/src/framework/theme/components/sidebar/_sidebar.component.theme.scss b/src/framework/theme/components/sidebar/_sidebar.component.theme.scss index 7774a817f1..ac0d2053de 100644 --- a/src/framework/theme/components/sidebar/_sidebar.component.theme.scss +++ b/src/framework/theme/components/sidebar/_sidebar.component.theme.scss @@ -84,7 +84,7 @@ } > .menu-items > .menu-item > a { - span, .expand-state { + span, nb-badge, .expand-state { display: none; } } diff --git a/src/framework/theme/components/tabset/_tabset.component.theme.scss b/src/framework/theme/components/tabset/_tabset.component.theme.scss index 63976042e5..26ff0d4c67 100644 --- a/src/framework/theme/components/tabset/_tabset.component.theme.scss +++ b/src/framework/theme/components/tabset/_tabset.component.theme.scss @@ -32,6 +32,24 @@ } } + nb-badge.dot-mode.position-left { + left: nb-theme(tabset-tab–badge-dot-mode-horizontal-offset); + } + + nb-badge.dot-mode.position-right { + right: nb-theme(tabset-tab–badge-dot-mode-horizontal-offset); + } + + nb-badge.dot-mode.position-start { + @include nb-ltr(left, nb-theme(tabset-tab–badge-dot-mode-horizontal-offset)); + @include nb-rtl(right, nb-theme(tabset-tab–badge-dot-mode-horizontal-offset)); + } + + nb-badge.dot-mode.position-end { + @include nb-ltr(right, nb-theme(tabset-tab–badge-dot-mode-horizontal-offset)); + @include nb-rtl(left, nb-theme(tabset-tab–badge-dot-mode-horizontal-offset)); + } + .tab.active { .tab-link { background-color: nb-theme(tabset-tab-active-background-color); diff --git a/src/framework/theme/components/tabset/tabset.component.ts b/src/framework/theme/components/tabset/tabset.component.ts index be07007270..ec3bd7ed1e 100644 --- a/src/framework/theme/components/tabset/tabset.component.ts +++ b/src/framework/theme/components/tabset/tabset.component.ts @@ -56,6 +56,20 @@ export class NbTabComponent { */ @Input() tabId: string; + /** + * Use badge dot mode + * @type {boolean} + */ + @Input() + get badgeDot(): boolean { + return this._badgeDot; + } + set badgeDot(val: boolean) { + this._badgeDot = convertToBoolProperty(val); + } + protected _badgeDot: boolean; + static ngAcceptInputType_badgeDot: NbBooleanInput; + /** * Tab icon name or icon config object * @type {string | NbIconConfig} @@ -261,8 +275,9 @@ export class NbTabComponent { {{ tab.tabTitle }} - diff --git a/src/framework/theme/styles/themes/_mapping.scss b/src/framework/theme/styles/themes/_mapping.scss index 7c8343084f..15d9183d61 100644 --- a/src/framework/theme/styles/themes/_mapping.scss +++ b/src/framework/theme/styles/themes/_mapping.scss @@ -204,6 +204,8 @@ $eva-mapping: ( tabset-tab-disabled-background-color: transparent, tabset-tab-disabled-text-color: text-disabled-color, tabset-tab-disabled-underline-color: transparent, + tabset-tab–badge-dot-mode-horizontal-offset: 0.75rem, + tabset-tab-badge-dot-mode-padding: 0.25rem, tabset-divider-color: divider-color, tabset-divider-style: divider-style, @@ -1392,6 +1394,9 @@ $eva-mapping: ( checkbox-control-disabled-checked-background-color: color-basic-transparent-600, checkbox-control-disabled-checked-border-color: color-basic-transparent-600, + badge-dot-mode-border-radius: 0.5rem, + badge-dot-mode-padding: 0.3rem, + badge-border-radius: border-radius, badge-text-font-family: text-button-font-family, badge-text-font-size: text-button-tiny-font-size, diff --git a/src/playground/with-layout/action/action-dot-mode.component.html b/src/playground/with-layout/action/action-dot-mode.component.html new file mode 100644 index 0000000000..a0535dac30 --- /dev/null +++ b/src/playground/with-layout/action/action-dot-mode.component.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/src/playground/with-layout/action/action-dot-mode.component.ts b/src/playground/with-layout/action/action-dot-mode.component.ts new file mode 100644 index 0000000000..d58fb3e5df --- /dev/null +++ b/src/playground/with-layout/action/action-dot-mode.component.ts @@ -0,0 +1,15 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + selector: 'nb-action-dot-mode', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './action-dot-mode.component.html', +}) +export class ActionDotModeComponent { +} diff --git a/src/playground/with-layout/action/action-routing.module.ts b/src/playground/with-layout/action/action-routing.module.ts index eed2f5d22a..3b1d9bcde2 100644 --- a/src/playground/with-layout/action/action-routing.module.ts +++ b/src/playground/with-layout/action/action-routing.module.ts @@ -11,6 +11,7 @@ import { ActionShowcaseComponent } from './action-showcase.component'; import { ActionSizesComponent } from './action-sizes.component'; import { ActionTestComponent } from './action-test.component'; import { ActionWidthComponent } from './action-width.component'; +import { ActionDotModeComponent } from './action-dot-mode.component'; const routes: Route[] = [ { @@ -33,6 +34,10 @@ const routes: Route[] = [ path: 'action-width.component', component: ActionWidthComponent, }, + { + path: 'action-dot-mode.component', + component: ActionDotModeComponent, + }, ]; @NgModule({ diff --git a/src/playground/with-layout/action/action.module.ts b/src/playground/with-layout/action/action.module.ts index bfdf1ed9c9..62cdee3b53 100644 --- a/src/playground/with-layout/action/action.module.ts +++ b/src/playground/with-layout/action/action.module.ts @@ -13,6 +13,7 @@ import { ActionShowcaseComponent } from './action-showcase.component'; import { ActionSizesComponent } from './action-sizes.component'; import { ActionTestComponent } from './action-test.component'; import { ActionWidthComponent } from './action-width.component'; +import { ActionDotModeComponent } from './action-dot-mode.component'; @NgModule({ declarations: [ @@ -21,6 +22,7 @@ import { ActionWidthComponent } from './action-width.component'; ActionSizesComponent, ActionTestComponent, ActionWidthComponent, + ActionDotModeComponent, ], imports: [ NbActionsModule, diff --git a/src/playground/with-layout/menu/menu-badge.component.html b/src/playground/with-layout/menu/menu-badge.component.html new file mode 100644 index 0000000000..eec714cb88 --- /dev/null +++ b/src/playground/with-layout/menu/menu-badge.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/src/playground/with-layout/menu/menu-badge.component.scss b/src/playground/with-layout/menu/menu-badge.component.scss new file mode 100644 index 0000000000..df57c265c8 --- /dev/null +++ b/src/playground/with-layout/menu/menu-badge.component.scss @@ -0,0 +1,9 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +nb-card { + width: 20rem; +} diff --git a/src/playground/with-layout/menu/menu-badge.component.ts b/src/playground/with-layout/menu/menu-badge.component.ts new file mode 100644 index 0000000000..cd9ac737b6 --- /dev/null +++ b/src/playground/with-layout/menu/menu-badge.component.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { NbMenuItem } from '@nebular/theme'; + +@Component({ + selector: 'nb-menu-badge', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './menu-badge.component.html', + styleUrls: ['./menu-badge.component.scss'], +}) +export class MenuBadgeComponent { + + items: NbMenuItem[] = [ + { + title: 'Profile', + expanded: true, + badge: { + text: '30', + status: 'primary', + }, + children: [ + { + title: 'Messages', + badge: { + text: '99+', + status: 'danger', + }, + }, + { + title: 'Notifications', + badge: { + dotMode: true, + status: 'warning', + }, + }, + { + title: 'Emails', + badge: { + text: 'new', + status: 'success', + }, + }, + ], + }, + ]; +} diff --git a/src/playground/with-layout/menu/menu-routing.module.ts b/src/playground/with-layout/menu/menu-routing.module.ts index 861d8236ea..9a1d61bd9f 100644 --- a/src/playground/with-layout/menu/menu-routing.module.ts +++ b/src/playground/with-layout/menu/menu-routing.module.ts @@ -21,6 +21,7 @@ import { MenuServiceItem3Component, } from './menu-service-children'; import { MenuServiceComponent } from './menu-service.component'; +import { MenuBadgeComponent } from './menu-badge.component'; const routes: Route[] = [ { @@ -39,6 +40,10 @@ const routes: Route[] = [ path: 'menu-link-params.component', component: MenuLinkParamsComponent, }, + { + path: 'menu-badge.component', + component: MenuBadgeComponent, + }, { path: 'menu-service.component', component: MenuServiceComponent, diff --git a/src/playground/with-layout/menu/menu.module.ts b/src/playground/with-layout/menu/menu.module.ts index a5fdcf96fe..0572034148 100644 --- a/src/playground/with-layout/menu/menu.module.ts +++ b/src/playground/with-layout/menu/menu.module.ts @@ -22,6 +22,7 @@ import { MenuServiceItem332Component, } from './menu-service-children'; import { MenuServiceComponent } from './menu-service.component'; +import { MenuBadgeComponent } from './menu-badge.component'; @NgModule({ declarations: [ @@ -38,6 +39,7 @@ import { MenuServiceComponent } from './menu-service.component'; MenuServiceItem331Component, MenuServiceItem332Component, MenuServiceComponent, + MenuBadgeComponent, ], imports: [ NbMenuModule.forRoot(), diff --git a/src/playground/with-layout/tabset/tabset-badge.component.html b/src/playground/with-layout/tabset/tabset-badge.component.html index 540d6c611e..f3f428f977 100644 --- a/src/playground/with-layout/tabset/tabset-badge.component.html +++ b/src/playground/with-layout/tabset/tabset-badge.component.html @@ -19,6 +19,12 @@ badgeStatus="success">

    List of transactions.

    + +

    List of notifications.

    +