Skip to content

Commit

Permalink
Merge pull request #206 from fmicheloni/master
Browse files Browse the repository at this point in the history
Disable selection of checkboxes without disabling the opening of the …
  • Loading branch information
softsimon authored Jul 5, 2017
2 parents 3191861 + 01718b4 commit bce3039
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 85 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ myOptions: IMultiSelectOption[] = [
| selectionLimit | Maximum number of items that may be selected (0 = no limit) | 0 |
| autoUnselect | Unselect the previous selection(s) once selectionLimit is reached | false |
| closeOnSelect | If enabled, dropdown will be closed after selection | false |
| showCheckAll | Display the `checkAll` item to select all options | false |
| showUncheckAll | Display the `uncheckAll` item to unselect all options | false |
| showCheckAll | Display the `checkAll` item to select all options | false |
| showUncheckAll | Display the `uncheckAll` item to unselect all options | false |
| fixedTitle | Use the default title (do not apply the dynamic title) | false |
| dynamicTitleMaxItems | The maximum number of options to display in the dynamic title | 3 |
| maxHeight | The maximum height for the dropdown (including unit) | '300px' |
| displayAllSelectedText | Display the `allSelected` text when all options are selected | false |
| displayAllSelectedText | Display the `allSelected` text when all options are selected | false |

### Texts
| Text Item | Description | Default Value |
Expand Down
18 changes: 9 additions & 9 deletions src/dropdown/dropdown.component.html
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
<div class="dropdown" [ngClass]="settings.containerClasses" [class.open]="isVisible">
<button type="button" class="dropdown-toggle" [ngClass]="settings.buttonClasses" (click)="toggleDropdown()" [disabled]="disabled">{{ title }}<span class="caret"></span></button>
<ul *ngIf="isVisible" class="dropdown-menu" [class.pull-right]="settings.pullRight" [class.dropdown-menu-right]="settings.pullRight"
[style.max-height]="settings.maxHeight" style="display: block; height: auto; overflow-y: auto;">
[style.max-height]="settings.maxHeight" style="display: block; height: auto; overflow-y: auto;">
<li class="dropdown-item search" *ngIf="settings.enableSearch">
<div class="input-group input-group-sm">
<span class="input-group-addon" id="sizing-addon3"><i class="fa fa-search"></i></span>
<input type="text" class="form-control" placeholder="{{ texts.searchPlaceholder }}" aria-describedby="sizing-addon3" [(ngModel)]="searchFilterText"
[ngModelOptions]="{standalone: true}" autofocus>
[ngModelOptions]="{standalone: true}" autofocus>
<span class="input-group-btn" *ngIf="searchFilterText.length > 0">
<button class="btn btn-default btn-secondary" type="button" (click)="clearSearch($event)"><i class="fa fa-times"></i></button>
</span>
</div>
</li>
<li class="dropdown-divider divider" *ngIf="settings.enableSearch"></li>
<li class="dropdown-item check-control check-control-check" *ngIf="settings.showCheckAll">
<li class="dropdown-item check-control check-control-check" *ngIf="settings.showCheckAll && !disabledSelection">
<a href="javascript:;" role="menuitem" tabindex="-1" (click)="checkAll()">
<span style="width: 16px;" [ngClass]="{'glyphicon glyphicon-ok': settings.checkedStyle !== 'fontawesome','fa fa-check': settings.checkedStyle === 'fontawesome'}"></span>
{{ texts.checkAll }}
</a>
</li>
<li class="dropdown-item check-control check-control-uncheck" *ngIf="settings.showUncheckAll">
<li class="dropdown-item check-control check-control-uncheck" *ngIf="settings.showUncheckAll && !disabledSelection">
<a href="javascript:;" role="menuitem" tabindex="-1" (click)="uncheckAll()">
<span style="width: 16px;" [ngClass]="{'glyphicon glyphicon-remove': settings.checkedStyle !== 'fontawesome','fa fa-times': settings.checkedStyle === 'fontawesome'}"></span>
{{ texts.uncheckAll }}
</a>
</li>
<li *ngIf="settings.showCheckAll || settings.showUncheckAll" class="dropdown-divider divider"></li>
<li *ngIf="(settings.showCheckAll || settings.showUncheckAll) && !disabledSelection" class="dropdown-divider divider"></li>
<li class="dropdown-item" [ngStyle]="getItemStyle(option)" *ngFor="let option of options | searchFilter:searchFilterText"
(click)="!option.isLabel && setSelected($event, option)" [class.dropdown-header]="option.isLabel">
(click)="!option.isLabel && setSelected($event, option)" [class.dropdown-header]="option.isLabel">
<ng-template [ngIf]="option.isLabel">{{ option.name }}</ng-template>
<a *ngIf="!option.isLabel" href="javascript:;" role="menuitem" tabindex="-1" [style.padding-left]="this.parents.length>0&&this.parents.indexOf(option.id)<0&&'30px'">
<input *ngIf="settings.checkedStyle === 'checkboxes'" type="checkbox" [checked]="isSelected(option)" (click)="preventCheckboxCheck($event, option)"/>
<a *ngIf="!option.isLabel" href="javascript:;" role="menuitem" tabindex="-1" [style.padding-left]="parents.length>0&&parents.indexOf(option.id)<0&&'30px'" [ngStyle]="getItemStyleSelectionDisabled()">
<input *ngIf="settings.checkedStyle === 'checkboxes'" type="checkbox" [checked]="isSelected(option)" (click)="preventCheckboxCheck($event, option)" [disabled]="isCheckboxDisabled()" [ngStyle]="getItemStyleSelectionDisabled()"/>
<span *ngIf="settings.checkedStyle === 'glyphicon'" style="width: 16px;" class="glyphicon" [class.glyphicon-ok]="isSelected(option)"></span>
<span *ngIf="settings.checkedStyle === 'fontawesome'" style="width: 16px;display: inline-block;">
<i *ngIf="isSelected(option)" class="fa fa-check" aria-hidden="true"></i>
</span>
<span [ngClass]="settings.itemClasses" [style.font-weight]="this.parents.indexOf(option.id)>=0?'bold':'normal'">
<span [ngClass]="settings.itemClasses" [style.font-weight]="parents.indexOf(option.id)>=0?'bold':'normal'">
{{ option.name }}
</span>
</a>
Expand Down
165 changes: 93 additions & 72 deletions src/dropdown/dropdown.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class MultiselectDropdown implements OnInit, OnChanges, DoCheck, ControlV
@Input() settings: IMultiSelectSettings;
@Input() texts: IMultiSelectTexts;
@Input() disabled: boolean = false;
@Input() disabledSelection: false;
@Output() selectionLimitReached = new EventEmitter();
@Output() dropdownClosed = new EventEmitter();
@Output() dropdownOpened = new EventEmitter();
Expand Down Expand Up @@ -82,7 +83,7 @@ export class MultiselectDropdown implements OnInit, OnChanges, DoCheck, ControlV
showUncheckAll: false,
fixedTitle: false,
dynamicTitleMaxItems: 3,
maxHeight: '300px',
maxHeight: '300px'
};
defaultTexts: IMultiSelectTexts = {
checkAll: 'Check all',
Expand All @@ -95,16 +96,23 @@ export class MultiselectDropdown implements OnInit, OnChanges, DoCheck, ControlV
};

