diff --git a/projects/igniteui-angular/src/lib/badge/README.md b/projects/igniteui-angular/src/lib/badge/README.md index cd6e11958ac..aefde3b2065 100644 --- a/projects/igniteui-angular/src/lib/badge/README.md +++ b/projects/igniteui-angular/src/lib/badge/README.md @@ -1,7 +1,7 @@ # igx-badge The **igx-badge** component is an absolutely positioned element that can be used in tandem with other components such as avatars, navigation menus, or anywhere else in an app where some active indication is required. -With the igx-badge you can display active count or an icon in several different predefined styles. +With the igx-badge you can display active count or an icon in several different predefined styles and sizes. A walkthrough of how to get started can be found [here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/badge.html) # Usage @@ -14,9 +14,12 @@ A walkthrough of how to get started can be found [here](https://www.infragistics |:----------|:-------------:|:------| | `id` | string | Unique identifier of the component. If not provided it will be automatically generated.| | `type` | string | Set the type of the badge to either `primary`, `info`, `success`, `warning`, or `error`. This will change the background color of the badge according to the values set in the default theme. | +| `dot` | boolean | Set whether the badge is displayed as a minimal dot indicator without any content. Default is `false`. | | `position` | string | Set the position of the badge relative to its parent container to either `top-right`, `top-left`, `bottom-right`, or `bottom-left`. | | `value` | string | Set the value to be displayed inside the badge. | | `icon` | string | Set an icon for the badge from the material icons set. Will not be displayed if `value` for the badge is already set. | +| `outlined` | boolean | Set whether the badge should have an outline. Default is `false`. | +| `shape` | string | Set the shape of the badge to either `rounded` or `square`. Default is `rounded`. | # Examples @@ -26,3 +29,18 @@ Using `igx-badge` with the `igx-avatar` component to show active status. ``` + +Using `igx-badge` as a dot indicator for notifications. +```html + + +``` + +Using different badge types. +```html + + + + + +``` \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts b/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts index d6654f0974e..541c38a0cbd 100644 --- a/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts +++ b/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts @@ -11,7 +11,8 @@ describe('Badge', () => { InitBadgeWithDefaultsComponent, InitBadgeWithIconComponent, IgxBadgeComponent, - InitBadgeWithIconARIAComponent + InitBadgeWithIconARIAComponent, + InitBadgeWithDotComponent ] }).compileComponents(); })); @@ -87,6 +88,26 @@ describe('Badge', () => { const container = fixture.nativeElement.querySelectorAll('.igx-badge')[0]; expect(container.getAttribute('aria-roledescription')).toMatch(expectedDescription); }); + + it('Initializes badge with dot property', () => { + const fixture = TestBed.createComponent(InitBadgeWithDotComponent); + fixture.detectChanges(); + const badge = fixture.componentInstance.badge; + + expect(badge.dot).toBeTruthy(); + expect(fixture.debugElement.query(By.css('.igx-badge--dot'))).toBeTruthy(); + }); + + it('Initializes success badge as dot', () => { + const fixture = TestBed.createComponent(InitBadgeWithDotComponent); + fixture.detectChanges(); + const badge = fixture.componentInstance.badge; + + expect(badge.type).toBe(IgxBadgeType.SUCCESS); + expect(badge.dot).toBeTruthy(); + expect(fixture.debugElement.query(By.css('.igx-badge--dot'))).toBeTruthy(); + expect(fixture.debugElement.query(By.css('.igx-badge--success'))).toBeTruthy(); + }); }); @Component({ @@ -120,3 +141,11 @@ class InitBadgeWithIconComponent { class InitBadgeWithIconARIAComponent { @ViewChild(IgxBadgeComponent, { static: true }) public badge: IgxBadgeComponent; } + +@Component({ + template: ``, + imports: [IgxBadgeComponent] +}) +class InitBadgeWithDotComponent { + @ViewChild(IgxBadgeComponent, { static: true }) public badge: IgxBadgeComponent; +} \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/badge/badge.component.ts b/projects/igniteui-angular/src/lib/badge/badge.component.ts index fe3990d8b12..c0cfc3a43ad 100644 --- a/projects/igniteui-angular/src/lib/badge/badge.component.ts +++ b/projects/igniteui-angular/src/lib/badge/badge.component.ts @@ -153,7 +153,9 @@ export class IgxBadgeComponent { /** @hidden @internal */ @HostBinding('class.igx-badge--square') public get _squareShape(): boolean { - return this.shape === 'square'; + if (!this.dot) { + return this.shape === 'square'; + } } /** @@ -183,6 +185,20 @@ export class IgxBadgeComponent { @HostBinding('class.igx-badge--outlined') public outlined = false; + /** + * Sets/gets whether the badge is displayed as a dot. + * When true, the badge will be rendered as a minimal 8px indicator without any content. + * Default value is `false`. + * + * @example + * ```html + * + * ``` + */ + @Input({transform: booleanAttribute}) + @HostBinding('class.igx-badge--dot') + public dot = false; + /** * Defines a human-readable, accessor, author-localized description for * the `type` and the `icon` or `value` of the element. diff --git a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss index 909374bd288..a630908f0f7 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss @@ -35,6 +35,10 @@ @extend %igx-badge--error !optional; } + @include m(dot) { + @extend %igx-badge--dot !optional; + } + @include m(outlined) { @extend %igx-badge--outlined !optional; } diff --git a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss index 2e82deb4545..82402c29970 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss @@ -6,12 +6,15 @@ /// @param {Map} $theme - The theme used to style the component. @mixin badge($theme) { @include css-vars($theme); - + $theme-variant: map.get($theme, '_meta', 'variant'); $variant: map.get($theme, '_meta', 'theme'); %igx-badge-display { - --size: #{rem(22px)}; - --_badge-size: var(--size); + @include sizable(); + + --component-size: var(--ig-size, #{var-get($theme, 'default-size')}); + --badge-size: var(--component-size); + --_badge-size: #{var-get($theme, 'size')}; display: inline-flex; justify-content: center; @@ -25,18 +28,18 @@ overflow: hidden; igx-icon { - --size: var(--igx-icon-size, calc(var(--_badge-size) / 2)); + --size: var(--igx-icon-size, #{sizable(rem(12px), rem(14px), rem(16px))}); + --component-size: var(--badge-size); display: inline-flex; justify-content: center; align-items: center; - font-weight: 400; color: var-get($theme, 'icon-color'); } @if $variant == 'indigo' { igx-icon { - $icon-size: rem(12px); + $icon-size: sizable(rem(8px), rem(10px), rem(12px)); --ig-icon-size: #{$icon-size}; --igx-icon-size: #{$icon-size}; @@ -45,7 +48,7 @@ } %igx-badge--outlined { - box-shadow: inset 0 0 0 rem(if($variant != 'bootstrap', 2px, 1px)) var-get($theme, 'border-color'); + box-shadow: 0 0 0 rem(2px) var-get($theme, 'border-color'); } %igx-badge--square { @@ -54,23 +57,57 @@ %igx-badge-value { white-space: nowrap; - padding-inline: pad-inline(rem(4px)); + padding-inline: pad-inline(rem(4px), rem(6px), if($variant == 'indigo', rem(6px), rem(8px))); } %igx-badge--success { - background: color($color: 'success'); + background: color($color: 'success', $variant: if($variant != 'material', if($variant == 'indigo', 700, 500), 900)); } %igx-badge--info { - background: color($color: 'info'); + background: color($color: 'info', $variant: if($variant != 'material', if($variant == 'fluent', 700, 500), 800)); } %igx-badge--warn { background: color($color: 'warn'); + + @if $variant == 'indigo' and $theme-variant == 'light' { + color: color($color: 'gray', $variant: 900); + + igx-icon { + color: color($color: 'gray', $variant: 900); + } + } @else if $variant == 'indigo' and $theme-variant == 'dark' { + color: color($color: 'gray', $variant: 50); + + igx-icon { + color: color($color: 'gray', $variant: 50); + } + } @else { + color: contrast-color($color: 'warn', $variant: 500); + + igx-icon { + color: contrast-color($color: 'warn', $variant: 500); + } + } } %igx-badge--error { - background: color($color: 'error'); + background: color($color: 'error', $variant: if($variant == 'material', 700, 500)); + color: contrast-color($color: 'error', $variant: if($variant == 'bootstrap', 100, 900)); + } + + %igx-badge--dot { + --_dot-size: #{var-get($theme, 'dot-size')}; + + min-width: var(--_dot-size); + min-height: var(--_dot-size); + padding: 0; + + igx-icon, + > * { + display: none; + } } %igx-badge--hidden { @@ -79,15 +116,28 @@ } /// Adds typography styles for the igx-badge component. -/// Uses the 'caption' category from the typographic scale. +/// Uses 'caption' and 'body-2' categories from the typographic scale. /// @group typography /// @param {Map} $categories [(text: 'caption')] - The categories from the typographic scale used for type styles. -@mixin badge-typography($categories: (text: 'caption')) { +@mixin badge-typography($categories: (text: null), $theme: null) { $text: map.get($categories, 'text'); %igx-badge-display { - @include type-style($text) { - margin: 0; + @if $text { + @include type-style($text); + } @else { + @if $theme == 'indigo' { + @include type-style('button', false) { + font-size: sizable(rem(9px), rem(10px), var(--ig-button-font-size)); + line-height: sizable(rem(12px), rem(14px), var(--ig-button-line-height)); + } + } @else { + font-size: sizable(var(--ig-caption-font-size), var(--ig-body-2-font-size), var(--ig-body-2-font-size)); + font-weight: sizable(var(--ig-caption-font-weight), var(--ig-body-2-font-weight), var(--ig-body-2-font-weight)); + line-height: sizable(var(--ig-caption-line-height), var(--ig-body-2-line-height), var(--ig-body-2-line-height)); + letter-spacing: sizable(var(--ig-caption-letter-spacing), var(--ig-body-2-letter-spacing), var(--ig-body-2-letter-spacing)); + text-transform: sizable(var(--ig-caption-text-transform), var(--ig-body-2-text-transform), var(--ig-body-2-text-transform)); + } } } } diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss index 92f3c71458d..fe8225fdea5 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss @@ -36,7 +36,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography($type-scale) { - @include badge-typography(); + @include badge-typography($theme: 'bootstrap'); @include banner-typography(); @include bottom-nav-typography(); @include button-typography(); diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss index 2b8a6323070..3f9ab803925 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss @@ -35,7 +35,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography() { - @include badge-typography(); + @include badge-typography($theme: 'fluent'); @include banner-typography($categories: ( message: 'caption' )); diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss index 737346995c4..5f1228dfb6e 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss @@ -35,9 +35,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography($type-scale) { - @include badge-typography($categories: ( - text: 'button', - )); + @include badge-typography($theme: 'indigo'); @include banner-typography(); @include bottom-nav-typography(); @include button-typography(); diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss index bc15191255c..8a6dd101db6 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss @@ -35,7 +35,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography() { - @include badge-typography(); + @include badge-typography($theme: 'material'); @include banner-typography(); @include bottom-nav-typography(); @include button-typography(); diff --git a/src/app/badge/badge.sample.html b/src/app/badge/badge.sample.html index 47e674f1fb3..5240855c490 100644 --- a/src/app/badge/badge.sample.html +++ b/src/app/badge/badge.sample.html @@ -1,21 +1,90 @@

