From 7056d99763633502038fc15b5da63803234ba3eb Mon Sep 17 00:00:00 2001 From: Blackbaud-PatrickOFriel Date: Wed, 1 Feb 2017 15:26:54 -0500 Subject: [PATCH 01/14] starting files for list-toolbar --- src/modules/list-toolbar/index.ts | 4 + .../list-toolbar-item-renderer.component.scss | 79 ++++++++++ .../list-toolbar-item-renderer.component.ts | 17 +++ .../list-toolbar-item.component.ts | 16 ++ .../list-toolbar/list-toolbar.component.html | 28 ++++ .../list-toolbar/list-toolbar.component.scss | 51 +++++++ .../list-toolbar/list-toolbar.component.ts | 140 ++++++++++++++++++ .../list-toolbar/list-toolbar.module.ts | 30 ++++ 8 files changed, 365 insertions(+) create mode 100644 src/modules/list-toolbar/index.ts create mode 100644 src/modules/list-toolbar/list-toolbar-item-renderer.component.scss create mode 100644 src/modules/list-toolbar/list-toolbar-item-renderer.component.ts create mode 100644 src/modules/list-toolbar/list-toolbar-item.component.ts create mode 100644 src/modules/list-toolbar/list-toolbar.component.html create mode 100644 src/modules/list-toolbar/list-toolbar.component.scss create mode 100644 src/modules/list-toolbar/list-toolbar.component.ts create mode 100644 src/modules/list-toolbar/list-toolbar.module.ts diff --git a/src/modules/list-toolbar/index.ts b/src/modules/list-toolbar/index.ts new file mode 100644 index 000000000..0d9aeea4e --- /dev/null +++ b/src/modules/list-toolbar/index.ts @@ -0,0 +1,4 @@ +export { SkyListToolbarComponent } from './list-toolbar.component'; +export { SkyListToolbarItemComponent } from './list-toolbar-item.component'; +export { SkyListToolbarItemRendererComponent } from './list-toolbar-item-renderer.component'; +export { SkyListToolbarModule } from './list-toolbar.module'; diff --git a/src/modules/list-toolbar/list-toolbar-item-renderer.component.scss b/src/modules/list-toolbar/list-toolbar-item-renderer.component.scss new file mode 100644 index 000000000..d2e4547d1 --- /dev/null +++ b/src/modules/list-toolbar/list-toolbar-item-renderer.component.scss @@ -0,0 +1,79 @@ +@import '../../scss/variables'; +@import '../../scss/mixins'; + +:host { + position: relative; + float: left; + margin-right: 6px; + + /deep/ button, /deep/ input { + height: 32px; + font-size: 13px; + } + + /deep/ a { + @include sky-link(); + } + + /deep/ .toolbar-item-container { + position: relative; + + input { + float: left; + width: 150px; + border-radius: 6px; + line-height: 1.42857; + border: 1px solid $sky-color-gray-lighter-er; + outline: none; + -webkit-appearance: none; + + &:focus { + box-shadow: 3px 0 8px 0 $sky-search-bar-outline-color; + border: 1px solid $sky-color-blue; + border-right: none; + } + + &[type='text'] { + padding: 6px; + padding-right: 30px; + } + + &::-webkit-input-placeholder { + font-style: italic; + } + &:-moz-placeholder { + font-style: italic; + } + &::-moz-placeholder { + font-style: italic; + } + &:-ms-input-placeholder { + font-style: italic; + } + } + + @media (min-width: 768px) { + input { + width: 300px; + } + } + + button { + position: absolute; + right: 0; + margin: 0; + border-color: transparent; + background-color: transparent; + cursor: pointer; + } + } + + /deep/ button { + border-radius: 3px; + color: $sky-color-black; + background-color: $sky-color-white; + border: 1px solid #e7eaec; + padding: 6px 12px; + cursor: pointer; + } +} diff --git a/src/modules/list-toolbar/list-toolbar-item-renderer.component.ts b/src/modules/list-toolbar/list-toolbar-item-renderer.component.ts new file mode 100644 index 000000000..b4a5f6a50 --- /dev/null +++ b/src/modules/list-toolbar/list-toolbar-item-renderer.component.ts @@ -0,0 +1,17 @@ +import { Component, ViewContainerRef, ViewChild, Input, TemplateRef, OnInit} from '@angular/core'; + +@Component({ + selector: 'sky-list-toolbar-item-renderer', + template: '', + styles: [require('./list-toolbar-item-renderer.component.scss')] +}) +export class SkyListToolbarItemRendererComponent implements OnInit { + @Input() public template: TemplateRef; + @ViewChild('container', { read: ViewContainerRef }) private container: ViewContainerRef; + + public ngOnInit() { + if (this.template !== undefined) { + this.container.createEmbeddedView(this.template, this); + } + } +} diff --git a/src/modules/list-toolbar/list-toolbar-item.component.ts b/src/modules/list-toolbar/list-toolbar-item.component.ts new file mode 100644 index 000000000..b68a70eed --- /dev/null +++ b/src/modules/list-toolbar/list-toolbar-item.component.ts @@ -0,0 +1,16 @@ +import { Component, Input, ContentChildren, TemplateRef, QueryList } from '@angular/core'; +import * as moment from 'moment'; + +@Component({ + selector: 'sky-list-toolbar-item', + template: '' +}) +export class SkyListToolbarItemComponent { + @Input() public id: string = moment().toDate().getTime().toString(); + @Input() public index: number = -1; + @Input() public location: string = 'left'; + @ContentChildren(TemplateRef) private templates: QueryList>; + public get template(): TemplateRef { + return this.templates.length > 0 ? this.templates.first : undefined; + } +} diff --git a/src/modules/list-toolbar/list-toolbar.component.html b/src/modules/list-toolbar/list-toolbar.component.html new file mode 100644 index 000000000..efb35d84d --- /dev/null +++ b/src/modules/list-toolbar/list-toolbar.component.html @@ -0,0 +1,28 @@ + + +
+
+ + + +
+ +
+ + diff --git a/src/modules/list-toolbar/list-toolbar.component.scss b/src/modules/list-toolbar/list-toolbar.component.scss new file mode 100644 index 000000000..4ff883c47 --- /dev/null +++ b/src/modules/list-toolbar/list-toolbar.component.scss @@ -0,0 +1,51 @@ +@import '../../scss/variables'; + +:host { + display: block; + width: 100%; + + /deep/ .standard, /deep/ .search { + min-height: 46px; + } + + /deep/ .sky-toolbar-standard { + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + height: 46px; + padding: 6px; + + sky-list-toolbar-item-renderer > a { + display: inline-block; + padding-top: 6px; + } + } + + /deep/ .sky-toolbar-search { + background-color: #eee; + + .primary-bar { + width: 100%; + display: block; + min-height: 46px; + padding: 7px; + + sky-list-toolbar-item-renderer { + width: 100%; + + .toolbar-item-container { + > input { + width: 100%; + } + } + } + } + + .secondary-bar { + width: 100%; + display: inline-block; + border-top: 1px solid #ccc; + padding: 6px 10px; + padding-bottom: 2px; + } + } +} diff --git a/src/modules/list-toolbar/list-toolbar.component.ts b/src/modules/list-toolbar/list-toolbar.component.ts new file mode 100644 index 000000000..284a09574 --- /dev/null +++ b/src/modules/list-toolbar/list-toolbar.component.ts @@ -0,0 +1,140 @@ +import { + Component, + ContentChildren, + QueryList, + ViewChild, + TemplateRef, + Input, + OnInit, + AfterContentInit, + ChangeDetectionStrategy +} from '@angular/core'; +import { + ListToolbarConfigSetSearchEnabledAction +} from './state/config/actions'; +import { Observable } from 'rxjs'; +import { + ListToolbarState, + ListToolbarStateDispatcher, + ListToolbarStateModel +} from './state'; +import { ListToolbarModel } from '../list/state/toolbar/toolbar.model'; +import { ListToolbarItemModel } from '../list/state/toolbar/toolbar-item.model'; +import { SkyListToolbarItemComponent } from './list-toolbar-item.component'; +import { ListState, ListStateDispatcher } from '../list/state'; +import { getValue } from 'microedge-rxstate/dist/helpers'; + +@Component({ + selector: 'sky-list-toolbar', + templateUrl: './list-toolbar.component.html', + styleUrls: ['./list-toolbar.component.scss'], + providers: [ + ListToolbarState, + ListToolbarStateDispatcher, + ListToolbarStateModel + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SkyListToolbarComponent implements OnInit, AfterContentInit { + @Input() public type: string = 'standard'; + @Input() public placeholder: string = 'Find in this list'; + @Input() public searchEnabled: boolean | Observable; + + /* tslint:disable */ + @Input('searchText') private searchTextInput: string | Observable; + /* tslint:enable */ + + @ContentChildren(SkyListToolbarItemComponent) + private toolbarItems: QueryList; + + @ViewChild('search') + private searchTemplate: TemplateRef; + + constructor( + private state: ListState, + private dispatcher: ListStateDispatcher, + private toolbarState: ListToolbarState, + public toolbarDispatcher: ListToolbarStateDispatcher + ) { + } + + public ngOnInit() { + this.dispatcher.toolbarExists(true); + getValue(this.searchTextInput, (searchText: string) => this.updateSearchText(searchText)); + getValue(this.searchEnabled, (searchEnabled: any) => + this.toolbarDispatcher.next( + new ListToolbarConfigSetSearchEnabledAction( + searchEnabled === undefined ? true : searchEnabled + ) + ) + ); + + this.dispatcher.toolbarAddItems([ + this.type !== 'search' ? + new ListToolbarItemModel({ + id: 'search', + template: this.searchTemplate, + location: 'center' + }) : + undefined + ].filter(s => s !== undefined)); + } + + public ngAfterContentInit() { + this.toolbarItems.forEach(toolbarItem => + this.dispatcher.toolbarAddItems( + [new ListToolbarItemModel(toolbarItem)], + toolbarItem.index + ) + ); + } + + get searchText() { + return this.state.map(s => s.search.searchText).distinctUntilChanged(); + } + + get leftTemplates() { + return Observable.combineLatest( + this.state.map(s => s.toolbar).distinctUntilChanged(), + this.view, + (toolbar: ListToolbarModel, view: string) => toolbar.items.filter( + (i: ListToolbarItemModel) => + i.location === 'left' && (i.view === undefined || i.view === view) + ) + ); + } + + get centerTemplates() { + return Observable.combineLatest( + this.state.map(s => s.toolbar).distinctUntilChanged(), + this.view, + (toolbar: ListToolbarModel, view: string) => toolbar.items.filter( + (i: ListToolbarItemModel) => + i.location === 'center' && (i.view === undefined || i.view === view) + ) + ); + } + + get rightTemplates() { + return Observable.combineLatest( + this.state.map(s => s.toolbar).distinctUntilChanged(), + this.view.distinctUntilChanged(), + (toolbar: ListToolbarModel, view: string) => { + return toolbar.items.filter( + (i: ListToolbarItemModel) => + i.location === 'right' && (i.view === undefined || i.view === view) + ); + }); + } + + + private updateSearchText(searchText: string) { + this.dispatcher.searchSetText(searchText); + } + + private get isSearchEnabled() { + return this.toolbarState.map(s => s.config) + .distinctUntilChanged() + .map(c => c.searchEnabled); + } +} diff --git a/src/modules/list-toolbar/list-toolbar.module.ts b/src/modules/list-toolbar/list-toolbar.module.ts new file mode 100644 index 000000000..e017ffbf7 --- /dev/null +++ b/src/modules/list-toolbar/list-toolbar.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SkyDropdownModule } from '../dropdown'; +import { SkyListToolbarComponent } from './list-toolbar.component'; +import { SkyListToolbarItemComponent } from './list-toolbar-item.component'; +import { SkyListToolbarItemRendererComponent } from './list-toolbar-item-renderer.component'; +import { SkyListToolbarSortComponent } from './list-toolbar-sort.component'; + +@NgModule({ + declarations: [ + SkyListToolbarComponent, + SkyListToolbarItemComponent, + SkyListToolbarItemRendererComponent, + SkyListToolbarSortComponent + ], + imports: [ + CommonModule, + SkyDropdownModule + ], + exports: [ + SkyListToolbarComponent, + SkyListToolbarItemComponent, + SkyListToolbarItemRendererComponent, + SkyListToolbarSortComponent + ], + providers: [ + ] +}) +export class SkyListToolbarModule { +} From a9da0a03c003d039ed0cd2dbdc4ae0faeb4d7f21 Mon Sep 17 00:00:00 2001 From: Blackbaud-PatrickOFriel Date: Wed, 1 Feb 2017 17:49:03 -0500 Subject: [PATCH 02/14] Add standalone toolbar component Also: - tweak input-group and search styles to not use table display since we can use flex instead. - Used toolbar component in search demo. TODO: - toolbar unit tests - toolbar visual tests - toolbar docs - use toolbar in list-toolbar component --- .../search/search-demo.component.html | 41 ++++++++----------- src/core.ts | 3 ++ .../list-toolbar-item.component.ts | 2 +- .../list-toolbar/list-toolbar.component.ts | 6 ++- src/modules/search/search.component.scss | 10 ++--- src/modules/toolbar/index.ts | 3 ++ .../toolbar/toolbar-item.component.html | 3 ++ .../toolbar/toolbar-item.component.scss | 5 +++ src/modules/toolbar/toolbar-item.component.ts | 12 ++++++ src/modules/toolbar/toolbar.component.html | 3 ++ src/modules/toolbar/toolbar.component.scss | 11 +++++ src/modules/toolbar/toolbar.component.ts | 12 ++++++ src/modules/toolbar/toolbar.module.ts | 20 +++++++++ src/scss/_input-groups.scss | 18 +++----- src/scss/_variables.scss | 6 +++ 15 files changed, 108 insertions(+), 47 deletions(-) create mode 100644 src/modules/toolbar/index.ts create mode 100644 src/modules/toolbar/toolbar-item.component.html create mode 100644 src/modules/toolbar/toolbar-item.component.scss create mode 100644 src/modules/toolbar/toolbar-item.component.ts create mode 100644 src/modules/toolbar/toolbar.component.html create mode 100644 src/modules/toolbar/toolbar.component.scss create mode 100644 src/modules/toolbar/toolbar.component.ts create mode 100644 src/modules/toolbar/toolbar.module.ts diff --git a/src/app/components/search/search-demo.component.html b/src/app/components/search/search-demo.component.html index 613fbb1d7..5fc710247 100644 --- a/src/app/components/search/search-demo.component.html +++ b/src/app/components/search/search-demo.component.html @@ -1,29 +1,20 @@ - - - + Predefined search text + + + + + + +
diff --git a/src/core.ts b/src/core.ts index b733b86f0..83b67c6ab 100644 --- a/src/core.ts +++ b/src/core.ts @@ -25,6 +25,7 @@ import { SkyRadioModule } from './modules/radio'; import { SkyRepeaterModule } from './modules/repeater'; import { SkySearchModule } from './modules/search'; import { SkyTabsModule } from './modules/tabs'; +import { SkyToolbarModule } from './modules/toolbar'; import { SkyTilesModule } from './modules/tiles'; import { SkyWaitModule } from './modules/wait'; @@ -54,6 +55,7 @@ import { SkyWaitModule } from './modules/wait'; SkySearchModule, SkyTabsModule, SkyTilesModule, + SkyToolbarModule, SkyWaitModule ] }) @@ -84,6 +86,7 @@ export * from './modules/repeater'; export * from './modules/search'; export * from './modules/tabs'; export * from './modules/tiles'; +export * from './modules/toolbar'; export * from './modules/wait'; export const SKY_PROVIDERS: any[] = [ diff --git a/src/modules/list-toolbar/list-toolbar-item.component.ts b/src/modules/list-toolbar/list-toolbar-item.component.ts index b68a70eed..4887f00a0 100644 --- a/src/modules/list-toolbar/list-toolbar-item.component.ts +++ b/src/modules/list-toolbar/list-toolbar-item.component.ts @@ -1,5 +1,5 @@ import { Component, Input, ContentChildren, TemplateRef, QueryList } from '@angular/core'; -import * as moment from 'moment'; +let moment = require('moment'); @Component({ selector: 'sky-list-toolbar-item', diff --git a/src/modules/list-toolbar/list-toolbar.component.ts b/src/modules/list-toolbar/list-toolbar.component.ts index 284a09574..3c642c2d6 100644 --- a/src/modules/list-toolbar/list-toolbar.component.ts +++ b/src/modules/list-toolbar/list-toolbar.component.ts @@ -1,3 +1,4 @@ +/* import { Component, ContentChildren, @@ -41,9 +42,9 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { @Input() public searchEnabled: boolean | Observable; /* tslint:disable */ - @Input('searchText') private searchTextInput: string | Observable; + //@Input('searchText') private searchTextInput: string | Observable; /* tslint:enable */ - +/* @ContentChildren(SkyListToolbarItemComponent) private toolbarItems: QueryList; @@ -138,3 +139,4 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { .map(c => c.searchEnabled); } } +*/ diff --git a/src/modules/search/search.component.scss b/src/modules/search/search.component.scss index 980bdf1ff..b5f3cbe74 100644 --- a/src/modules/search/search.component.scss +++ b/src/modules/search/search.component.scss @@ -2,7 +2,7 @@ @media (min-width: $sky-screen-sm-min) { .sky-search-input-container { - max-width: 300px; + min-width: 300px; } } @@ -52,10 +52,12 @@ .sky-search-item-dismiss { flex-shrink: 0; + display: flex; } .sky-search-item-input { flex-grow: 1; + display: flex; } /* This is needed or else the input will be tiny @@ -128,9 +130,3 @@ border: 2px solid $sky-color-green; padding: 5px 11px; } - -.sky-search-container { - display: inline-block; - vertical-align: middle; - margin-bottom: 0; -} diff --git a/src/modules/toolbar/index.ts b/src/modules/toolbar/index.ts new file mode 100644 index 000000000..72b6e8ab1 --- /dev/null +++ b/src/modules/toolbar/index.ts @@ -0,0 +1,3 @@ +export { SkyToolbarComponent } from './toolbar.component'; +export { SkyToolbarItemComponent } from './toolbar-item.component'; +export { SkyToolbarModule } from './toolbar.module'; diff --git a/src/modules/toolbar/toolbar-item.component.html b/src/modules/toolbar/toolbar-item.component.html new file mode 100644 index 000000000..3c16427e8 --- /dev/null +++ b/src/modules/toolbar/toolbar-item.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/modules/toolbar/toolbar-item.component.scss b/src/modules/toolbar/toolbar-item.component.scss new file mode 100644 index 000000000..24f709a94 --- /dev/null +++ b/src/modules/toolbar/toolbar-item.component.scss @@ -0,0 +1,5 @@ +@import '../../scss/mixins'; + +.sky-toolbar-item { + margin-right: $sky-margin-half; +} diff --git a/src/modules/toolbar/toolbar-item.component.ts b/src/modules/toolbar/toolbar-item.component.ts new file mode 100644 index 000000000..04918eb9b --- /dev/null +++ b/src/modules/toolbar/toolbar-item.component.ts @@ -0,0 +1,12 @@ +import { + Component +} from '@angular/core'; + +@Component({ + selector: 'sky-toolbar-item', + styleUrls: ['./toolbar-item.component.scss'], + templateUrl: './toolbar-item.component.html' +}) +export class SkyToolbarItemComponent { + +} diff --git a/src/modules/toolbar/toolbar.component.html b/src/modules/toolbar/toolbar.component.html new file mode 100644 index 000000000..d7b65b59b --- /dev/null +++ b/src/modules/toolbar/toolbar.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/modules/toolbar/toolbar.component.scss b/src/modules/toolbar/toolbar.component.scss new file mode 100644 index 000000000..6bf938c93 --- /dev/null +++ b/src/modules/toolbar/toolbar.component.scss @@ -0,0 +1,11 @@ +@import '../../scss/mixins'; + +.sky-toolbar-container { + min-height: $sky-toolbar-min-height; + background-color: $sky-toolbar-background-color; + padding: $sky-padding-half $sky-padding; + @include sky-border(dark, top, bottom); + display: flex; + align-items: center; + position: relative; +} diff --git a/src/modules/toolbar/toolbar.component.ts b/src/modules/toolbar/toolbar.component.ts new file mode 100644 index 000000000..6f88fe64d --- /dev/null +++ b/src/modules/toolbar/toolbar.component.ts @@ -0,0 +1,12 @@ +import { + Component +} from '@angular/core'; + +@Component({ + selector: 'sky-toolbar', + styleUrls: ['./toolbar.component.scss'], + templateUrl: './toolbar.component.html' +}) +export class SkyToolbarComponent { + +} diff --git a/src/modules/toolbar/toolbar.module.ts b/src/modules/toolbar/toolbar.module.ts new file mode 100644 index 000000000..30edeb47a --- /dev/null +++ b/src/modules/toolbar/toolbar.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { SkyToolbarComponent } from './toolbar.component'; +import { SkyToolbarItemComponent } from './toolbar-item.component'; + +@NgModule({ + declarations: [ + SkyToolbarComponent, + SkyToolbarItemComponent + ], + imports: [ + CommonModule + ], + exports: [ + SkyToolbarComponent, + SkyToolbarItemComponent + ] +}) +export class SkyToolbarModule { } diff --git a/src/scss/_input-groups.scss b/src/scss/_input-groups.scss index bf7df5b48..5b631bf15 100644 --- a/src/scss/_input-groups.scss +++ b/src/scss/_input-groups.scss @@ -2,8 +2,7 @@ .sky-input-group { position: relative; // For dropdowns - display: table; - border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table + display: flex; .sky-form-control { // Ensure that the input is always above the *appended* addon button for @@ -16,23 +15,14 @@ } } -// Display as table-cell .sky-input-group-btn, .sky-input-group .sky-form-control { - display: table-cell; &:not(:first-child):not(:last-child) { border-radius: 0; } } -// Addon and addon wrapper for buttons -.sky-input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; // Match the inputs -} - // Reset rounded corners .sky-input-group .sky-form-control:first-child, .sky-input-group-btn:first-child > .sky-btn, @@ -42,12 +32,14 @@ .sky-input-group .sky-form-control:last-child, .sky-input-group-btn:last-child > .sky-btn, -.sky-input-group-btn:first-child > .sky-btn:not(:first-child) { +.sky-input-group-btn:first-child > .sky-btn:not(:first-child), +.sky-input-group-btn:not(:first-child) > .sky-btn { @include sky-border-left-radius(0); } // Button input groups .sky-input-group-btn { + display: flex; position: relative; // Jankily prevent input button groups from wrapping with `white-space` and // `font-size` in combination with `inline-block` on buttons. @@ -57,6 +49,8 @@ // Negative margin for spacing, position for bringing hovered/focused/actived // element above the siblings. > .sky-btn { + padding-top: 0; + padding-bottom: 0; position: relative; + .sky-btn { margin-left: -1px; diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss index 7b6d22899..8062d61d0 100644 --- a/src/scss/_variables.scss +++ b/src/scss/_variables.scss @@ -135,6 +135,12 @@ $sky-paging-border-color: #ddd !default; $sky-paging-background-color: $sky-color-white !default; // end paging +// begin toolbar +$sky-toolbar-background-color: $sky-color-white !default; +$sky-toolbar-min-height: 49px; +// end toolbar + + // begin wait $sky-wait-color: $sky-color-blue !default; $sky-wait-mask-color: $sky-color-white !default; From 69245e2d6215c3939e833ff55a5f78f649e5f0bf Mon Sep 17 00:00:00 2001 From: Blackbaud-PatrickOFriel Date: Thu, 2 Feb 2017 11:01:12 -0500 Subject: [PATCH 03/14] dont worry about search type for now --- .../list-toolbar/list-toolbar.component.html | 48 ++++++++--------- .../list-toolbar/list-toolbar.component.scss | 51 ------------------- .../list-toolbar/list-toolbar.component.ts | 5 +- 3 files changed, 27 insertions(+), 77 deletions(-) delete mode 100644 src/modules/list-toolbar/list-toolbar.component.scss diff --git a/src/modules/list-toolbar/list-toolbar.component.html b/src/modules/list-toolbar/list-toolbar.component.html index efb35d84d..908a73e4d 100644 --- a/src/modules/list-toolbar/list-toolbar.component.html +++ b/src/modules/list-toolbar/list-toolbar.component.html @@ -1,28 +1,30 @@ - - -
-
- - - -
- +
+ + + + + + + + + + + +
diff --git a/src/modules/list-toolbar/list-toolbar.component.scss b/src/modules/list-toolbar/list-toolbar.component.scss deleted file mode 100644 index 4ff883c47..000000000 --- a/src/modules/list-toolbar/list-toolbar.component.scss +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../scss/variables'; - -:host { - display: block; - width: 100%; - - /deep/ .standard, /deep/ .search { - min-height: 46px; - } - - /deep/ .sky-toolbar-standard { - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; - height: 46px; - padding: 6px; - - sky-list-toolbar-item-renderer > a { - display: inline-block; - padding-top: 6px; - } - } - - /deep/ .sky-toolbar-search { - background-color: #eee; - - .primary-bar { - width: 100%; - display: block; - min-height: 46px; - padding: 7px; - - sky-list-toolbar-item-renderer { - width: 100%; - - .toolbar-item-container { - > input { - width: 100%; - } - } - } - } - - .secondary-bar { - width: 100%; - display: inline-block; - border-top: 1px solid #ccc; - padding: 6px 10px; - padding-bottom: 2px; - } - } -} diff --git a/src/modules/list-toolbar/list-toolbar.component.ts b/src/modules/list-toolbar/list-toolbar.component.ts index 3c642c2d6..297ba132c 100644 --- a/src/modules/list-toolbar/list-toolbar.component.ts +++ b/src/modules/list-toolbar/list-toolbar.component.ts @@ -1,4 +1,3 @@ -/* import { Component, ContentChildren, @@ -44,7 +43,7 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { /* tslint:disable */ //@Input('searchText') private searchTextInput: string | Observable; /* tslint:enable */ -/* + @ContentChildren(SkyListToolbarItemComponent) private toolbarItems: QueryList; @@ -139,4 +138,4 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { .map(c => c.searchEnabled); } } -*/ + From 516fc3cc26cdfb6933c266b861c698d97712072d Mon Sep 17 00:00:00 2001 From: Blackbaud-PatrickOFriel Date: Thu, 2 Feb 2017 14:39:50 -0500 Subject: [PATCH 04/14] update list state for toolbar and search stuff --- .../list-data-in-memory.provider.ts | 68 +++++++++++++++- .../list-toolbar-item-renderer.component.scss | 79 ------------------- .../list-toolbar-item-renderer.component.ts | 3 +- .../list-toolbar/list-toolbar.component.html | 2 +- .../list-toolbar/list-toolbar.component.ts | 28 ++++--- .../list-toolbar/list-toolbar.module.ts | 7 +- .../list-toolbar/state/config/actions.ts | 1 + .../list-toolbar/state/config/config.model.ts | 9 +++ .../state/config/config.orchestrator.ts | 23 ++++++ .../state/config/set-search-enabled.action.ts | 3 + src/modules/list-toolbar/state/index.ts | 3 + .../state/toolbar-state-action.type.ts | 6 ++ .../list-toolbar/state/toolbar-state.model.ts | 5 ++ .../state/toolbar-state.rxstate.ts | 10 +++ .../state/toolbar-state.state-node.ts | 16 ++++ src/modules/list/list-data-request.model.ts | 4 + src/modules/list/list.component.ts | 12 ++- src/modules/list/state/index.ts | 6 ++ .../list/state/list-state-action.type.ts | 15 +++- src/modules/list/state/list-state.model.ts | 5 ++ src/modules/list/state/list-state.rxstate.ts | 38 ++++++++- .../list/state/list-state.state-node.ts | 4 + src/modules/list/state/search/actions.ts | 3 + src/modules/list/state/search/search.model.ts | 13 +++ .../list/state/search/search.orchestrator.ts | 38 +++++++++ .../search/set-field-selectors.action.ts | 3 + .../list/state/search/set-functions.action.ts | 3 + .../state/search/set-search-text.action.ts | 3 + src/modules/list/state/toolbar/actions.ts | 2 + src/modules/list/state/toolbar/load.action.ts | 7 ++ .../list/state/toolbar/set-exists.action.ts | 3 + .../list/state/toolbar/toolbar-item.model.ts | 18 +++++ .../list/state/toolbar/toolbar.model.ts | 13 +++ .../state/toolbar/toolbar.orchestrator.ts | 48 +++++++++++ 34 files changed, 396 insertions(+), 105 deletions(-) delete mode 100644 src/modules/list-toolbar/list-toolbar-item-renderer.component.scss create mode 100644 src/modules/list-toolbar/state/config/actions.ts create mode 100644 src/modules/list-toolbar/state/config/config.model.ts create mode 100644 src/modules/list-toolbar/state/config/config.orchestrator.ts create mode 100644 src/modules/list-toolbar/state/config/set-search-enabled.action.ts create mode 100644 src/modules/list-toolbar/state/index.ts create mode 100644 src/modules/list-toolbar/state/toolbar-state-action.type.ts create mode 100644 src/modules/list-toolbar/state/toolbar-state.model.ts create mode 100644 src/modules/list-toolbar/state/toolbar-state.rxstate.ts create mode 100644 src/modules/list-toolbar/state/toolbar-state.state-node.ts create mode 100644 src/modules/list/state/search/actions.ts create mode 100644 src/modules/list/state/search/search.model.ts create mode 100644 src/modules/list/state/search/search.orchestrator.ts create mode 100644 src/modules/list/state/search/set-field-selectors.action.ts create mode 100644 src/modules/list/state/search/set-functions.action.ts create mode 100644 src/modules/list/state/search/set-search-text.action.ts create mode 100644 src/modules/list/state/toolbar/actions.ts create mode 100644 src/modules/list/state/toolbar/load.action.ts create mode 100644 src/modules/list/state/toolbar/set-exists.action.ts create mode 100644 src/modules/list/state/toolbar/toolbar-item.model.ts create mode 100644 src/modules/list/state/toolbar/toolbar.model.ts create mode 100644 src/modules/list/state/toolbar/toolbar.orchestrator.ts diff --git a/src/modules/list-data-provider-in-memory/list-data-in-memory.provider.ts b/src/modules/list-data-provider-in-memory/list-data-in-memory.provider.ts index e21bcee14..25c94c397 100644 --- a/src/modules/list-data-provider-in-memory/list-data-in-memory.provider.ts +++ b/src/modules/list-data-provider-in-memory/list-data-in-memory.provider.ts @@ -3,6 +3,7 @@ import { ListDataProvider } from '../list/list-data.provider'; import { ListDataRequestModel } from '../list/list-data-request.model'; import { ListDataResponseModel } from '../list/list-data-response.model'; import { ListItemModel } from '../list/state/items/item.model'; +import { ListSearchModel } from '../list/state/search/search.model'; let moment = require('moment'); @@ -10,11 +11,19 @@ export class SkyListInMemoryDataProvider extends ListDataProvider { private items: BehaviorSubject> = new BehaviorSubject>([]); + private lastItems: ListItemModel[]; + private lastSearch: ListSearchModel; + private lastSearchResults: ListItemModel[]; + private searchFunction: (data: any, searchText: string) => boolean; + constructor( - data?: Observable> + data?: Observable>, + searchFunction?: (data: any, searchText: string) => boolean ) { super(data); + this.searchFunction = searchFunction; + if (data) { data.subscribe(items => { this.items.next(items.map(d => @@ -29,7 +38,7 @@ export class SkyListInMemoryDataProvider extends ListDataProvider { } public get(request: ListDataRequestModel): Observable { - return this.items.map((result: Array) => { + return this.filteredItems(request).map((result: Array) => { let itemStart = (request.pageNumber - 1) * request.pageSize; let pagedResult = result.slice(itemStart, itemStart + request.pageSize); @@ -39,4 +48,59 @@ export class SkyListInMemoryDataProvider extends ListDataProvider { }); }); } + + private filteredItems(request: ListDataRequestModel): Observable> { + return this.items.map(items => { + let dataChanged = false; + let search = request.search; + + if (this.lastItems === undefined || this.lastItems !== items) { + dataChanged = true; + this.lastItems = items; + } + + let searchChanged = false; + if (this.lastSearch === undefined || this.lastSearch !== search) { + searchChanged = true; + this.lastSearch = search; + } + + let result = items; + + if (!dataChanged && !searchChanged && this.lastSearchResults !== undefined) { + result = this.lastSearchResults; + } else if (search.searchText !== undefined && search.searchText.length > 0) { + let searchText = search.searchText.toLowerCase(); + let searchFunctions: any[]; + if (this.searchFunction !== undefined) { + searchFunctions = [this.searchFunction]; + } else { + searchFunctions = search.functions; + } + + result = result.filter(item => { + let isMatch = false; + + for (let i = 0; i < searchFunctions.length; i++) { + let searchFunction = searchFunctions[i]; + let searchResult = searchFunction(item.data, searchText); + + if ( + (typeof searchResult === 'string' && searchResult.indexOf(searchText) !== -1) || + searchResult === true + ) { + isMatch = true; + break; + } + } + + return isMatch; + }); + + this.lastSearchResults = result; + } + + return result; + }); + } } diff --git a/src/modules/list-toolbar/list-toolbar-item-renderer.component.scss b/src/modules/list-toolbar/list-toolbar-item-renderer.component.scss deleted file mode 100644 index d2e4547d1..000000000 --- a/src/modules/list-toolbar/list-toolbar-item-renderer.component.scss +++ /dev/null @@ -1,79 +0,0 @@ -@import '../../scss/variables'; -@import '../../scss/mixins'; - -:host { - position: relative; - float: left; - margin-right: 6px; - - /deep/ button, /deep/ input { - height: 32px; - font-size: 13px; - } - - /deep/ a { - @include sky-link(); - } - - /deep/ .toolbar-item-container { - position: relative; - - input { - float: left; - width: 150px; - border-radius: 6px; - line-height: 1.42857; - border: 1px solid $sky-color-gray-lighter-er; - outline: none; - -webkit-appearance: none; - - &:focus { - box-shadow: 3px 0 8px 0 $sky-search-bar-outline-color; - border: 1px solid $sky-color-blue; - border-right: none; - } - - &[type='text'] { - padding: 6px; - padding-right: 30px; - } - - &::-webkit-input-placeholder { - font-style: italic; - } - &:-moz-placeholder { - font-style: italic; - } - &::-moz-placeholder { - font-style: italic; - } - &:-ms-input-placeholder { - font-style: italic; - } - } - - @media (min-width: 768px) { - input { - width: 300px; - } - } - - button { - position: absolute; - right: 0; - margin: 0; - border-color: transparent; - background-color: transparent; - cursor: pointer; - } - } - - /deep/ button { - border-radius: 3px; - color: $sky-color-black; - background-color: $sky-color-white; - border: 1px solid #e7eaec; - padding: 6px 12px; - cursor: pointer; - } -} diff --git a/src/modules/list-toolbar/list-toolbar-item-renderer.component.ts b/src/modules/list-toolbar/list-toolbar-item-renderer.component.ts index b4a5f6a50..b045d88ff 100644 --- a/src/modules/list-toolbar/list-toolbar-item-renderer.component.ts +++ b/src/modules/list-toolbar/list-toolbar-item-renderer.component.ts @@ -2,8 +2,7 @@ import { Component, ViewContainerRef, ViewChild, Input, TemplateRef, OnInit} fro @Component({ selector: 'sky-list-toolbar-item-renderer', - template: '', - styles: [require('./list-toolbar-item-renderer.component.scss')] + template: '' }) export class SkyListToolbarItemRendererComponent implements OnInit { @Input() public template: TemplateRef; diff --git a/src/modules/list-toolbar/list-toolbar.component.html b/src/modules/list-toolbar/list-toolbar.component.html index 908a73e4d..ba0e8c625 100644 --- a/src/modules/list-toolbar/list-toolbar.component.html +++ b/src/modules/list-toolbar/list-toolbar.component.html @@ -1,5 +1,5 @@
- + ; + @Input() + public placeholder: string; + @Input() + public searchEnabled: boolean | Observable; /* tslint:disable */ - //@Input('searchText') private searchTextInput: string | Observable; + @Input('searchText') + private searchTextInput: string | Observable; /* tslint:enable */ @ContentChildren(SkyListToolbarItemComponent) @@ -70,14 +72,12 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { ); this.dispatcher.toolbarAddItems([ - this.type !== 'search' ? - new ListToolbarItemModel({ - id: 'search', - template: this.searchTemplate, - location: 'center' - }) : - undefined - ].filter(s => s !== undefined)); + new ListToolbarItemModel({ + id: 'search', + template: this.searchTemplate, + location: 'center' + }) + ]); } public ngAfterContentInit() { @@ -93,6 +93,10 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { return this.state.map(s => s.search.searchText).distinctUntilChanged(); } + get view() { + return this.state.map(s => s.views.active).distinctUntilChanged(); + } + get leftTemplates() { return Observable.combineLatest( this.state.map(s => s.toolbar).distinctUntilChanged(), diff --git a/src/modules/list-toolbar/list-toolbar.module.ts b/src/modules/list-toolbar/list-toolbar.module.ts index e017ffbf7..6a64580ae 100644 --- a/src/modules/list-toolbar/list-toolbar.module.ts +++ b/src/modules/list-toolbar/list-toolbar.module.ts @@ -4,14 +4,12 @@ import { SkyDropdownModule } from '../dropdown'; import { SkyListToolbarComponent } from './list-toolbar.component'; import { SkyListToolbarItemComponent } from './list-toolbar-item.component'; import { SkyListToolbarItemRendererComponent } from './list-toolbar-item-renderer.component'; -import { SkyListToolbarSortComponent } from './list-toolbar-sort.component'; @NgModule({ declarations: [ SkyListToolbarComponent, SkyListToolbarItemComponent, - SkyListToolbarItemRendererComponent, - SkyListToolbarSortComponent + SkyListToolbarItemRendererComponent ], imports: [ CommonModule, @@ -20,8 +18,7 @@ import { SkyListToolbarSortComponent } from './list-toolbar-sort.component'; exports: [ SkyListToolbarComponent, SkyListToolbarItemComponent, - SkyListToolbarItemRendererComponent, - SkyListToolbarSortComponent + SkyListToolbarItemRendererComponent ], providers: [ ] diff --git a/src/modules/list-toolbar/state/config/actions.ts b/src/modules/list-toolbar/state/config/actions.ts new file mode 100644 index 000000000..0d07de78b --- /dev/null +++ b/src/modules/list-toolbar/state/config/actions.ts @@ -0,0 +1 @@ +export { ListToolbarConfigSetSearchEnabledAction } from './set-search-enabled.action'; diff --git a/src/modules/list-toolbar/state/config/config.model.ts b/src/modules/list-toolbar/state/config/config.model.ts new file mode 100644 index 000000000..e48d0ad45 --- /dev/null +++ b/src/modules/list-toolbar/state/config/config.model.ts @@ -0,0 +1,9 @@ +export class ListToolbarConfigModel { + public searchEnabled: boolean = true; + + constructor(data?: any) { + if (data) { + this.searchEnabled = data.searchEnabled; + } + } +} diff --git a/src/modules/list-toolbar/state/config/config.orchestrator.ts b/src/modules/list-toolbar/state/config/config.orchestrator.ts new file mode 100644 index 000000000..5c51e2d20 --- /dev/null +++ b/src/modules/list-toolbar/state/config/config.orchestrator.ts @@ -0,0 +1,23 @@ +import { ListToolbarStateOrchestrator } from '../toolbar-state.rxstate'; +import { ListToolbarConfigModel } from './config.model'; + +import { + ListToolbarConfigSetSearchEnabledAction +} from './actions'; + +export class ListToolbarConfigOrchestrator + extends ListToolbarStateOrchestrator { + constructor() { + super(); + + this + .register(ListToolbarConfigSetSearchEnabledAction, this.setSearchEnabled); + } + + private setSearchEnabled( + state: ListToolbarConfigModel, + action: ListToolbarConfigSetSearchEnabledAction): ListToolbarConfigModel { + return new ListToolbarConfigModel(Object.assign({}, state, { searchEnabled: action.enabled })); + } + +} diff --git a/src/modules/list-toolbar/state/config/set-search-enabled.action.ts b/src/modules/list-toolbar/state/config/set-search-enabled.action.ts new file mode 100644 index 000000000..7305756d2 --- /dev/null +++ b/src/modules/list-toolbar/state/config/set-search-enabled.action.ts @@ -0,0 +1,3 @@ +export class ListToolbarConfigSetSearchEnabledAction { + constructor(public enabled: true) {} +} diff --git a/src/modules/list-toolbar/state/index.ts b/src/modules/list-toolbar/state/index.ts new file mode 100644 index 000000000..7a6997c47 --- /dev/null +++ b/src/modules/list-toolbar/state/index.ts @@ -0,0 +1,3 @@ +export { ListToolbarState } from './toolbar-state.state-node'; +export { ListToolbarStateDispatcher } from './toolbar-state.rxstate'; +export { ListToolbarStateModel } from './toolbar-state.model'; diff --git a/src/modules/list-toolbar/state/toolbar-state-action.type.ts b/src/modules/list-toolbar/state/toolbar-state-action.type.ts new file mode 100644 index 000000000..af5bdf48c --- /dev/null +++ b/src/modules/list-toolbar/state/toolbar-state-action.type.ts @@ -0,0 +1,6 @@ +import { + ListToolbarConfigSetSearchEnabledAction +} from './config/actions'; + +export type ListToolbarStateAction = + ListToolbarConfigSetSearchEnabledAction; diff --git a/src/modules/list-toolbar/state/toolbar-state.model.ts b/src/modules/list-toolbar/state/toolbar-state.model.ts new file mode 100644 index 000000000..c16e84c5f --- /dev/null +++ b/src/modules/list-toolbar/state/toolbar-state.model.ts @@ -0,0 +1,5 @@ +import { ListToolbarConfigModel } from './config/config.model'; + +export class ListToolbarStateModel { + public config: ListToolbarConfigModel = new ListToolbarConfigModel(); +} diff --git a/src/modules/list-toolbar/state/toolbar-state.rxstate.ts b/src/modules/list-toolbar/state/toolbar-state.rxstate.ts new file mode 100644 index 000000000..233b3b8d3 --- /dev/null +++ b/src/modules/list-toolbar/state/toolbar-state.rxstate.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@angular/core'; +import { StateDispatcher, StateOrchestrator } from 'microedge-rxstate/dist'; +import { ListToolbarStateAction } from './toolbar-state-action.type'; + +@Injectable() +export class ListToolbarStateDispatcher extends StateDispatcher { +} + +export class ListToolbarStateOrchestrator extends StateOrchestrator { +} diff --git a/src/modules/list-toolbar/state/toolbar-state.state-node.ts b/src/modules/list-toolbar/state/toolbar-state.state-node.ts new file mode 100644 index 000000000..624450728 --- /dev/null +++ b/src/modules/list-toolbar/state/toolbar-state.state-node.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { StateNode } from 'microedge-rxstate/dist'; +import { ListToolbarStateModel } from './toolbar-state.model'; +import { ListToolbarStateDispatcher } from './toolbar-state.rxstate'; +import { ListToolbarConfigOrchestrator } from './config/config.orchestrator'; + +@Injectable() +export class ListToolbarState extends StateNode { + constructor(initialState: ListToolbarStateModel, dispatcher: ListToolbarStateDispatcher) { + super(initialState, dispatcher); + + this + .register('config', ListToolbarConfigOrchestrator) + .begin(); + } +} diff --git a/src/modules/list/list-data-request.model.ts b/src/modules/list/list-data-request.model.ts index 63c9bfeea..d24ff3c31 100644 --- a/src/modules/list/list-data-request.model.ts +++ b/src/modules/list/list-data-request.model.ts @@ -1,11 +1,15 @@ +import { ListSearchModel } from './state/search/search.model'; + export class ListDataRequestModel { public pageSize: number = 10; public pageNumber: number = 1; + public search: ListSearchModel; constructor(data?: any) { if (data !== undefined) { this.pageSize = data.pageSize; this.pageNumber = data.pageNumber; + this.search = data.search; } } } diff --git a/src/modules/list/list.component.ts b/src/modules/list/list.component.ts index 964675764..f1fb2fd68 100644 --- a/src/modules/list/list.component.ts +++ b/src/modules/list/list.component.ts @@ -13,6 +13,8 @@ import { ListState, ListStateDispatcher } from './state'; import { Observable } from 'rxjs/Observable'; import { ListViewComponent } from './list-view.component'; +import { ListSearchModel } from './state/search/search.model'; + import { ListViewsLoadAction, ListViewsSetActiveAction @@ -48,6 +50,9 @@ export class SkyListComponent implements AfterContentInit { @Input() public sortFields?: string | Array | Observable> | Observable; + /* tslint:disable-next-line */ + @Input('search') private searchFunction: (data: any, searchText: string) => boolean; + private dataFirstLoad: boolean = false; @ContentChildren(ListViewComponent) @@ -101,14 +106,16 @@ export class SkyListComponent implements AfterContentInit { } if (!this.dataProvider) { - this.dataProvider = new SkyListInMemoryDataProvider(data); + this.dataProvider = new SkyListInMemoryDataProvider(data, this.searchFunction); } return Observable.combineLatest( + this.state.map(s => s.search).distinctUntilChanged(), this.state.map(s => s.paging.itemsPerPage).distinctUntilChanged(), this.state.map(s => s.paging.pageNumber).distinctUntilChanged(), data.distinctUntilChanged(), ( + search: ListSearchModel, itemsPerPage: number, pageNumber: number, itemsData: Array @@ -126,7 +133,8 @@ export class SkyListComponent implements AfterContentInit { } else { response = this.dataProvider.get(new ListDataRequestModel({ pageSize: itemsPerPage, - pageNumber: pageNumber + pageNumber: pageNumber, + search: search })); } diff --git a/src/modules/list/state/index.ts b/src/modules/list/state/index.ts index 2ffccd356..bfaff9ba8 100644 --- a/src/modules/list/state/index.ts +++ b/src/modules/list/state/index.ts @@ -5,3 +5,9 @@ export * from './items/actions'; export * from './items/item.model'; export * from './paging/actions'; export * from './paging/paging.model'; +export * from './toolbar/actions'; +export * from './toolbar/toolbar.model'; +export * from './toolbar/toolbar-item.model'; +export * from './search/actions'; +export * from './search/search.model'; + diff --git a/src/modules/list/state/list-state-action.type.ts b/src/modules/list/state/list-state-action.type.ts index e073e2bdf..40f55b157 100644 --- a/src/modules/list/state/list-state-action.type.ts +++ b/src/modules/list/state/list-state-action.type.ts @@ -13,7 +13,20 @@ import { ListViewsSetActiveAction } from './views/actions'; +import { + ListToolbarItemsLoadAction, + ListToolbarSetExistsAction +} from './toolbar/actions'; + +import { + ListSearchSetSearchTextAction, + ListSearchSetFunctionsAction, + ListSearchSetFieldSelectorsAction +} from './search/actions'; + export type ListStateAction = ListItemsSetLoadingAction | ListItemsLoadAction | ListPagingSetMaxPagesAction | ListPagingSetItemsPerPageAction | ListPagingSetPageNumberAction | - ListViewsLoadAction | ListViewsSetActiveAction; + ListViewsLoadAction | ListViewsSetActiveAction | ListToolbarItemsLoadAction | + ListToolbarSetExistsAction | ListSearchSetSearchTextAction | ListSearchSetFunctionsAction | + ListSearchSetFieldSelectorsAction; diff --git a/src/modules/list/state/list-state.model.ts b/src/modules/list/state/list-state.model.ts index e6adcc2fb..36e1b79e5 100644 --- a/src/modules/list/state/list-state.model.ts +++ b/src/modules/list/state/list-state.model.ts @@ -2,9 +2,14 @@ import { AsyncList } from 'microedge-rxstate/dist'; import { ListPagingModel } from './paging/paging.model'; import { ListItemModel } from './items/item.model'; import { ListViewsModel } from './views/views.model'; +import { ListToolbarModel } from './toolbar/toolbar.model'; +import { ListSearchModel } from './search/search.model'; export class ListStateModel { public paging: ListPagingModel = new ListPagingModel(); public items: AsyncList = new AsyncList(); public views: ListViewsModel = new ListViewsModel(); + public toolbar: ListToolbarModel = new ListToolbarModel(); + public search: ListSearchModel = new ListSearchModel(); + } diff --git a/src/modules/list/state/list-state.rxstate.ts b/src/modules/list/state/list-state.rxstate.ts index c73b24d6a..e5dc4c9a9 100644 --- a/src/modules/list/state/list-state.rxstate.ts +++ b/src/modules/list/state/list-state.rxstate.ts @@ -1,9 +1,25 @@ import { Injectable } from '@angular/core'; -import { StateDispatcher, StateOrchestrator } from 'microedge-rxstate/dist'; +import { + StateDispatcher, + StateOrchestrator +} from 'microedge-rxstate/dist'; import { ListStateAction } from './list-state-action.type'; import { ListViewsSetActiveAction } from './views/actions'; +import { + ListToolbarItemsLoadAction, + ListToolbarSetExistsAction +} from './toolbar/actions'; + +import { ListToolbarItemModel } from './toolbar/toolbar-item.model'; + +import { + ListSearchSetFunctionsAction, + ListSearchSetSearchTextAction, + ListSearchSetFieldSelectorsAction +} from './search/actions'; + export class ListStateOrchestrator extends StateOrchestrator { } @@ -13,4 +29,24 @@ export class ListStateDispatcher extends StateDispatcher { public viewsSetActive(id: string) { this.next(new ListViewsSetActiveAction(id)); } + + public toolbarExists(exists: boolean): void { + this.next(new ListToolbarSetExistsAction(exists)); + } + + public toolbarAddItems(items: ListToolbarItemModel[], index: number = -1): void { + setTimeout(() => this.next(new ListToolbarItemsLoadAction(items, index))); + } + + public searchSetFunctions(sortFunctions: ((data: any, searchText: string) => boolean)[]): void { + this.next(new ListSearchSetFunctionsAction(sortFunctions)); + } + + public searchSetFieldSelectors(fieldSelectors: Array): void { + this.next(new ListSearchSetFieldSelectorsAction(fieldSelectors)); + } + + public searchSetText(searchText: string) { + this.next(new ListSearchSetSearchTextAction(searchText)); + } } diff --git a/src/modules/list/state/list-state.state-node.ts b/src/modules/list/state/list-state.state-node.ts index 2b0e4c8fc..38f5857cd 100644 --- a/src/modules/list/state/list-state.state-node.ts +++ b/src/modules/list/state/list-state.state-node.ts @@ -7,6 +7,8 @@ import { ListStateDispatcher } from './list-state.rxstate'; import { ListItemsOrchestrator } from './items/items.orchestrator'; import { ListPagingOrchestrator } from './paging/paging.orchestrator'; import { ListViewsOrchestrator } from './views/views.orchestrator'; +import { ListToolbarOrchestrator } from './toolbar/toolbar.orchestrator'; +import { ListSearchOrchestrator } from './search/search.orchestrator'; @Injectable() export class ListState extends StateNode { @@ -16,6 +18,8 @@ export class ListState extends StateNode { this .register('items', ListItemsOrchestrator) .register('paging', ListPagingOrchestrator) + .register('search', ListSearchOrchestrator) + .register('toolbar', ListToolbarOrchestrator) .register('views', ListViewsOrchestrator) .begin(); } diff --git a/src/modules/list/state/search/actions.ts b/src/modules/list/state/search/actions.ts new file mode 100644 index 000000000..526d7fd54 --- /dev/null +++ b/src/modules/list/state/search/actions.ts @@ -0,0 +1,3 @@ +export { ListSearchSetSearchTextAction } from './set-search-text.action'; +export { ListSearchSetFunctionsAction } from './set-functions.action'; +export { ListSearchSetFieldSelectorsAction } from './set-field-selectors.action'; diff --git a/src/modules/list/state/search/search.model.ts b/src/modules/list/state/search/search.model.ts new file mode 100644 index 000000000..e30fb3dfd --- /dev/null +++ b/src/modules/list/state/search/search.model.ts @@ -0,0 +1,13 @@ +export class ListSearchModel { + public searchText: string = ''; + public functions: Array<(data: any, searchText: string) => boolean> = []; + public fieldSelectors: Array = []; + + constructor(data?: any) { + if (data) { + this.searchText = data.searchText; + this.functions = [...data.functions]; + this.fieldSelectors = data.fieldSelectors; + } + } +} diff --git a/src/modules/list/state/search/search.orchestrator.ts b/src/modules/list/state/search/search.orchestrator.ts new file mode 100644 index 000000000..1b3a4d9aa --- /dev/null +++ b/src/modules/list/state/search/search.orchestrator.ts @@ -0,0 +1,38 @@ +import { ListStateOrchestrator } from '../list-state.rxstate'; +import { ListSearchModel } from './search.model'; +import { ListSearchSetSearchTextAction } from './set-search-text.action'; +import { ListSearchSetFunctionsAction } from './set-functions.action'; +import { ListSearchSetFieldSelectorsAction } from './set-field-selectors.action'; + +export class ListSearchOrchestrator extends ListStateOrchestrator { + constructor() { + super(); + + this + .register(ListSearchSetSearchTextAction, this.setSearchText) + .register(ListSearchSetFunctionsAction, this.setFunctions) + .register(ListSearchSetFieldSelectorsAction, this.setFieldSelectors); + } + + private setSearchText( + state: ListSearchModel, + action: ListSearchSetSearchTextAction): ListSearchModel { + return new ListSearchModel( + Object.assign({}, state, { searchText: action.searchText ? action.searchText : '' }) + ); + } + + private setFunctions( + state: ListSearchModel, + action: ListSearchSetFunctionsAction): ListSearchModel { + return new ListSearchModel(Object.assign({}, state, { functions: [...action.functions] })); + } + + private setFieldSelectors( + state: ListSearchModel, + action: ListSearchSetFieldSelectorsAction): ListSearchModel { + return new ListSearchModel( + Object.assign({}, state, { fieldSelectors: [...action.fieldSelectors] }) + ); + } +} diff --git a/src/modules/list/state/search/set-field-selectors.action.ts b/src/modules/list/state/search/set-field-selectors.action.ts new file mode 100644 index 000000000..5d7593bc6 --- /dev/null +++ b/src/modules/list/state/search/set-field-selectors.action.ts @@ -0,0 +1,3 @@ +export class ListSearchSetFieldSelectorsAction { + constructor(public fieldSelectors: Array = []) {} +} diff --git a/src/modules/list/state/search/set-functions.action.ts b/src/modules/list/state/search/set-functions.action.ts new file mode 100644 index 000000000..61ff7b403 --- /dev/null +++ b/src/modules/list/state/search/set-functions.action.ts @@ -0,0 +1,3 @@ +export class ListSearchSetFunctionsAction { + constructor(public functions: Array<(data: any, searchText: string) => boolean> = []) {} +} diff --git a/src/modules/list/state/search/set-search-text.action.ts b/src/modules/list/state/search/set-search-text.action.ts new file mode 100644 index 000000000..d16aff9a7 --- /dev/null +++ b/src/modules/list/state/search/set-search-text.action.ts @@ -0,0 +1,3 @@ +export class ListSearchSetSearchTextAction { + constructor(public searchText: string) {} +} diff --git a/src/modules/list/state/toolbar/actions.ts b/src/modules/list/state/toolbar/actions.ts new file mode 100644 index 000000000..e6fd9b5ab --- /dev/null +++ b/src/modules/list/state/toolbar/actions.ts @@ -0,0 +1,2 @@ +export { ListToolbarItemsLoadAction } from './load.action'; +export { ListToolbarSetExistsAction } from './set-exists.action'; diff --git a/src/modules/list/state/toolbar/load.action.ts b/src/modules/list/state/toolbar/load.action.ts new file mode 100644 index 000000000..b00940bdb --- /dev/null +++ b/src/modules/list/state/toolbar/load.action.ts @@ -0,0 +1,7 @@ +import { ListToolbarItemModel } from './toolbar-item.model'; + +export class ListToolbarItemsLoadAction { + constructor(public items: ListToolbarItemModel[], public index: number = -1) { + + } +} diff --git a/src/modules/list/state/toolbar/set-exists.action.ts b/src/modules/list/state/toolbar/set-exists.action.ts new file mode 100644 index 000000000..acea09a2c --- /dev/null +++ b/src/modules/list/state/toolbar/set-exists.action.ts @@ -0,0 +1,3 @@ +export class ListToolbarSetExistsAction { + constructor(public exists: boolean) {} +} diff --git a/src/modules/list/state/toolbar/toolbar-item.model.ts b/src/modules/list/state/toolbar/toolbar-item.model.ts new file mode 100644 index 000000000..c8037c5e7 --- /dev/null +++ b/src/modules/list/state/toolbar/toolbar-item.model.ts @@ -0,0 +1,18 @@ +import { TemplateRef } from '@angular/core'; + +export class ListToolbarItemModel { + public template: TemplateRef; + public location: string; + public view: string; + + public id: string; + + constructor(data?: any) { + if (data) { + this.template = data.template; + this.location = data.location; + this.view = data.view; + this.id = data.id; + } + } +} diff --git a/src/modules/list/state/toolbar/toolbar.model.ts b/src/modules/list/state/toolbar/toolbar.model.ts new file mode 100644 index 000000000..a568a4efe --- /dev/null +++ b/src/modules/list/state/toolbar/toolbar.model.ts @@ -0,0 +1,13 @@ +import { ListToolbarItemModel } from './toolbar-item.model'; + +export class ListToolbarModel { + public exists: boolean; + public items: ListToolbarItemModel[] = []; + + constructor(data?: any) { + if (data) { + this.exists = data.exists; + this.items = data.items; + } + } +} diff --git a/src/modules/list/state/toolbar/toolbar.orchestrator.ts b/src/modules/list/state/toolbar/toolbar.orchestrator.ts new file mode 100644 index 000000000..838e68c1a --- /dev/null +++ b/src/modules/list/state/toolbar/toolbar.orchestrator.ts @@ -0,0 +1,48 @@ +import { ListStateOrchestrator } from '../list-state.rxstate'; +import { ListToolbarModel } from './toolbar.model'; +import { ListToolbarItemModel } from './toolbar-item.model'; +import { + ListToolbarItemsLoadAction, + ListToolbarSetExistsAction +} from './actions'; + +export class ListToolbarOrchestrator + extends ListStateOrchestrator { + + constructor() { + super(); + + this + .register(ListToolbarSetExistsAction, this.setExists) + .register(ListToolbarItemsLoadAction, this.load); + } + + private setExists( + state: ListToolbarModel, + action: ListToolbarSetExistsAction + ): ListToolbarModel { + const newModel = new ListToolbarModel(state); + newModel.exists = action.exists; + return newModel; + } + + private load( + state: ListToolbarModel, + action: ListToolbarItemsLoadAction + ): ListToolbarModel { + const newModel = new ListToolbarModel(state); + const newListItems = action.items.map(item => new ListToolbarItemModel(item)); + + let resultItems = [...state.items]; + if (action.index === -1 || action.index > state.items.length) { + resultItems = [...resultItems, ...newListItems]; + } else if (action.index === 0) { + resultItems = [...newListItems, ...resultItems]; + } else { + newListItems.reverse().forEach(item => resultItems.splice(action.index, 0, item)); + } + + newModel.items = resultItems; + return newModel; + } +} From ddd7e2d9cefee1be6cf3867b49c13fa1617aeabe Mon Sep 17 00:00:00 2001 From: Blackbaud-PatrickOFriel Date: Thu, 2 Feb 2017 15:52:14 -0500 Subject: [PATCH 05/14] Get list toolbar with grid and search working --- src/app/components/demo-components.service.ts | 32 ++++++++ src/app/components/list-toolbar/index.html | 80 +++++++++++++++++++ .../list-toolbar-demo-custom.component.html | 18 +++++ .../list-toolbar-demo-custom.component.ts | 18 +++++ .../list-toolbar-demo.component.html | 8 ++ .../list-toolbar-demo.component.ts | 18 +++++ src/core.ts | 3 + src/modules/grid/grid-column.component.ts | 15 +++- src/modules/grid/grid-column.model.ts | 2 + src/modules/grid/grid.component.html | 4 +- src/modules/grid/grid.component.scss | 6 ++ src/modules/grid/grid.component.ts | 3 + .../list-toolbar/list-toolbar.component.ts | 3 - .../list-toolbar/list-toolbar.module.ts | 6 +- .../list-view-grid.component.html | 1 + .../list-view-grid.component.ts | 30 ++++++- src/modules/list/list-view.component.ts | 21 ++++- src/modules/list/state/index.ts | 1 - src/modules/search/search.component.ts | 7 +- 19 files changed, 263 insertions(+), 13 deletions(-) create mode 100644 src/app/components/list-toolbar/index.html create mode 100644 src/app/components/list-toolbar/list-toolbar-demo-custom.component.html create mode 100644 src/app/components/list-toolbar/list-toolbar-demo-custom.component.ts create mode 100644 src/app/components/list-toolbar/list-toolbar-demo.component.html create mode 100644 src/app/components/list-toolbar/list-toolbar-demo.component.ts diff --git a/src/app/components/demo-components.service.ts b/src/app/components/demo-components.service.ts index 9de14b70a..2a31d0371 100644 --- a/src/app/components/demo-components.service.ts +++ b/src/app/components/demo-components.service.ts @@ -260,6 +260,38 @@ export class SkyDemoComponentsService { ]; } }, + { + name: 'List toolbar', + icon: 'wrench', + // tslint:disable-next-line + summary: + `The list toolbar component displays a SKY UX-themed toolbar for the list component.`, + url: '/components/list-toolbar', + getCodeFiles: function () { + return [ + { + name: 'list-toolbar-demo.component.html', + fileContents: require('!!raw!./list-toolbar/list-toolbar-demo.component.html') + }, + { + name: 'list-toolbar-demo.component.ts', + fileContents: require('!!raw!./list-toolbar/list-toolbar-demo.component.ts'), + componentName: 'SkyListToolbarDemoComponent', + bootstrapSelector: 'sky-list-toolbar-demo' + }, + { + name: 'list-toolbar-demo-custom.component.html', + fileContents: require('!!raw!./list-toolbar/list-toolbar-demo-custom.component.html') + }, + { + name: 'list-toolbar-demo-custom.component.ts', + fileContents: require('!!raw!./list-toolbar/list-toolbar-demo-custom.component.ts'), + componentName: 'SkyListToolbarDemoCustomComponent', + bootstrapSelector: 'sky-list-toolbar-demo-custom' + } + ]; + } + }, { name: 'List view grid', icon: 'table', diff --git a/src/app/components/list-toolbar/index.html b/src/app/components/list-toolbar/index.html new file mode 100644 index 000000000..174369ef3 --- /dev/null +++ b/src/app/components/list-toolbar/index.html @@ -0,0 +1,80 @@ + + + The list toolbar component displays a SKY UX-themed toolbar for the list component. + + + + + Specific the placeholder text for the search bar. + Acceptable values: string. + + + Specific whether the search bar is enabled. + Acceptable values: boolean or Observable boolean. + + + + Specific a text string to search with. + Acceptable values: string or Observable string. + + + + + + This list toolbar item component defines a given toolbar item. It is used in conjunction with the + list toolbar component. + + + + + + Specifies the id of the item. + Acceptable values: string. + + + Specifies the index of the item at the given item location. + Acceptable values: number. + + + Specifies the toolbar location of the item. + Acceptable values: 'left', 'center', or 'right'. + + + + +

List with standard toolbar

+ + +

List with toolbar with custom items

+ + + +
+ +
diff --git a/src/app/components/list-toolbar/list-toolbar-demo-custom.component.html b/src/app/components/list-toolbar/list-toolbar-demo-custom.component.html new file mode 100644 index 000000000..3bccfd294 --- /dev/null +++ b/src/app/components/list-toolbar/list-toolbar-demo-custom.component.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/app/components/list-toolbar/list-toolbar-demo-custom.component.ts b/src/app/components/list-toolbar/list-toolbar-demo-custom.component.ts new file mode 100644 index 000000000..a032b580c --- /dev/null +++ b/src/app/components/list-toolbar/list-toolbar-demo-custom.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'sky-list-toolbar-demo-custom', + templateUrl: './list-toolbar-demo-custom.component.html' +}) +export class SkyListToolbarDemoCustomComponent { + public items: Observable = Observable.of([ + { id: '1', column1: 101, column2: 'Apple', column3: 'Anne eats apples' }, + { id: '2', column1: 202, column2: 'Banana', column3: 'Ben eats bananas' }, + { id: '3', column1: 303, column2: 'Pear', column3: 'Patty eats pears' }, + { id: '4', column1: 404, column2: 'Grape', column3: 'George eats grapes' }, + { id: '5', column1: 505, column2: 'Banana', column3: 'Becky eats bananas' }, + { id: '6', column1: 606, column2: 'Lemon', column3: 'Larry eats lemons' }, + { id: '7', column1: 707, column2: 'Strawberry', column3: 'Sally eats strawberries' } + ]); +} diff --git a/src/app/components/list-toolbar/list-toolbar-demo.component.html b/src/app/components/list-toolbar/list-toolbar-demo.component.html new file mode 100644 index 000000000..c58f4b998 --- /dev/null +++ b/src/app/components/list-toolbar/list-toolbar-demo.component.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/app/components/list-toolbar/list-toolbar-demo.component.ts b/src/app/components/list-toolbar/list-toolbar-demo.component.ts new file mode 100644 index 000000000..f9e284d85 --- /dev/null +++ b/src/app/components/list-toolbar/list-toolbar-demo.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'sky-list-toolbar-demo', + templateUrl: './list-toolbar-demo.component.html' +}) +export class SkyListToolbarDemoComponent { + public items: Observable = Observable.of([ + { id: '1', column1: 101, column2: 'Apple', column3: 'Anne eats apples' }, + { id: '2', column1: 202, column2: 'Banana', column3: 'Ben eats bananas' }, + { id: '3', column1: 303, column2: 'Pear', column3: 'Patty eats pears' }, + { id: '4', column1: 404, column2: 'Grape', column3: 'George eats grapes' }, + { id: '5', column1: 505, column2: 'Banana', column3: 'Becky eats bananas' }, + { id: '6', column1: 606, column2: 'Lemon', column3: 'Larry eats lemons' }, + { id: '7', column1: 707, column2: 'Strawberry', column3: 'Sally eats strawberries' } + ]); +} diff --git a/src/core.ts b/src/core.ts index b84ccd5ba..5ee7c4ab9 100644 --- a/src/core.ts +++ b/src/core.ts @@ -16,6 +16,7 @@ import { SkyKeyInfoModule } from './modules/key-info'; import { SkyLabelModule } from './modules/label'; import { SkyListModule } from './modules/list'; import { SkyListPagingModule } from './modules/list-paging'; +import { SkyListToolbarModule } from './modules/list-toolbar'; import { SkyListViewGridModule } from './modules/list-view-grid'; import { SkyMediaQueryModule } from './modules/media-queries'; import { SkyModalModule } from './modules/modal'; @@ -46,6 +47,7 @@ import { SkyWaitModule } from './modules/wait'; SkyLabelModule, SkyListModule, SkyListPagingModule, + SkyListToolbarModule, SkyListViewGridModule, SkyMediaQueryModule, SkyModalModule, @@ -78,6 +80,7 @@ export * from './modules/label'; export * from './modules/list'; export * from './modules/list/state'; export * from './modules/list-paging'; +export * from './modules/list-toolbar'; export * from './modules/list-view-grid'; export * from './modules/modal'; export * from './modules/media-queries'; diff --git a/src/modules/grid/grid-column.component.ts b/src/modules/grid/grid-column.component.ts index cc767944f..de4ed45c4 100644 --- a/src/modules/grid/grid-column.component.ts +++ b/src/modules/grid/grid-column.component.ts @@ -29,6 +29,9 @@ export class SkyGridColumnComponent { public type: string; /* tslint:disable */ + @Input('search') + private searchFunction: (value: any, searchText: string) => boolean = this.search; + @Input('template') private templateInput: TemplateRef; /* tslint:enable */ @@ -38,5 +41,15 @@ export class SkyGridColumnComponent { public get template(): TemplateRef { return (this.templates.length > 0 ? this.templates.first : undefined) || this.templateInput; - }; + } + + private search(value: any, searchText: string): boolean { + /* tslint:disable */ + if (value !== undefined && value !== null) { + return value.toString().toLowerCase().indexOf(searchText) !== -1; + } + /* tslint:enable */ + + return false; + } } diff --git a/src/modules/grid/grid-column.model.ts b/src/modules/grid/grid-column.model.ts index 04702207f..1be6bd751 100644 --- a/src/modules/grid/grid-column.model.ts +++ b/src/modules/grid/grid-column.model.ts @@ -9,6 +9,7 @@ export class SkyGridColumnModel { public width: number; public hidden: boolean; public locked: boolean; + public searchFunction: (data: any, searchText: string) => boolean; constructor(template: TemplateRef, data?: any) { this.template = template; @@ -21,6 +22,7 @@ export class SkyGridColumnModel { this.width = data.width ? Number(data.width) : undefined; this.hidden = data.hidden; this.locked = data.locked; + this.searchFunction = data.searchFunction; } } } diff --git a/src/modules/grid/grid.component.html b/src/modules/grid/grid.component.html index f3b2a1ce4..648e52dd7 100644 --- a/src/modules/grid/grid.component.html +++ b/src/modules/grid/grid.component.html @@ -1,6 +1,8 @@
- +
; + @Input() + public hasToolbar: boolean = false; + @Output() public selectedColumnIdsChange = new EventEmitter>(); diff --git a/src/modules/list-toolbar/list-toolbar.component.ts b/src/modules/list-toolbar/list-toolbar.component.ts index 4428fa9e0..1d4ec2d1d 100644 --- a/src/modules/list-toolbar/list-toolbar.component.ts +++ b/src/modules/list-toolbar/list-toolbar.component.ts @@ -27,7 +27,6 @@ import { getValue } from 'microedge-rxstate/dist/helpers'; @Component({ selector: 'sky-list-toolbar', templateUrl: './list-toolbar.component.html', - styleUrls: ['./list-toolbar.component.scss'], providers: [ ListToolbarState, ListToolbarStateDispatcher, @@ -131,7 +130,6 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { }); } - private updateSearchText(searchText: string) { this.dispatcher.searchSetText(searchText); } @@ -142,4 +140,3 @@ export class SkyListToolbarComponent implements OnInit, AfterContentInit { .map(c => c.searchEnabled); } } - diff --git a/src/modules/list-toolbar/list-toolbar.module.ts b/src/modules/list-toolbar/list-toolbar.module.ts index 6a64580ae..0ce17ebf2 100644 --- a/src/modules/list-toolbar/list-toolbar.module.ts +++ b/src/modules/list-toolbar/list-toolbar.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SkyDropdownModule } from '../dropdown'; +import { SkyToolbarModule } from '../toolbar'; +import { SkySearchModule } from '../search'; import { SkyListToolbarComponent } from './list-toolbar.component'; import { SkyListToolbarItemComponent } from './list-toolbar-item.component'; import { SkyListToolbarItemRendererComponent } from './list-toolbar-item-renderer.component'; @@ -13,7 +14,8 @@ import { SkyListToolbarItemRendererComponent } from './list-toolbar-item-rendere ], imports: [ CommonModule, - SkyDropdownModule + SkyToolbarModule, + SkySearchModule ], exports: [ SkyListToolbarComponent, diff --git a/src/modules/list-view-grid/list-view-grid.component.html b/src/modules/list-view-grid/list-view-grid.component.html index 549fa6ce9..3b7eabdde 100644 --- a/src/modules/list-view-grid/list-view-grid.component.html +++ b/src/modules/list-view-grid/list-view-grid.component.html @@ -7,6 +7,7 @@ [data]="items | async" [selectedColumnIds]="selectedColumnIds | async" [columns]="columns | async" + [hasToolbar]="hasToolbar | async" (selectedColumnIdsChange)="columnIdsChanged($event)"> diff --git a/src/modules/list-view-grid/list-view-grid.component.ts b/src/modules/list-view-grid/list-view-grid.component.ts index 29dda7f04..baa1317c8 100644 --- a/src/modules/list-view-grid/list-view-grid.component.ts +++ b/src/modules/list-view-grid/list-view-grid.component.ts @@ -10,7 +10,11 @@ import { } from '@angular/core'; import { ListViewComponent } from '../list/list-view.component'; import { ListState } from '../list/state'; -import { GridState, GridStateDispatcher, GridStateModel } from './state'; +import { + GridState, + GridStateDispatcher, + GridStateModel +} from './state'; import { ListStateDispatcher } from '../list/state'; import { ListViewGridColumnsLoadAction } from './state/columns/actions'; import { ListViewDisplayedGridColumnsLoadAction } from './state/displayed-columns/actions'; @@ -24,6 +28,7 @@ import { import { ListItemModel } from '../list/state'; +import { getData } from '../list/helpers'; @Component({ selector: 'sky-list-view-grid', @@ -64,6 +69,11 @@ export class SkyListViewGridComponent @ViewChild(SkyGridComponent) public gridComponent: SkyGridComponent; + /* tslint:disable */ + @Input('search') + private searchFunction: (data: any, searchText: string) => boolean; + /* tslint:enable */ + @ContentChildren(SkyGridColumnComponent, {descendants: true}) private columnComponents: QueryList; @@ -142,6 +152,24 @@ export class SkyListViewGridComponent }); } + public onViewActive() { + let sub = this.gridState.map(s => s.displayedColumns.items) + .distinctUntilChanged() + .subscribe(displayedColumns => { + let setFunctions = + this.searchFunction !== undefined ? [this.searchFunction] : + displayedColumns + .map(column => (data: any, searchText: string) => + column.searchFunction(getData(data, column.field), searchText) + ) + .filter(c => c !== undefined); + + this.dispatcher.searchSetFieldSelectors(displayedColumns.map(d => d.field)); + this.dispatcher.searchSetFunctions(setFunctions); + }); + this.subscriptions.push(sub); + } + get items(): Observable { return Observable.combineLatest( this.state.map(s => s.items.items).distinctUntilChanged(), diff --git a/src/modules/list/list-view.component.ts b/src/modules/list/list-view.component.ts index 5ed0ba7d2..dbbc682c6 100644 --- a/src/modules/list/list-view.component.ts +++ b/src/modules/list/list-view.component.ts @@ -1,13 +1,22 @@ +import { + OnDestroy +} from '@angular/core'; + import { ListState } from './state'; + import { SkyListComponent } from '../list/list.component'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { + BehaviorSubject, + Observable +} from 'rxjs'; let moment = require('moment'); -export abstract class ListViewComponent { +export abstract class ListViewComponent implements OnDestroy { protected viewName: string; protected state: ListState; protected list: SkyListComponent; + protected subscriptions: Array = []; /* tslint:disable */ private initialized: BehaviorSubject = new BehaviorSubject(false); /* tslint:enable */ @@ -29,6 +38,10 @@ export abstract class ListViewComponent { return this.viewName; } + get hasToolbar() { + return this.state.map(s => s.toolbar.exists); + } + get active(): Observable { return this.state.map(s => s.views.active === this.viewId); } @@ -39,4 +52,8 @@ export abstract class ListViewComponent { public onViewInactive() { } + + public ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()); + } } diff --git a/src/modules/list/state/index.ts b/src/modules/list/state/index.ts index bfaff9ba8..ca0730ec4 100644 --- a/src/modules/list/state/index.ts +++ b/src/modules/list/state/index.ts @@ -10,4 +10,3 @@ export * from './toolbar/toolbar.model'; export * from './toolbar/toolbar-item.model'; export * from './search/actions'; export * from './search/search.model'; - diff --git a/src/modules/search/search.component.ts b/src/modules/search/search.component.ts index 04438282c..8e0d1948d 100644 --- a/src/modules/search/search.component.ts +++ b/src/modules/search/search.component.ts @@ -13,7 +13,8 @@ import { Input, EventEmitter, OnChanges, - SimpleChanges + SimpleChanges, + ChangeDetectorRef } from '@angular/core'; import { @@ -94,7 +95,8 @@ export class SkySearchComponent implements OnDestroy, OnInit, OnChanges { private mediaQueryService: SkyMediaQueryService, private elRef: ElementRef, private searchAdapter: SkySearchAdapterService, - private resources: SkyResourcesService + private resources: SkyResourcesService, + private changeRef: ChangeDetectorRef ) {} public ngOnInit() { @@ -206,5 +208,6 @@ export class SkySearchComponent implements OnDestroy, OnInit, OnChanges { } else { this.mobileSearchShown = false; } + this.changeRef.markForCheck(); } } From 09348ebec1a67033f57000550b73aed1614ec9e3 Mon Sep 17 00:00:00 2001 From: Blackbaud-PatrickOFriel Date: Fri, 3 Feb 2017 10:49:57 -0500 Subject: [PATCH 06/14] update grid with hasToolbar and search tests --- .../grid/fixtures/grid.component.fixture.html | 4 +- .../grid/fixtures/grid.component.fixture.ts | 12 +++++ src/modules/grid/grid.component.spec.ts | 54 +++++++++++++++++++ src/modules/list/list.component.ts | 6 ++- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/modules/grid/fixtures/grid.component.fixture.html b/src/modules/grid/fixtures/grid.component.fixture.html index 703761997..322df3c23 100644 --- a/src/modules/grid/fixtures/grid.component.fixture.html +++ b/src/modules/grid/fixtures/grid.component.fixture.html @@ -2,6 +2,7 @@ @@ -13,7 +14,8 @@ + [width]="150" + [search]="searchFunction">