constructor(private element: ElementRef,
differs: IterableDiffers) {
differs: IterableDiffers) {
this.differ = differs.find([]).create(null);
}

getItemStyle(option: IMultiSelectOption): any {
if (!option.isLabel) {
return { 'cursor': 'pointer' };
return {'cursor': 'pointer'};
}
}

getItemStyleSelectionDisabled(): any {
if (this.disabledSelection) {
return {'cursor': 'default'};
}
}


ngOnInit() {
this.settings = Object.assign(this.defaultSettings, this.settings);
this.texts = Object.assign(this.defaultTexts, this.texts);
Expand All @@ -125,8 +133,10 @@ export class MultiselectDropdown implements OnInit, OnChanges, DoCheck, ControlV
}
}

onModelChange: Function = (_: any) => { };
onModelTouched: Function = () => { };
onModelChange: Function = (_: any) => {
};
onModelTouched: Function = () => {
};

writeValue(value: any): void {
if (value !== undefined && value !== null) {
Expand Down Expand Up @@ -183,58 +193,60 @@ export class MultiselectDropdown implements OnInit, OnChanges, DoCheck, ControlV
}

setSelected(_event: Event, option: IMultiSelectOption) {
_event.stopPropagation();
if (!this.model) {
this.model = [];
}
const index = this.model.indexOf(option.id);
if (index > -1) {
this.model.splice(index, 1);
this.onRemoved.emit(option.id);
const parentIndex = option.parentId && this.model.indexOf(option.parentId);
if (parentIndex >= 0) {
this.model.splice(parentIndex, 1);
this.onRemoved.emit(option.parentId);
} else if (this.parents.indexOf(option.id) > -1) {
let childIds = this.options.filter(child => this.model.indexOf(child.id) > -1 && child.parentId == option.id).map(child => child.id);
this.model = this.model.filter(id => childIds.indexOf(id) < 0);
childIds.forEach(childId => this.onRemoved.emit(childId));
if (!this.disabledSelection) {
_event.stopPropagation();
if (!this.model) {
this.model = [];
}
} else {
if (this.settings.selectionLimit === 0 || (this.settings.selectionLimit && this.model.length < this.settings.selectionLimit)) {
this.model.push(option.id);
this.onAdded.emit(option.id);
if (option.parentId) {
let children = this.options.filter(child => child.id !== option.id && child.parentId == option.parentId);
if (children.every(child => this.model.indexOf(child.id) > -1)) {
this.model.push(option.parentId);
this.onAdded.emit(option.parentId);
}
const index = this.model.indexOf(option.id);
if (index > -1) {
this.model.splice(index, 1);
this.onRemoved.emit(option.id);
const parentIndex = option.parentId && this.model.indexOf(option.parentId);
if (parentIndex >= 0) {
this.model.splice(parentIndex, 1);
this.onRemoved.emit(option.parentId);
} else if (this.parents.indexOf(option.id) > -1) {
let children = this.options.filter(child => this.model.indexOf(child.id) < 0 && child.parentId == option.id);
children.forEach(child => {
this.model.push(child.id);
this.onAdded.emit(child.id);
})
let childIds = this.options.filter(child => this.model.indexOf(child.id) > -1 && child.parentId == option.id).map(child => child.id);
this.model = this.model.filter(id => childIds.indexOf(id) < 0);
childIds.forEach(childId => this.onRemoved.emit(childId));
}
} else {
if (this.settings.autoUnselect) {
if (this.settings.selectionLimit === 0 || (this.settings.selectionLimit && this.model.length < this.settings.selectionLimit)) {
this.model.push(option.id);
this.onAdded.emit(option.id);
const removedOption = this.model.shift();
this.onRemoved.emit(removedOption);
if (option.parentId) {
let children = this.options.filter(child => child.id !== option.id && child.parentId == option.parentId);
if (children.every(child => this.model.indexOf(child.id) > -1)) {
this.model.push(option.parentId);
this.onAdded.emit(option.parentId);
}
} else if (this.parents.indexOf(option.id) > -1) {
let children = this.options.filter(child => this.model.indexOf(child.id) < 0 && child.parentId == option.id);
children.forEach(child => {
this.model.push(child.id);
this.onAdded.emit(child.id);
})
}
} else {
this.selectionLimitReached.emit(this.model.length);
return;
if (this.settings.autoUnselect) {
this.model.push(option.id);
this.onAdded.emit(option.id);
const removedOption = this.model.shift();
this.onRemoved.emit(removedOption);
} else {
this.selectionLimitReached.emit(this.model.length);
return;
}
}
}
if (this.settings.closeOnSelect) {
this.toggleDropdown();
}
this.model = this.model.slice();
this.onModelChange(this.model);
this.onModelTouched();
}
if (this.settings.closeOnSelect) {
this.toggleDropdown();
}
this.model = this.model.slice();
this.onModelChange(this.model);
this.onModelTouched();
}

updateNumSelected() {
Expand Down Expand Up @@ -265,34 +277,38 @@ export class MultiselectDropdown implements OnInit, OnChanges, DoCheck, ControlV
}

checkAll() {
let checkedOptions = (!this.searchFilterApplied() ? this.options :
(new MultiSelectSearchFilter()).transform(this.options, this.searchFilterText))
.filter((option: IMultiSelectOption) => {
if (this.model.indexOf(option.id) === -1) {
this.onAdded.emit(option.id);
return true;
}
return false;
}).map((option: IMultiSelectOption) => option.id);
this.model = this.model.concat(checkedOptions);
this.onModelChange(this.model);
this.onModelTouched();
if (!this.disabledSelection) {
let checkedOptions = (!this.searchFilterApplied() ? this.options :
(new MultiSelectSearchFilter()).transform(this.options, this.searchFilterText))
.filter((option: IMultiSelectOption) => {
if (this.model.indexOf(option.id) === -1) {
this.onAdded.emit(option.id);
return true;
}
return false;
}).map((option: IMultiSelectOption) => option.id);
this.model = this.model.concat(checkedOptions);
this.onModelChange(this.model);
this.onModelTouched();
}
}

uncheckAll() {
let unCheckedOptions = (!this.searchFilterApplied() ? this.model
: (new MultiSelectSearchFilter()).transform(this.options, this.searchFilterText).map((option: IMultiSelectOption) => option.id)
);
this.model = this.model.filter((id: number) => {
if (unCheckedOptions.indexOf(id) < 0) {
return true;
} else {
this.onRemoved.emit(id);
return false;
}
});
this.onModelChange(this.model);
this.onModelTouched();
if (!this.disabledSelection) {
let unCheckedOptions = (!this.searchFilterApplied() ? this.model
: (new MultiSelectSearchFilter()).transform(this.options, this.searchFilterText).map((option: IMultiSelectOption) => option.id)
);
this.model = this.model.filter((id: number) => {
if (unCheckedOptions.indexOf(id) < 0) {
return true;
} else {
this.onRemoved.emit(id);
return false;
}
});
this.onModelChange(this.model);
this.onModelTouched();
}
}

preventCheckboxCheck(event: Event, option: IMultiSelectOption) {
Expand All @@ -303,4 +319,9 @@ export class MultiselectDropdown implements OnInit, OnChanges, DoCheck, ControlV
event.preventDefault();
}
}

isCheckboxDisabled(): boolean {
return this.disabledSelection;
}

}
2 changes: 1 addition & 1 deletion src/dropdown/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface IMultiSelectSettings {
fixedTitle?: boolean;
dynamicTitleMaxItems?: number;
maxHeight?: string;
displayAllSelectedText?: boolean;
displayAllSelectedText?: boolean
}

export interface IMultiSelectTexts {
Expand Down

0 comments on commit bce3039

Please sign in to comment.