Angular Badge

- -
- - - bluetooth - +
+
+ + With Value +
+
+ + bluetooth + + With Icon +
+
+ + + bluetooth + + On Avatar +
+
+ +

Dot Type Badges

+
+
+ + Primary Dot +
+
+ + Info Dot +
+
+ + Success Dot +
+
+ + Warning Dot +
+
+ + Error Dot +
+
+ + + Dot on Avatar +
+
+ +

Type Variants

+
+
+ + Primary +
+
+ + Info +
+
+ + Success +
+
+ + Warning +
+
+ + Error +
diff --git a/src/app/badge/badge.sample.scss b/src/app/badge/badge.sample.scss index 85f3261b048..126f27254d0 100644 --- a/src/app/badge/badge.sample.scss +++ b/src/app/badge/badge.sample.scss @@ -1,16 +1,54 @@ +@use '../../../projects/igniteui-angular/src/lib/core/styles/themes/utilities' as *; + .wrapper { display: grid; - grid-template-columns: repeat(2, 1fr); + gap: rem(48px); + padding: rem(24px); } .badges { - place-items: center; - display: grid; - grid-template-columns: subgrid; + place-items: start; + display: flex; + flex-direction: column; gap: 24px; } +.badge-examples { + display: flex; + flex-wrap: wrap; + gap: rem(32px); + align-items: center; + padding: rem(16px); + background: color($color: 'gray', $variant: 100); + border-radius: rem(8px); +} + +.badge-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + position: relative; + + span { + font-size: 14px; + } +} + .avatar-sample { display: flex; align-items: center; + position: relative; + + igx-avatar { + anchor-name: --avatar; + } + + igx-badge { + position: absolute; + position-anchor: --avatar; + bottom: anchor(--avatar top); + left: anchor(right); + transform: translate(-75%, 75%); + } } diff --git a/src/app/badge/badge.sample.ts b/src/app/badge/badge.sample.ts index f8bcb45c90a..0e784d41fea 100644 --- a/src/app/badge/badge.sample.ts +++ b/src/app/badge/badge.sample.ts @@ -3,6 +3,7 @@ import { IgxBadgeComponent, IgxAvatarComponent, IgxIconComponent, + IgSizeDirective } from 'igniteui-angular'; import { defineComponents, @@ -32,11 +33,18 @@ registerIconFromText('bluetooth', bluetooth); styleUrls: ['badge.sample.scss'], templateUrl: 'badge.sample.html', schemas: [CUSTOM_ELEMENTS_SCHEMA], - imports: [IgxBadgeComponent, IgxAvatarComponent, IgxIconComponent] + imports: [IgxBadgeComponent, IgxAvatarComponent, IgxIconComponent, IgSizeDirective] }) export class BadgeSampleComponent { public panelConfig: PropertyPanelConfig = { + size: { + control: { + type: 'button-group', + options: ['small', 'medium', 'large'], + defaultValue: 'medium' + } + }, shape: { control: { type: 'button-group', diff --git a/src/styles/_app-layout.scss b/src/styles/_app-layout.scss index b8f1cb11ada..4626008c618 100644 --- a/src/styles/_app-layout.scss +++ b/src/styles/_app-layout.scss @@ -81,7 +81,6 @@ &-title { @include type-style('h6'); - margin-bottom: 24px; .light { font-weight: 400;