Skip to content

Commit

Permalink
feat: added dbxListItemIsSelectedModifier
Browse files Browse the repository at this point in the history
- added IsSelectedDecisionFunctionFactory
  • Loading branch information
dereekb committed Nov 25, 2022
1 parent c883a2c commit 7dea240
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,27 @@ <h4>DbxListGridSize</h4>

<p>Clicked: {{ clickedModifiedAnchorItem | json }}</p>
<div style="height: 200px">
<doc-custom-item-list [state$]="stateWithAnchors$" [dbxListItemModifier]="inputDbxListItemModifier"></doc-custom-item-list>
<doc-complex-item-list [state$]="stateWithAnchors$" [dbxListItemModifier]="inputDbxListItemModifier"></doc-complex-item-list>
</div>

<h4>dbxListItemAnchorModifier</h4>
<p>Used with dbxListItemModifier to set the anchor value on the list item using the itemValue of the list item.</p>
<p>Clicked: {{ clickedModifiedAnchorItem | json }}</p>
<div style="height: 200px">
<doc-custom-item-list [state$]="stateWithAnchors$" [dbxListItemModifier] [dbxListItemAnchorModifier]="makeClickAnchor"></doc-custom-item-list>
<doc-complex-item-list [state$]="stateWithAnchors$" [dbxListItemModifier] [dbxListItemAnchorModifier]="makeClickAnchor"></doc-complex-item-list>
</div>

<h4>dbxListItemSelectionModifier</h4>
<p>Used with dbxListItemModifier to change the selection of items. Is typically used in cases where items with a specific key or index are "selected".</p>
<p>This example sets all items to be selected.</p>
<div style="height: 200px">
<doc-complex-item-list [state$]="stateWithSelection$" [dbxListItemModifier] [dbxListItemIsSelectedModifier]="isSelectedModifierFunction"></doc-complex-item-list>
</div>
</doc-feature-example>

<doc-feature-example header="dbxListItemDisableRippleModifier" hint="Used to disable ripples conditionally on a per-item basis using a function.">
<div style="height: 200px">
<doc-custom-item-list [dbxListItemModifier] [dbxListItemDisableRippleModifier]="disableAllRipples" [state$]="stateWithAnchors$" [dbxListItemModifier]="inputDbxListItemModifier"></doc-custom-item-list>
<doc-complex-item-list [dbxListItemModifier] [dbxListItemDisableRippleModifier]="disableAllRipples" [state$]="stateWithAnchors$" [dbxListItemModifier]="inputDbxListItemModifier"></doc-complex-item-list>
</div>
</doc-feature-example>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ClickableAnchor, safeDetectChanges } from '@dereekb/dbx-core';
import { listItemModifier, ListItemModifier, ListSelectionState, AnchorForValueFunction, DbxValueListGridItemViewGridSizeConfig, DbxListSelectionMode } from '@dereekb/dbx-web';
import { listItemModifier, ListItemModifier, ListSelectionState, AnchorForValueFunction, DbxValueListGridItemViewGridSizeConfig, DbxListSelectionMode, DbxValueListItemDecisionFunction, dbxValueListItemDecisionFunction } from '@dereekb/dbx-web';
import { CustomDocValue } from './../component/item.list.custom.component';
import { ListLoadingState, mapLoadingStateResults, successResult } from '@dereekb/rxjs';
import { BehaviorSubject, map, switchMap, startWith, Observable, delay, of } from 'rxjs';
Expand Down Expand Up @@ -102,6 +102,10 @@ export class DocLayoutListComponent implements OnInit, OnDestroy {
};
};

readonly isSelectedModifierFunction: DbxValueListItemDecisionFunction<DocValue> = dbxValueListItemDecisionFunction((value: DocValue) => {
return true; // all are selected.
});

