diff --git a/CHANGELOG.md b/CHANGELOG.md index 834140fb71f..a4ee7998ff3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,21 @@ All notable changes for each version of this project will be documented in this ### New Features +- `igxSplitter` component added. + - Allows rendering a vertical or horizontal splitter with multiple splitter panes with templatable content. + Panes can be resized or collapsed/expanded via the UI. Splitter orientation is defined via the `type` input. + + ```html + + + ... + + + ... + + + ``` + - `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid` - Added ability to pin rows to top or bottom depending on the new `pinning` input. And new API methods `pinRow` and `unpinRow`. diff --git a/projects/igniteui-angular/src/lib/core/i18n/paginator-resources.ts b/projects/igniteui-angular/src/lib/core/i18n/paginator-resources.ts index 6a421c5bc73..9319a08b377 100644 --- a/projects/igniteui-angular/src/lib/core/i18n/paginator-resources.ts +++ b/projects/igniteui-angular/src/lib/core/i18n/paginator-resources.ts @@ -1,7 +1,9 @@ export interface IPaginatorResourceStrings { igx_paginator_label?: string; + igx_paginator_pager_text?: string; } export const PaginatorResourceStringsEN: IPaginatorResourceStrings = { - igx_paginator_label: 'Items per page' + igx_paginator_label: 'Items per page', + igx_paginator_pager_text: 'of' }; diff --git a/projects/igniteui-angular/src/lib/core/styles/components/splitter/_splitter-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/splitter/_splitter-component.scss new file mode 100644 index 00000000000..75da58e80a8 --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/components/splitter/_splitter-component.scss @@ -0,0 +1,52 @@ +/// @group components +/// @author Simeon Simeonoff +/// @author Maya Kirova +/// @requires {mixin} bem-block +/// @requires {mixin} bem-elem +/// @requires {mixin} bem-mod +//// + +@include b(igx-splitter) { + // Register the component in the component registry + $this: str-slice(bem--selector-to-string(&), 2, -1); + @include register-component($this); + + @include b(#{$this}-bar) { + @extend %igx-splitter-bar !optional; + + @include e(handle) { + @extend %igx-splitter-handle !optional; + @extend %igx-splitter-handle--horizontal !optional; + } + + @include e(expander, 'start') { + @extend %igx-splitter-expander !optional; + @extend %igx-splitter-expander--start !optional; + } + + @include e(expander, 'end') { + @extend %igx-splitter-expander !optional; + @extend %igx-splitter-expander--end !optional; + } + + @include m('vertical') { + @extend %igx-splitter-bar--vertical !optional; + + @include e(handle) { + @extend %igx-splitter-handle !optional; + @extend %igx-splitter-handle--vertical !optional; + } + + @include e(expander, 'start') { + @extend %igx-splitter-expander !optional; + @extend %igx-splitter-expander--start-vertical !optional; + } + + @include e(expander, 'end') { + @extend %igx-splitter-expander !optional; + @extend %igx-splitter-expander--end-vertical !optional; + } + } + } +} + diff --git a/projects/igniteui-angular/src/lib/core/styles/components/splitter/_splitter-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/splitter/_splitter-theme.scss new file mode 100644 index 00000000000..b0733eb6575 --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/components/splitter/_splitter-theme.scss @@ -0,0 +1,206 @@ +//// +/// @group themes +@function igx-splitter-theme( + $palette: $default-palette, + $schema: $light-schema, + $elevations: $elevations, + + $bar-color: null, + $handle-color: null, + $expander-color: null, + $border-radius: null, + $size: null +) { + $name: 'igx-splitter'; + $splitter-schema: (); + + @if map-has-key($schema, $name) { + $splitter-schema: map-get($schema, $name); + } @else { + $splitter-schema: $schema; + } + + $border-radius-handle: round-borders( + if($border-radius, $border-radius, map-get($splitter-schema, 'border-radius')), 0, 2px + ); + + $theme: apply-palette($splitter-schema, $palette); + + @if not($handle-color) and $bar-color { + $handle-color: text-contrast($bar-color); + } + + @if not($expander-color) and $bar-color { + $expander-color: text-contrast($bar-color); + } + + @return extend($theme, ( + name: $name, + palette: $palette, + bar-color: $bar-color, + handle-color: $handle-color, + expander-color: $expander-color, + border-radius: $border-radius-handle, + size: $size + )); +} + +/// @param {Map} $theme - The theme used to style the component. +/// @requires {mixin} igx-root-css-vars +/// @requires rem +/// @requires --var +@mixin igx-splitter($theme) { + @include igx-root-css-vars($theme); + $splitter-color: --var($theme, 'bar-color'); + $hitbox-size: 4px; + $debug-hitbox: false; + $hitbox-debug-color: rgba(coral, .24); + + %handle-area { + position: absolute; + content: ''; + width: 100%; + height: $hitbox-size; + background: if($debug-hitbox, $hitbox-debug-color, transparent); + } + + %handle-area--vertical { + width: $hitbox-size; + height: 100%; + } + + %igx-splitter-bar { + position: relative; + display: flex; + flex-grow: 1; + justify-content: center; + align-items: center; + background: $splitter-color; + border: 1px solid $splitter-color; + cursor: row-resize; + z-index: 99; + opacity: .68; + transition: opacity .15s $ease-out-quad !important; + + &::before { + @extend %handle-area; + top: 100%; + } + + &::after { + @extend %handle-area; + bottom: 100%; + } + + &:hover { + transition: all .25s ease-out; + opacity: 1; + } + } + + %igx-splitter-bar--vertical { + flex-direction: column; + height: 100%; + cursor: col-resize; + + &::before { + @extend %handle-area--vertical; + top: 0; + right: 100%; + } + + &::after { + @extend %handle-area--vertical; + top: 0; + left: 100%; + } + } + + %igx-splitter-handle { + background: --var($theme, 'handle-color'); + border-radius: --var($theme, 'border-radius'); + } + + %igx-splitter-handle--horizontal { + width: 25%; + height: --var($theme, 'size'); + margin: 0 rem(48px); + } + + %igx-splitter-handle--vertical { + width: --var($theme, 'size'); + height: 25%; + margin: rem(48px) 0; + } + + %igx-splitter-hitbox { + position: absolute; + content: ''; + background: if($debug-hitbox, $hitbox-debug-color, transparent); + } + + %igx-splitter-expander { + position: relative; + width: 0; + height: 0; + border-right: --var($theme, 'size') solid transparent; + border-left: --var($theme, 'size') solid transparent; + cursor: pointer; + z-index: 1; + } + + %igx-splitter-expander--start { + border-bottom: --var($theme, 'size') solid --var($theme, 'expander-color'); + + &::before { + @extend %igx-splitter-hitbox; + top: calc(100% - #{map-get($theme, 'size')}); + left: calc(100% - #{map-get($theme, 'size') * 2}); + width: #{map-get($theme, 'size') * 4}; + height: #{map-get($theme, 'size') * 3}; + } + } + + %igx-splitter-expander--end { + border-bottom: unset; + border-top: --var($theme, 'size') solid --var($theme, 'expander-color'); + + &::before { + @extend %igx-splitter-hitbox; + top: calc(100% - #{map-get($theme, 'size') * 2}); + left: calc(100% - #{map-get($theme, 'size') * 2}); + width: #{map-get($theme, 'size') * 4}; + height: #{map-get($theme, 'size') * 3}; + } + } + + %igx-splitter-expander--start-vertical { + border-top: --var($theme, 'size') solid transparent; + border-right: --var($theme, 'size') solid --var($theme, 'expander-color'); + border-bottom: --var($theme, 'size') solid transparent; + border-left: unset; + + &::before { + @extend %igx-splitter-hitbox; + top: calc(100% - #{map-get($theme, 'size') * 2}); + left: calc(100% - #{map-get($theme, 'size')}); + width: #{map-get($theme, 'size') * 3}; + height: #{map-get($theme, 'size') * 4}; + } + } + + %igx-splitter-expander--end-vertical { + border-top: --var($theme, 'size') solid transparent; + border-right: unset; + border-bottom: --var($theme, 'size') solid transparent; + border-left: --var($theme, 'size') solid --var($theme, 'expander-color'); + + &::before { + @extend %igx-splitter-hitbox; + left: calc(100% - #{map-get($theme, 'size') * 2}); + top: calc(100% - #{map-get($theme, 'size') * 2}); + height: #{map-get($theme, 'size') * 4}; + width: #{map-get($theme, 'size') * 3}; + } + } +} diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss b/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss index 8fc3548ad05..8e335642c48 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/_core.scss @@ -53,6 +53,7 @@ @import '../components/radio/radio-component'; @import '../components/scrollbar/scrollbar-component'; @import '../components/slider/slider-component'; +@import '../components/splitter/splitter-component'; @import '../components/snackbar/snackbar-component'; @import '../components/switch/switch-component'; @import '../components/tabs/tabs-component'; diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss b/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss index b8eb967d04d..bdacac534b4 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/_index.scss @@ -38,6 +38,7 @@ @import '../components/switch/switch-theme'; @import '../components/snackbar/snackbar-theme'; @import '../components/slider/slider-theme'; +@import '../components/splitter/splitter-theme'; @import '../components/ripple/ripple-theme'; @import '../components/radio/radio-theme'; @import '../components/progress/progress-theme'; @@ -198,6 +199,14 @@ )); } + @if not(index($exclude, 'igx-splitter')) { + @include igx-splitter(igx-splitter-theme( + $palette, + $schema, + $border-radius: $roundness, + )); + } + @if not(index($exclude, 'igx-checkbox')) { @include igx-checkbox(igx-checkbox-theme( $palette, diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss index c7cdbec9843..a16012b71f8 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_index.scss @@ -38,6 +38,7 @@ @import './scrollbar'; @import './slider'; @import './snackbar'; +@import './splitter'; @import './switch'; @import './tabs'; @import './time-picker'; @@ -82,6 +83,7 @@ /// @property {Map} igx-scrollbar [$_dark-scrollbar] /// @property {Map} igx-slider [$_dark-slider] /// @property {Map} igx-snackbar [$_dark-snackbar] +/// @property {Map} igx-splitter [$_dark-splitter] /// @property {Map} igx-switch [$_dark-switch] /// @property {Map} igx-tabs [$_dark-tabs] /// @property {Map} igx-time-picker [$_dark-time-picker] @@ -124,6 +126,7 @@ $dark-schema: ( igx-scrollbar: $_dark-scrollbar, igx-slider: $_dark-slider, igx-snackbar: $_dark-snackbar, + igx-splitter: $_dark-splitter, igx-switch: $_dark-switch, igx-tabs: $_dark-tabs, igx-time-picker: $_dark-time-picker, @@ -169,6 +172,7 @@ $dark-schema: ( /// @property {map} igx-scrollbar [$_dark-fluent-scrollbar], /// @property {map} igx-slider [$_dark-fluent-slider], /// @property {map} igx-snackbar [$_dark-fluent-snackbar], +/// @property {map} igx-splitter [$_dark-fluent-splitter], /// @property {map} igx-switch [$_dark-fluent-switch], /// @property {map} igx-tabs [$_dark-fluent-tabs], /// @property {map} igx-time-picker [$_dark-fluent-time-picker], @@ -211,6 +215,7 @@ $dark-fluent-schema: ( igx-scrollbar: $_dark-fluent-scrollbar, igx-slider: $_dark-fluent-slider, igx-snackbar: $_dark-fluent-snackbar, + igx-splitter: $_dark-fluent-splitter, igx-switch: $_dark-fluent-switch, igx-tabs: $_dark-fluent-tabs, igx-time-picker: $_dark-fluent-time-picker, @@ -256,6 +261,7 @@ $dark-fluent-schema: ( /// @property {map} igx-scrollbar [$_dark-bootstrap-scrollbar], /// @property {map} igx-slider [$_dark-bootstrap-slider], /// @property {map} igx-snackbar [$_dark-bootstrap-snackbar], +/// @property {map} igx-splitter [$_dark-bootstrap-splitter], /// @property {map} igx-switch [$_dark-bootstrap-switch], /// @property {map} igx-tabs [$_dark-bootstrap-tabs], /// @property {map} igx-time-picker [$_dark-bootstrap-time-picker], @@ -298,6 +304,7 @@ $dark-bootstrap-schema: ( igx-scrollbar: $_dark-bootstrap-scrollbar, igx-slider: $_dark-bootstrap-slider, igx-snackbar: $_dark-bootstrap-snackbar, + igx-splitter: $_dark-bootstrap-splitter, igx-switch: $_dark-bootstrap-switch, igx-tabs: $_dark-bootstrap-tabs, igx-time-picker: $_dark-bootstrap-time-picker, diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_scrollbar.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_scrollbar.scss index 1ae63442f09..daa82c7bf96 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_scrollbar.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_scrollbar.scss @@ -20,11 +20,6 @@ $_dark-scrollbar: extend( igx-color: 'surface', lighten: 20% ), - - track-background: ( - igx-color: 'surface', - lighten: 5% - ), ) ); diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_splitter.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_splitter.scss new file mode 100644 index 00000000000..27f81fe897c --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/dark/_splitter.scss @@ -0,0 +1,39 @@ +@import '../light/splitter'; + +//// +/// @group schemas +/// @access private +/// @author Simeon Simeonoff +//// + +/// Generates a dark splitter schema. +/// @type {Map} +/// @requires {function} extend +/// @requires $_light-splitter +$_dark-splitter: extend( + $_light-splitter, + ( + handle-color: ( + igx-color: 'surface', + lighten: 20% + ), + + expander-color: ( + igx-color: 'surface', + lighten: 20% + ), + ) +); + +/// Generates a fluent splitter schema. +/// @type {Map} +/// @requires {function} extend +/// @requires $_fluent-splitter +$_dark-fluent-splitter: extend($_fluent-splitter); + +/// Generates a bootstrap splitter schema. +/// @type {Map} +/// @requires {function} extend +/// @requires $_bootstrap-splitter +$_dark-bootstrap-splitter: extend($_bootstrap-splitter); + diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss index c91b32b9d8a..4f67c8e036e 100644 --- a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_index.scss @@ -39,6 +39,7 @@ @import './scrollbar'; @import './slider'; @import './snackbar'; +@import './splitter'; @import './switch'; @import './tabs'; @import './time-picker'; @@ -83,6 +84,7 @@ /// @property {Map} igx-scrollbar [$_light-scrollbar] /// @property {Map} igx-slider [$_light-slider] /// @property {Map} igx-snackbar [$_light-snackbar] +/// @property {Map} igx-splitter [$_light-splitter] /// @property {Map} igx-switch [$_light-switch] /// @property {Map} igx-tabs [$_light-tabs] /// @property {Map} igx-time-picker [$_light-time-picker] @@ -125,6 +127,7 @@ $light-schema: ( igx-scrollbar: $_light-scrollbar, igx-slider: $_light-slider, igx-snackbar: $_light-snackbar, + igx-splitter: $_light-splitter, igx-switch: $_light-switch, igx-tabs: $_light-tabs, igx-time-picker: $_light-time-picker, @@ -169,6 +172,7 @@ $light-fluent-schema: ( igx-scrollbar: $_fluent-scrollbar, igx-slider: $_fluent-slider, igx-snackbar: $_fluent-snackbar, + igx-splitter: $_fluent-splitter, igx-switch: $_fluent-switch, igx-tabs: $_fluent-tabs, igx-time-picker: $_fluent-time-picker, @@ -213,6 +217,7 @@ $light-bootstrap-schema: ( igx-scrollbar: $_bootstrap-scrollbar, igx-slider: $_bootstrap-slider, igx-snackbar: $_bootstrap-snackbar, + igx-splitter: $_bootstrap-splitter, igx-switch: $_bootstrap-switch, igx-tabs: $_bootstrap-tabs, igx-time-picker: $_bootstrap-time-picker, diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_splitter.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_splitter.scss new file mode 100644 index 00000000000..08b0b2e9777 --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/light/_splitter.scss @@ -0,0 +1,57 @@ +@import '../shape/splitter'; + +//// +/// @group schemas +/// @access private +/// @author Simeon Simeonoff +//// + +/// Generates a light splitter schema. +/// @type {Map} +/// @property {Map} icon-background [igx-color: ('grays', 400)]- The background color used for splitters of type icon. +/// @property {Map} icon-color [igx-color: ('grays', 800)] - The icon color used for splitters of type icon. +/// @property {Map} initials-background [igx-color: ('grays', 400)] - The background color used for splitters of type initials. +/// @property {Map} initials-color [igx-color: ('grays', 800)] - The text color used for splitters of type initials. +/// @property {Color} image-background [transparent] - The background color used for splitters of type image. +/// @see $default-palette +$_light-splitter: extend( + $_default-shape-splitter, + ( + bar-color: ( + igx-color: 'surface', + darken: 5% + ), + + handle-color: ( + igx-color: 'surface', + darken: 20% + ), + + expander-color: ( + igx-color: 'surface', + darken: 20% + ), + + size: 4px + ) +); + +/// Generates a fluent splitter schema. +/// @type {Map} +/// @requires {function} extend +/// @requires $_light-splitter +$_fluent-splitter: extend($_light-splitter); + +/// Generates a bootstrap splitter schema. +/// @type {Map} +/// @requires {function} extend +/// @requires $_light-splitter +/// @requires $_bootstrap-shape-splitter +$_bootstrap-splitter: extend( + $_light-splitter, + $_bootstrap-shape-splitter, + ( + variant: 'bootstrap', + ) +); + diff --git a/projects/igniteui-angular/src/lib/core/styles/themes/schemas/shape/_splitter.scss b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/shape/_splitter.scss new file mode 100644 index 00000000000..6d82113c37e --- /dev/null +++ b/projects/igniteui-angular/src/lib/core/styles/themes/schemas/shape/_splitter.scss @@ -0,0 +1,23 @@ +//// +/// @group schemas +/// @access private +/// @author Simeon Simeonoff +//// +$_default-shape-splitter: ( + border-radius: 1, +); + +/// @type Map +$_round-shape-splitter: ( + border-radius: 1, +); + +/// @type Map +$_square-shape-splitter: ( + border-radius: 0, +); + +/// @type Map +$_bootstrap-shape-splitter: ( + border-radius: 1, +); diff --git a/projects/igniteui-angular/src/lib/grids/grid-toolbar.component.html b/projects/igniteui-angular/src/lib/grids/grid-toolbar.component.html deleted file mode 100644 index d77672b7b7b..00000000000 --- a/projects/igniteui-angular/src/lib/grids/grid-toolbar.component.html +++ /dev/null @@ -1,83 +0,0 @@ - - {{ getTitle() }} - - -
- - -
- -
-
- -
- -
- - - - - -
-
- - - - -
- -
- - - -
    -
  • {{ getExportExcelText() }}
  • -
  • {{ getExportCsvText() }}
  • -