readonly customGridSize: Partial<DbxValueListGridItemViewGridSizeConfig> = {
columns: 'repeat(auto-fill, minmax(200px, 1fr))',
gap: '25px'
Expand Down
1 change: 1 addition & 0 deletions packages/dbx-web/src/lib/layout/list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './list.view.value.item.directive';
export * from './list.view.value.modifier';
export * from './list.view.value.modifier.directive';
export * from './list.view.value.modifier.ripple.directive';
export * from './list.view.value.modifier.selection.directive';
export * from './list.view.value.selection.component';
export * from './list.view.value';
export * from './list.view';
Expand Down
7 changes: 5 additions & 2 deletions packages/dbx-web/src/lib/layout/list/list.layout.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DbxValueListItemModifierDirective } from './list.view.value.modifier.di
import { DbxListItemDisableRippleModifierDirective } from './list.view.value.modifier.ripple.directive';
import { DbxValueListGridItemViewComponent, DbxValueListGridSizeDirective, DbxValueListGridViewComponent } from './list.grid.view.component';
import { FlexLayoutModule } from '@angular/flex-layout';
import { DbxListItemIsSelectedModifierDirective } from './list.view.value.modifier.selection.directive';

@NgModule({
imports: [CommonModule, MatRippleModule, FlexLayoutModule, DbxLoadingModule, DbxRouterAnchorModule, InfiniteScrollModule, DbxInjectionComponentModule, MatListModule, MatIconModule],
Expand All @@ -31,7 +32,8 @@ import { FlexLayoutModule } from '@angular/flex-layout';
DbxSelectionValueListViewComponent,
DbxSelectionValueListItemViewComponent,
DbxValueListItemModifierDirective,
DbxListItemDisableRippleModifierDirective
DbxListItemDisableRippleModifierDirective,
DbxListItemIsSelectedModifierDirective
],
exports: [
//
Expand All @@ -45,7 +47,8 @@ import { FlexLayoutModule } from '@angular/flex-layout';
DbxSelectionValueListViewComponent,
DbxSelectionValueListItemViewComponent,
DbxValueListItemModifierDirective,
DbxListItemDisableRippleModifierDirective
DbxListItemDisableRippleModifierDirective,
DbxListItemIsSelectedModifierDirective
]
})
export class DbxListLayoutModule {}
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { Directive, Input, OnDestroy } from '@angular/core';
import { Maybe, ArrayOrValue, Modifier } from '@dereekb/util';
import { Maybe, ArrayOrValue, Modifier, DecisionFunction } from '@dereekb/util';
import { BehaviorSubject, Observable, map } from 'rxjs';
import { DbxValueListItem } from './list.view.value';
import { DbxValueListItem, DbxValueListItemDecisionFunction } from './list.view.value';
import { AbstractDbxValueListItemModifierDirective } from './list.view.value.modifier.directive';

export type DisableRippleForValueFunction<T> = (item: DbxValueListItem<T>) => boolean;

export const DBX_LIST_ITEM_DISABLE_RIPPLE_LIST_ITEM_MODIFIER_KEY = 'disable_ripple_anchor';

export const DBX_LIST_ITEM_DEFAULT_DISABLE_FUNCTION: DisableRippleForValueFunction<unknown> = <T>(item: DbxValueListItem<T>) => {
export const DBX_LIST_ITEM_DEFAULT_DISABLE_FUNCTION: DbxValueListItemDecisionFunction<unknown> = <T>(item: DbxValueListItem<T>) => {
return item.rippleDisabled || !item.anchor || (!item.anchor.ref && !item.anchor.url && !item.anchor.onClick);
};

@Directive({
selector: '[dbxListItemDisableRippleModifier]'
})
export class DbxListItemDisableRippleModifierDirective<T> extends AbstractDbxValueListItemModifierDirective<T> implements OnDestroy {
private _disableRippleForItem = new BehaviorSubject<DisableRippleForValueFunction<T>>(DBX_LIST_ITEM_DEFAULT_DISABLE_FUNCTION);
private _disableRippleForItem = new BehaviorSubject<DbxValueListItemDecisionFunction<T>>(DBX_LIST_ITEM_DEFAULT_DISABLE_FUNCTION);

readonly modifiers$: Observable<Maybe<ArrayOrValue<Modifier<DbxValueListItem<T>>>>> = this._disableRippleForItem.pipe(
map((disableRippleForItem) => {
Expand All @@ -39,7 +37,13 @@ export class DbxListItemDisableRippleModifierDirective<T> extends AbstractDbxVal
}

@Input('dbxListItemDisableRippleModifier')
set disableRippleForItem(disableRippleForItem: Maybe<DisableRippleForValueFunction<T>>) {
set disableRippleForItem(disableRippleForItem: Maybe<DbxValueListItemDecisionFunction<T>>) {
this._disableRippleForItem.next(disableRippleForItem ?? DBX_LIST_ITEM_DEFAULT_DISABLE_FUNCTION);
}
}

// MARK: Compat
/**
* @Deprecated use DbxValueListItemDecisionFunction instead.
*/
export type DisableRippleForValueFunction<T> = DecisionFunction<DbxValueListItem<T>>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Directive, Input, OnDestroy } from '@angular/core';
import { Maybe, ArrayOrValue, Modifier, DecisionFunction } from '@dereekb/util';
import { BehaviorSubject, Observable, map } from 'rxjs';
import { DbxValueListItem, DbxValueListItemDecisionFunction } from './list.view.value';
import { AbstractDbxValueListItemModifierDirective } from './list.view.value.modifier.directive';

export const DBX_LIST_ITEM_IS_SELECTED_ITEM_MODIFIER_KEY = 'is_selected_item_modifier';

export const DEFAULT_DBX_LIST_ITEM_IS_SELECTED_FUNCTION: DbxValueListItemDecisionFunction<unknown> = <T>(item: DbxValueListItem<T>) => {
return item.selected ?? false;
};

@Directive({
selector: '[dbxListItemIsSelectedModifier]'
})
export class DbxListItemIsSelectedModifierDirective<T> extends AbstractDbxValueListItemModifierDirective<T> implements OnDestroy {
private _listItemIsSelected = new BehaviorSubject<DbxValueListItemDecisionFunction<T>>(DEFAULT_DBX_LIST_ITEM_IS_SELECTED_FUNCTION);

readonly modifiers$: Observable<Maybe<ArrayOrValue<Modifier<DbxValueListItem<T>>>>> = this._listItemIsSelected.pipe(
map((listItemIsSelected) => {
const modifiers: Modifier<DbxValueListItem<T>> = {
key: DBX_LIST_ITEM_IS_SELECTED_ITEM_MODIFIER_KEY,
modify: (x: DbxValueListItem<T>) => {
x.selected = listItemIsSelected(x);
}
};

return modifiers;
})
);

override ngOnDestroy(): void {
super.ngOnDestroy();
this._listItemIsSelected.complete();
}

@Input('dbxListItemIsSelectedModifier')
set listItemIsSelected(listItemIsSelected: Maybe<DbxValueListItemDecisionFunction<T>>) {
this._listItemIsSelected.next(listItemIsSelected ?? DEFAULT_DBX_LIST_ITEM_IS_SELECTED_FUNCTION);
}
}
13 changes: 12 additions & 1 deletion packages/dbx-web/src/lib/layout/list/list.view.value.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { forwardRef, InjectionToken, Provider, StaticProvider, Type } from '@angular/core';
import { ClickableAnchor, DbxInjectionComponentConfig } from '@dereekb/dbx-core';
import { map, Observable, of } from 'rxjs';
import { Maybe } from '@dereekb/util';
import { DecisionFunction, Maybe } from '@dereekb/util';

export const DBX_VALUE_LIST_VIEW_ITEM = new InjectionToken<unknown>('DbxValueListViewItem');

Expand All @@ -14,6 +14,17 @@ export interface DbxValueListItem<T> {
anchor?: Maybe<ClickableAnchor>;
}

export type DbxValueListItemDecisionFunction<T> = DecisionFunction<DbxValueListItem<T>>;

/**
* Convenience function for mapping a DecisionFunction for a value to a DecisionFunction for a DbxValueListItem with the same value type.
* @param decisionFunction
* @returns
*/
export function dbxValueListItemDecisionFunction<T>(decisionFunction: DecisionFunction<T>): DbxValueListItemDecisionFunction<T> {
return (item) => decisionFunction(item.itemValue);
}

/**
* Special type used with values that contain all the items of DbxValueListItem internally.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/util/src/lib/set/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './set.allowed';
export * from './set.hashset';
export * from './set.maybe';
export * from './set.selection';
export * from './set';
21 changes: 21 additions & 0 deletions packages/util/src/lib/set/set.selection.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { readIndexNumber } from '../value/indexed';
import { isSelectedDecisionFunctionFactory } from './set.selection';

describe('isSelectedDecisionFunctionFactory()', () => {
describe('factory', () => {
const factory = isSelectedDecisionFunctionFactory({
readKey: readIndexNumber
});

describe('function', () => {
it('should return true for the selected numbers.', () => {
const selected = [0, 1, 2];
const fn = factory(selected);

selected.forEach((i) => expect(fn({ i })));
expect(fn({ i: -1 })).toBe(false);
expect(fn({ i: 4 })).toBe(false);
});
});
});
});
40 changes: 40 additions & 0 deletions packages/util/src/lib/set/set.selection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { iterableToArray } from '../iterable';
import { PrimativeKey, ReadKeyFunction } from '../key';
import { DecisionFunction } from '../value';

/**
* Factory that creates a DecisionFunction using the input "selected" values. That function returns true if the value's key is considered to be included in the selected values.
*/
export type IsSelectedDecisionFunctionFactory<T, K extends PrimativeKey = PrimativeKey> = (selectedValues: Iterable<K>) => DecisionFunction<T>;

export interface IsSelectedDecisionFunctionConfig<T, K extends PrimativeKey = PrimativeKey> {
/**
* Reads the key from the input value.
*/
readKey: ReadKeyFunction<T, K>;
/**
* Default value to use if readKey returns no key.
*
* Defaults to false.
*/
defaultIfKeyNull?: boolean;
}

/**
* Creates a IsSelectedDecisionFunctionFactory
*
* @param config
* @returns
*/
export function isSelectedDecisionFunctionFactory<T, K extends PrimativeKey = PrimativeKey>(config: IsSelectedDecisionFunctionConfig<T, K>): IsSelectedDecisionFunctionFactory<T, K> {
const { readKey, defaultIfKeyNull = false } = config;

return (selectedValues: Iterable<K>) => {
const selectedValuesSet = new Set(selectedValues);

return (value: T) => {
const key = readKey(value);
return key != null ? selectedValuesSet.has(key) : defaultIfKeyNull;
};
};
}
4 changes: 4 additions & 0 deletions packages/util/src/lib/value/indexed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FactoryWithRequiredInput } from '../getter';
import { Maybe } from './maybe.type';
import { separateValues } from '../grouping';
import { readKeysToMap } from '../map/map.key';
import { isSelectedDecisionFunctionFactory } from '../set';

/**
* A number that denotes which index an item is at.
Expand Down Expand Up @@ -372,3 +373,6 @@ export function indexRangeOverlapsIndexRangeFunction(input: IndexRangeFunctionIn
return input.minIndex <= maxIndex && input.maxIndex >= minIndex;
};
}

// MARK: Selection
export const isSelectedIndexDecisionFunction = isSelectedDecisionFunctionFactory<IndexRef, IndexNumber>({ readKey: readIndexNumber });

0 comments on commit 7dea240

Please sign in to comment.