-
-
-
diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-toolbar.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-toolbar.spec.ts index 9ff4a52b3a1..246401c834e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-toolbar.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-toolbar.spec.ts @@ -402,8 +402,10 @@ describe('IgxGrid - Grid Toolbar #grid', () => { const button = getColumnPinningButton(fixture); expect(button).toBeDefined(); + const icon = button.nativeElement.querySelector('igx-icon'); + const iconName = icon.getAttribute('name'); const btnText = button.nativeElement.innerText.toLowerCase(); - expect(btnText.includes('0') && btnText.includes('lock_open')).toBe(true); + expect(btnText.includes('0') && iconName === 'unpin').toBe(true); }); it('toggleColumnPinningUI() method opens and closes the ColumnPinning dropdown.', () => { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts index 3962afcd032..531f35bcd66 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pinning.spec.ts @@ -14,7 +14,7 @@ import { IGridCellEventArgs } from '../common/events'; import { IgxColumnComponent } from '../columns/column.component'; import { ColumnPinningPosition } from '../common/enums'; import { IPinningConfig } from '../common/grid.interface'; -import { wait } from '../../test-utils/ui-interactions.spec'; +import { wait, UIInteractions } from '../../test-utils/ui-interactions.spec'; import { GridSummaryFunctions } from '../../test-utils/grid-functions.spec'; describe('IgxGrid - Column Pinning #grid', () => { @@ -23,6 +23,7 @@ describe('IgxGrid - Column Pinning #grid', () => { const FIXED_HEADER_CSS = 'igx-grid__th--pinned'; const FIXED_CELL_CSS = 'igx-grid__td--pinned'; const FIRST_PINNED_CELL_CSS = 'igx-grid__td--pinned-first'; + const DEBOUNCETIME = 30; describe('To Start', () => { configureTestSuite(); @@ -417,6 +418,39 @@ describe('IgxGrid - Column Pinning #grid', () => { expect(grid.pinnedColumns.length).toEqual(1); expect(grid.unpinnedColumns.length).toEqual(2); })); + + it('should allow navigating to/from pinned area', (async() => { + pending('https://github.com/IgniteUI/igniteui-angular/pull/6910'); + const fix = TestBed.createComponent(DefaultGridComponent); + fix.detectChanges(); + const grid = fix.componentInstance.instance; + + const cellContactName = grid.getCellByColumn(0, 'ContactName'); + const range = { + rowStart: cellContactName.rowIndex, + rowEnd: cellContactName.rowIndex, + columnStart: cellContactName.visibleColumnIndex, + columnEnd: cellContactName.visibleColumnIndex + }; + grid.selectRange(range); + grid.navigation.activeNode = {row: cellContactName.rowIndex, column: cellContactName.visibleColumnIndex}; + fix.detectChanges(); + + grid.navigation.dispatchEvent(UIInteractions.getKeyboardEvent('keydown', 'ArrowRight')); + await wait(DEBOUNCETIME); + fix.detectChanges(); + + const cellID = grid.getCellByColumn(0, 'ID'); + expect(cellID.active).toBe(true); + expect(cellContactName.active).toBe(false); + + grid.navigation.dispatchEvent(UIInteractions.getKeyboardEvent('keydown', 'ArrowLeft')); + await wait(DEBOUNCETIME); + fix.detectChanges(); + + expect(cellID.active).toBe(false); + expect(cellContactName.active).toBe(true); + })); }); describe('To End', () => { @@ -639,6 +673,61 @@ describe('IgxGrid - Column Pinning #grid', () => { expect(fistPinnedHeaders.classes['igx-grid__mrl-block']).toBeTruthy(); expect(fistPinnedHeaders.classes['igx-grid__th--pinned-first']).toBeTruthy(); }); + + it('should allow navigating to/from pinned area', (async() => { + pending('https://github.com/IgniteUI/igniteui-angular/pull/6910'); + const fix = TestBed.createComponent(GridRightPinningComponent); + fix.detectChanges(); + const grid = fix.componentInstance.instance as any; + + const cellCompanyName = grid.getCellByColumn(0, 'CompanyName'); + const range = { rowStart: 0, rowEnd: 0, columnStart: 9, columnEnd: 9 }; + grid.selectRange(range); + grid.navigation.activeNode = {row: 0, column: 9}; + fix.detectChanges(); + expect(cellCompanyName.active).toBe(true); + + grid.navigation.dispatchEvent(UIInteractions.getKeyboardEvent('keydown', 'ArrowLeft')); + await wait(DEBOUNCETIME); + fix.detectChanges(); + const cellFax = grid.getCellByColumn(0, 'Fax'); + expect(cellFax.active).toBe(true); + expect(cellCompanyName.active).toBe(false); + + grid.navigation.dispatchEvent(UIInteractions.getKeyboardEvent('keydown', 'ArrowRight')); + await wait(DEBOUNCETIME); + fix.detectChanges(); + expect(cellFax.active).toBe(false); + expect(cellCompanyName.active).toBe(true); + })); + + it('should allow navigating to/from pinned area using Ctrl+Left/Right', (async() => { + pending('https://github.com/IgniteUI/igniteui-angular/pull/6910'); + const fix = TestBed.createComponent(GridRightPinningComponent); + fix.detectChanges(); + const grid = fix.componentInstance.instance as any; + + const cellCompanyName = grid.getCellByColumn(0, 'CompanyName'); + const range = { rowStart: 0, rowEnd: 0, columnStart: 9, columnEnd: 9 }; + grid.selectRange(range); + grid.navigation.activeNode = {row: 0, column: 9}; + fix.detectChanges(); + expect(cellCompanyName.active).toBe(true); + + grid.navigation.dispatchEvent(UIInteractions.getKeyboardEvent('keydown', 'ArrowLeft', false, false, true)); + await wait(DEBOUNCETIME); + fix.detectChanges(); + const cellID = grid.getCellByColumn(0, 'ID'); + expect(cellID.active).toBe(true); + expect(cellCompanyName.active).toBe(false); + + grid.navigation.dispatchEvent(UIInteractions.getKeyboardEvent('keydown', 'ArrowRight', false, false, true)); + await wait(DEBOUNCETIME); + fix.detectChanges(); + const cellContactName = grid.getCellByColumn(0, 'ContactName'); + expect(cellID.active).toBe(false); + expect(cellContactName.active).toBe(true); + })); }); }); diff --git a/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar.component.html b/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar.component.html index 9da73586215..03e46ed448e 100644 --- a/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar.component.html +++ b/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar.component.html @@ -45,8 +45,8 @@
{{ page + 1 }} -  of  +  {{prepositionPage}}  {{ totalPages }}
@@ -19,8 +19,8 @@ [height]="'500px'" [rowSelectable]="false"> - - {{cell.row.pinned ? 'lock' : 'lock_open'}} + diff --git a/src/app/grid-row-pinning/grid-row-pinning.sample.ts b/src/app/grid-row-pinning/grid-row-pinning.sample.ts index fda771b0795..66df6272f90 100644 --- a/src/app/grid-row-pinning/grid-row-pinning.sample.ts +++ b/src/app/grid-row-pinning/grid-row-pinning.sample.ts @@ -10,6 +10,10 @@ import { IgxExcelExporterOptions } from 'igniteui-angular'; import { IPinningConfig } from 'projects/igniteui-angular/src/lib/grids/common/grid.interface'; +import { IgxIconService } from 'projects/igniteui-angular/src/lib/icon/icon.service'; +import icons from 'projects/igniteui-angular/src/lib/grids/filtering/svgIcons'; + +const FILTERING_ICONS_FONT_SET = 'filtering-icons'; @Component({ providers: [{ provide: IgxGridTransaction, useClass: IgxTransactionService }], @@ -39,7 +43,7 @@ export class GridRowPinningSampleComponent implements OnInit { @ViewChild(IgxGridStateDirective, { static: true }) public state: IgxGridStateDirective; - constructor(private excelExportService: IgxExcelExporterService) { + constructor(private excelExportService: IgxExcelExporterService, private iconService: IgxIconService) { } onRowChange() { @@ -108,6 +112,15 @@ export class GridRowPinningSampleComponent implements OnInit { // tslint:enable:max-line-length } + ngAfterViewInit() { + const pinnedIcons = icons.filter(icon => icon.name === 'pin' || icon.name === 'unpin'); + for (const icon of pinnedIcons) { + if (!this.iconService.isSvgIconCached(icon.name, FILTERING_ICONS_FONT_SET)) { + this.iconService.addSvgIconFromText(icon.name, icon.value, FILTERING_ICONS_FONT_SET); + } + } + } + togglePinRow(index) { const rec = this.data[index]; this.grid1.isRecordPinned(rec) ? diff --git a/src/app/routing.ts b/src/app/routing.ts index 2745466630d..3a506d660d2 100644 --- a/src/app/routing.ts +++ b/src/app/routing.ts @@ -23,6 +23,7 @@ import { NavdrawerSampleComponent } from './navdrawer/navdrawer.sample'; import { ProgressbarSampleComponent } from './progressbar/progressbar.sample'; import { RippleSampleComponent } from './ripple/ripple.sample'; import { SliderSampleComponent } from './slider/slider.sample'; +import { SplitterSampleComponent } from './splitter/slitter.sample'; import { SnackbarSampleComponent } from './snackbar/snackbar.sample'; import { ColorsSampleComponent } from './styleguide/colors/color.sample'; import { ShadowsSampleComponent } from './styleguide/shadows/shadows.sample'; @@ -250,6 +251,10 @@ const appRoutes = [ path: 'slider', component: SliderSampleComponent }, + { + path: 'splitter', + component: SplitterSampleComponent + }, { path: 'snackbar', component: SnackbarSampleComponent diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index abfa6badabb..990f2026a79 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -32,6 +32,7 @@ import { IgxSliderModule, IgxSnackbarModule, IgxSwitchModule, + IgxSplitterModule, IgxTabsModule, IgxTimePickerModule, IgxToastModule, @@ -74,6 +75,7 @@ const igniteModules = [ IgxSliderModule, IgxSnackbarModule, IgxSwitchModule, + IgxSplitterModule, IgxTabsModule, IgxTimePickerModule, IgxToastModule, diff --git a/src/app/splitter/slitter.sample.ts b/src/app/splitter/slitter.sample.ts new file mode 100644 index 00000000000..6e209b779ce --- /dev/null +++ b/src/app/splitter/slitter.sample.ts @@ -0,0 +1,94 @@ +import { Component, ViewChild } from '@angular/core'; +import { SplitterType } from 'projects/igniteui-angular/src/lib/splitter/splitter.component'; +import { RemoteService } from '../shared/remote.service'; +import { IgxGridComponent } from 'igniteui-angular'; + +@Component({ + selector: 'app-splitter-sample', + styleUrls: ['splitter.sample.scss'], + templateUrl: 'splitter.sample.html' +}) +export class SplitterSampleComponent { + type = SplitterType.Horizontal; + data1 = []; + data2 = []; + data3 = []; + primaryKeys = [ + { name: 'CustomerID', type: 'string', level: 0 }, + { name: 'OrderID', type: 'number', level: 1 }, + { name: 'EmployeeID', type: 'number', level: 2 }, + { name: 'ProductID', type: 'number', level: 2 } + ]; + + @ViewChild('grid1', { static: true }) + grid1: IgxGridComponent; + + @ViewChild('grid2', { static: true }) + grid2: IgxGridComponent; + + @ViewChild('grid3', { static: true }) + grid3: IgxGridComponent; + + constructor(private remoteService: RemoteService) { + remoteService.url = 'https://services.odata.org/V4/Northwind/Northwind.svc/'; + + this.remoteService.urlBuilder = (dataState) => this.buildUrl(dataState); + } + + public buildUrl(dataState) { + let qS = ''; + if (dataState) { + qS += `${dataState.key}?`; + + const level = dataState.level; + if (level > 0) { + const parentKey = this.primaryKeys.find((key) => key.level === level - 1); + const parentID = typeof dataState.parentID !== 'object' ? dataState.parentID : dataState.parentID[parentKey.name]; + + if (parentKey.type === 'string') { + qS += `$filter=${parentKey.name} eq '${parentID}'`; + } else { + qS += `$filter=${parentKey.name} eq ${parentID}`; + } + } + } + return `${this.remoteService.url}${qS}`; + } + + changeType() { + if (this.type === SplitterType.Horizontal) { + this.type = SplitterType.Vertical; + } else { + this.type = SplitterType.Horizontal; + } + } + + public ngAfterViewInit() { + this.remoteService.getData({ parentID: null, level: 0, key: 'Customers' }, (data) => { + this.data1 = data['value']; + this.grid1.isLoading = false; + this.grid1.selectRows([this.data1[0].CustomerID], true); + const evt = { newSelection: [this.data1[0].CustomerID]}; + this.onCustomerSelection(evt); + }); + } + + public onCustomerSelection(evt) { + const newSelection = evt.newSelection[0]; + this.remoteService.getData({ parentID: newSelection, level: 1, key: 'Orders' }, (data) => { + this.data2 = data['value']; + this.grid2.isLoading = false; + this.grid2.selectRows([this.data2[0].OrderID], true); + const evt = { newSelection: [this.data2[0].OrderID]}; + this.onOrderSelection(evt); + }); + } + + public onOrderSelection(evt) { + const newSelection = evt.newSelection[0]; + this.remoteService.getData({ parentID: newSelection, level: 2, key: 'Order_Details' }, (data) => { + this.data3 = data['value']; + this.grid3.isLoading = false; + }); + } +} diff --git a/src/app/splitter/splitter.sample.html b/src/app/splitter/splitter.sample.html new file mode 100644 index 00000000000..f1712652efe --- /dev/null +++ b/src/app/splitter/splitter.sample.html @@ -0,0 +1,76 @@ +
+ Toggle Splitter Direction +
Simple sample
+ + +
+ Pane 1 +
+
+ +
+ Pane 2 +
+
+ +
+ Pane 3 +
+
+
+
Nested splitters sample
+ + + + + Pane1.1 + + + Pane1.2 + + + + + + + Pane2.1 + + + Pane2.2 + + + + + +
Sample with Grids
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/app/splitter/splitter.sample.scss b/src/app/splitter/splitter.sample.scss new file mode 100644 index 00000000000..ef4134bb18c --- /dev/null +++ b/src/app/splitter/splitter.sample.scss @@ -0,0 +1 @@ +@import '../../../projects/igniteui-angular/src/lib/core/styles/themes/utilities'; diff --git a/src/styles/igniteui-theme.scss b/src/styles/igniteui-theme.scss index 43b370ee527..6c938f4f0a3 100644 --- a/src/styles/igniteui-theme.scss +++ b/src/styles/igniteui-theme.scss @@ -14,7 +14,7 @@ $igx-foreground-color: text-contrast($igx-background-color); body { background: $igx-background-color; - color: #222; + color: rgba($igx-foreground-color, .87); } @include igx-core($direction: ltr);