Skip to content

Commit 3ea5c2d

Browse files
authored
Merge pull request #78 from czeckd/locale
Support for locale
2 parents 6adb6e7 + 515d06c commit 3ea5c2d

File tree

7 files changed

+103
-63
lines changed

7 files changed

+103
-63
lines changed

README.md

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@
33
Angular Dual-Listbox
44
=========
55

6-
The **angular-dual-listbox** is an Angular 4+ component that provides two lists controls
7-
side-by-side that allows items in one list to be moved to the other list via drag-and-drop and/or a
8-
button-based interface. The component supports multiple select options from the list, programatic
9-
setting of list sources, and layout with direction and button formatting.
6+
The **angular-dual-listbox** is an Angular 4+ component that provides two lists controls side-by-side that allows items in one list to be selected and moved* to the other list via drag-and-drop and/or a button-based interface. The component supports multiple select options from the list, programatic setting of list sources, and layout with direction and button formatting.
107

118
A [working demo](http://czeckd.github.io/angular-dual-listbox/demo/) shows the dual listbox in action.
129

10+
\* Technically, the dual-list component does not move items from one array to another. Rather it makes a copy from the source array of the item and adds it to the destination array, or removes it from the destination array. Thus, the source array is a master list of all available item and the destintion array is a list of items that have been selected from the master list. Therefore, in order for an item to be in the destination array it must also exist in the source array.
11+
1312
![Dual ListBox](http://czeckd.github.io/angular-dual-listbox/images/dual-listbox.png)
1413

1514
## How to use?
@@ -28,8 +27,7 @@ import { AngularDualListBoxModule } from 'angular-dual-listbox';
2827
})
2928
export class AppModule {}
3029
```
31-
See also the [basic-dual-list-demo](https://github.com/czeckd/basic-dual-listbox-demo) for a sample project using this module.
32-
30+
See also the [basic-dual-list-demo](https://github.com/czeckd/basic-dual-listbox-demo) for a sample project using this module. Note that the default component uses Bootstrap 3 for styling and so the bootstrap.css would need to be included in the project for it to be styled correctly. That said, the styles can be overriden with your own style sheet or fully customized by extending the `DualListComponent` and providing a new template. For more details, see the section on **Extending** below.
3331

3432
## Usage
3533
Basic usage is:
@@ -38,22 +36,36 @@ Basic usage is:
3836
```
3937
The following parameters can be set on a dual-list:
4038
- **key** - The unique identifier field of each object in the `source` and
41-
`destination` arrays, default is ``_id``. (With a source of an array of strings, each string is its own id.)
39+
`destination` arrays, default is ``_id``. (Note: with a source of an array of strings, each string is its own id.)
4240
- **display** - The field of each object for displaying the object each the
43-
lists, default is ``_name``. (With a source of an array of strings, each string is its own display.)
41+
lists, default is ``_name``. Or, a function that returns a string that can be used for displaying an object. (Note: with a source of an array of strings, each string is its own display.)
4442
- **height** - The height of the lists, default is ``100px``.
45-
- **format** - A format object, default is ``{ add: 'Add', remove: 'Remove', all: 'All', none: 'None', direction: 'left-to-right', draggable: true }``
43+
- **format** - A format object, default is ``{ add: 'Add', remove: 'Remove', all: 'All', none: 'None', direction: 'left-to-right', draggable: true, locale: undefined }``
4644
- **filter** - A boolean whether or not to display a filter for the lists, default is ``false``.
4745
- **sort** - A boolean whether or not to keep the lists sorted, default is ``false``.
4846
- **compare** - A compare function to be used for sorting the lists. Note if
4947
sort is not set and compare is set, then sort will be set ``true``.
5048
- **source** - The source array of objects or strings for the list. (This is the universal, master list of all possible objects.)
51-
- **destination** The destination array of objects or strings selected from the source.
52-
Note, the ``destination`` array can have prexisting elements.
49+
- **destination** The destination array of objects or strings selected from the source. Note, the ``destination`` array can have prexisting elements.
5350
- **disabled** - The dual-list is disabled, default is ``false``.
5451

5552
For more usage examples, see the [`demo-app.component.ts`](https://github.com/czeckd/angular-dual-listbox/blob/master/app/demo-app.component.ts).
5653

54+
## Format
55+
The format object allows for the text for the add, remove, all, and none buttons to be set. It also can be used to set the layout direction with the source being on the left-hand side as the default, toggling drag-and-drop, and explicitly setting the locale for the filter string comparision. The default locale is undefined and will use host environment's current locale. An example format object:
56+
```typescript
57+
export class MyComponent {
58+
...
59+
format = { add: 'Tilføje', remove: 'Fjerne', all: 'Alle', none: 'Intet',
60+
direction: DualListComponent.LTR, draggable: true, locale: 'da' };
61+
...
62+
}
63+
```
64+
Then used in an html template:
65+
```html
66+
<dual-list [source]="source" [(destination)]="confirmed" [format]="format"></dual-list>
67+
```
68+
5769
## Extending
5870
The html template packaged with this component is based on Bootstap 3; however it can be overridden for customization. Here is an example:
5971

@@ -69,8 +81,8 @@ import { DualListComponent } from 'angular-dual-listbox';
6981
export class CustomDualListComponent extends DualListComponent {
7082
}
7183
```
72-
See [`dual-list.component.html`](https://github.com/czeckd/angular-dual-listbox/blob/master/lib/dual-list.component.html) and
73-
[`dual-list.component.css`](https://github.com/czeckd/angular-dual-listbox/blob/master/lib/dual-list.component.css) for template and style guidance.
84+
See [`dual-list.component.html`](https://github.com/czeckd/angular-dual-listbox/blob/master/lib/dual-list.component.html) and [`dual-list.component.css`](https://github.com/czeckd/angular-dual-listbox/blob/master/lib/dual-list.component.css) for template and style guidance.
85+
7486
There is also an Angular-CLI seed project, [custom-dual-listbox](https://github.com/czeckd/custom-dual-listbox), available with an example of a
7587
customized view and extended functionality.
7688

app/demo-app.component.ts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import { Component, OnInit } from '@angular/core';
22

33
import { DualListComponent } from 'angular-dual-listbox';
44

5-
65
@Component({
76
selector: 'demo-app',
8-
styles: [ '.form-group { margin-bottom: 16px; }', '.checkbox { margin-top: inherit }' ],
7+
styles: [ 'form { margin-top: 15px; }', '.checkbox { margin-top: inherit; }', 'ul.nav-tabs { cursor: pointer; }' ],
98
template: `
109
<div class="container-fluid">
1110
<p></p>
@@ -27,37 +26,43 @@ import { DualListComponent } from 'angular-dual-listbox';
2726
</div>
2827
2928
<div class="tab-pane" [class.active]="tab===2">
30-
<form class="form" style="margin:20px 0;">
31-
<label>Direction</label><br/>
29+
<form class="form">
3230
<div class="form-group">
31+
<label style="display:block;">Direction</label>
3332
<div class="btn-group">
3433
<button type="button" class="btn" [ngClass]="{ 'btn-primary' : sourceLeft, 'btn-default' : !sourceLeft }"
3534
(click)="swapDirection()">left-to-right</button>
3635
<button type="button" class="btn" [ngClass]="{ 'btn-primary' : !sourceLeft, 'btn-default' : sourceLeft }"
3736
(click)="swapDirection()">right-to-left</button>
3837
</div>
3938
</div>
39+
40+
<div class="form-group">
41+
<label>Enable drag-and-drop</label>
42+
<div class="checkbox">
43+
<label><input type="checkbox" [(ngModel)]="format.draggable" name="drag">draggable</label>
44+
</div>
45+
</div>
46+
47+
<div class="form-group">
48+
<label>Locale</label>
49+
<input class="form-control" [(ngModel)]="format.locale" name="locale">
50+
</div>
4051
<div class="form-group">
4152
<label>Add button</label>
42-
<input class="form-control col-sm-2" [(ngModel)]="format.add" name="addBtn">
53+
<input class="form-control" [(ngModel)]="format.add" name="addBtn">
4354
</div>
4455
<div class="form-group">
4556
<label>Remove button</label>
46-
<input class="form-control col-sm-2" [(ngModel)]="format.remove" name="rmBtn">
57+
<input class="form-control" [(ngModel)]="format.remove" name="rmBtn">
4758
</div>
4859
<div class="form-group">
4960
<label>All button</label>
50-
<input class="form-control col-sm-2" [(ngModel)]="format.all" name="allBtn">
61+
<input class="form-control" [(ngModel)]="format.all" name="allBtn">
5162
</div>
5263
<div class="form-group">
5364
<label>None button</label>
54-
<input class="form-control col-sm-2" [(ngModel)]="format.none" name="noneBtn">
55-
</div>
56-
<div class="form-group">
57-
<label>Enable drag-and-drop</label>
58-
<div class="checkbox">
59-
<label><input type="checkbox" [(ngModel)]="format.draggable" name="drag">draggable</label>
60-
</div>
65+
<input class="form-control" [(ngModel)]="format.none" name="noneBtn">
6166
</div>
6267
</form>
6368
</div>
@@ -114,7 +119,7 @@ export class DemoAppComponent implements OnInit {
114119
tab = 1;
115120
keepSorted = true;
116121
key:string;
117-
display:string;
122+
display:any;
118123
filter = false;
119124
source:Array<any>;
120125
confirmed:Array<any>;
@@ -216,9 +221,13 @@ export class DemoAppComponent implements OnInit {
216221
this.doReset();
217222
}
218223

224+
private stationLabel(item:any) {
225+
return item.station + ', ' + item.state;
226+
}
227+
219228
private useStations() {
220229
this.key = 'key';
221-
this.display = 'station'; // [ 'station', 'state' ];
230+
this.display = this.stationLabel;
222231
this.keepSorted = true;
223232
this.source = this.sourceStations;
224233
this.confirmed = this.confirmedStations;

lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "../../node_modules/ng-packagr/package.schema.json",
33
"name": "angular-dual-listbox",
44
"description": "Angular 4+ component for a dual listbox control.",
5-
"version": "4.6.3",
5+
"version": "4.7.0",
66
"repository": {
77
"type": "git",
88
"url": "https://github.com/czeckd/angular-dual-listbox.git"

lib/src/dual-list.component.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,14 @@ div.record-picker::-webkit-scrollbar-thumb:hover {
128128

129129
/* &nbsp;&nbsp;&nbsp;&#9654; */
130130
.point-right::after {
131-
content: "\00A0\00A0\00A0\25B6";
131+
content: "\25B6";
132+
padding-left: 1em;
132133
}
133134

134135
/* &#9664;&nbsp;&nbsp;&nbsp; */
135136
.point-left::before {
136-
content: "\25C0\00A0\00A0\00A0";
137+
content: "\25C0";
138+
padding-right: 1em;
137139
}
138140

139141
.dual-list .button-bar button {

lib/src/dual-list.component.ts

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ export class DualListComponent implements DoCheck, OnChanges {
2626
all: 'All',
2727
none: 'None',
2828
direction: DualListComponent.LTR,
29-
draggable: true
29+
draggable: true,
30+
locale: undefined
3031
};
3132

3233
@Input() id = `dual-list-${nextId++}`;
3334
@Input() key = '_id';
34-
@Input() display = '_name';
35+
@Input() display:any = '_name';
3536
@Input() height = '100px';
3637
@Input() filter = false;
3738
@Input() format = DualListComponent.DEFAULT_FORMAT;
@@ -206,7 +207,7 @@ export class DualListComponent implements DoCheck, OnChanges {
206207
return this.format.direction === DualListComponent.LTR;
207208
}
208209

209-
dragEnd(list:BasicList = null) {
210+
dragEnd(list:BasicList = null) : boolean {
210211
if (list) {
211212
list.dragStart = false;
212213
} else {
@@ -226,7 +227,7 @@ export class DualListComponent implements DoCheck, OnChanges {
226227
event.dataTransfer.setData(this.id, item['_id']);
227228
}
228229

229-
allowDrop(event:DragEvent, list:BasicList) {
230+
allowDrop(event:DragEvent, list:BasicList) : boolean {
230231
if (event.dataTransfer.types.length && (event.dataTransfer.types[0] === this.id)) {
231232
event.preventDefault();
232233
if (!list.dragStart) {
@@ -275,7 +276,6 @@ export class DualListComponent implements DoCheck, OnChanges {
275276
}
276277
}
277278

278-
279279
// Push added items.
280280
for (let i = 0, len = this.confirmed.list.length; i < len; i += 1) {
281281
let mv = this.destination.filter( (d:any) => {
@@ -396,7 +396,7 @@ export class DualListComponent implements DoCheck, OnChanges {
396396
}, 10);
397397
}
398398

399-
isItemSelected(list:Array<any>, item:any) {
399+
isItemSelected(list:Array<any>, item:any) : boolean {
400400
if (list.filter(e => Object.is(e, item)).length > 0) {
401401
return true;
402402
}
@@ -445,14 +445,14 @@ export class DualListComponent implements DoCheck, OnChanges {
445445
source.pick.length = 0;
446446
}
447447

448-
isAllSelected(source:BasicList) {
448+
isAllSelected(source:BasicList) : boolean {
449449
if (source.list.length === 0 || source.list.length === source.pick.length) {
450450
return true;
451451
}
452452
return false;
453453
}
454454

455-
isAnySelected(source:BasicList) {
455+
isAnySelected(source:BasicList) : boolean {
456456
if (source.pick.length > 0) {
457457
return true;
458458
}
@@ -476,19 +476,30 @@ export class DualListComponent implements DoCheck, OnChanges {
476476

477477
onFilter(source:BasicList) {
478478
if (source.picker.length > 0) {
479-
const filtered = source.list.filter( (item:any) => {
480-
if (Object.prototype.toString.call(item) === '[object Object]') {
481-
if (item._name !== undefined) {
482-
return item._name.toLocaleLowerCase().indexOf(source.picker.toLocaleLowerCase()) !== -1;
479+
try {
480+
const filtered = source.list.filter( (item:any) => {
481+
if (Object.prototype.toString.call(item) === '[object Object]') {
482+
if (item._name !== undefined) {
483+
// @ts-ignore: remove when d.ts has locale as an argument.
484+
return item._name.toLocaleLowerCase(this.format.locale).indexOf(source.picker.toLocaleLowerCase(this.format.locale)) !== -1;
485+
} else {
486+
// @ts-ignore: remove when d.ts has locale as an argument.
487+
return JSON.stringify(item).toLocaleLowerCase(this.format.locale).indexOf(source.picker.toLocaleLowerCase(this.format.locale)) !== -1;
488+
}
483489
} else {
484-
return JSON.stringify(item).toLocaleLowerCase().indexOf(source.picker.toLocaleLowerCase()) !== -1;
490+
// @ts-ignore: remove when d.ts has locale as an argument.
491+
return item.toLocaleLowerCase(this.format.locale).indexOf(source.picker.toLocaleLowerCase(this.format.locale)) !== -1;
485492
}
486-
} else {
487-
return item.toLocaleLowerCase().indexOf(source.picker.toLocaleLowerCase()) !== -1;
493+
});
494+
source.sift = filtered;
495+
this.unpick(source);
496+
}
497+
catch (e) {
498+
if (e instanceof RangeError) {
499+
this.format.locale = undefined;
488500
}
489-
});
490-
source.sift = filtered;
491-
this.unpick(source);
501+
source.sift = source.list;
502+
}
492503
} else {
493504
source.sift = source.list;
494505
}
@@ -525,17 +536,20 @@ export class DualListComponent implements DoCheck, OnChanges {
525536
let str = '';
526537

527538
if (this.display !== undefined) {
528-
if (Object.prototype.toString.call( this.display ) === '[object Array]' ) {
539+
switch (Object.prototype.toString.call(this.display)) {
540+
case '[object Function]':
541+
str = this.display(item);
542+
break;
529543

530-
for (let i = 0; i < this.display.length; i += 1) {
544+
case '[object Array]':
545+
for (let i = 0, len = this.display.length; i < len; i += 1) {
531546
if (str.length > 0) {
532547
str = str + separator;
533548
}
534549

535550
if (this.display[i].indexOf('.') === -1) {
536551
// Simple, just add to string.
537552
str = str + item[this.display[i]];
538-
539553
} else {
540554
// Complex, some action needs to be performed
541555
const parts = this.display[i].split('.');
@@ -564,12 +578,15 @@ export class DualListComponent implements DoCheck, OnChanges {
564578
}
565579
}
566580
}
567-
return str;
568-
} else {
569-
return fallback(item);
581+
break;
582+
default:
583+
str = fallback(item);
584+
break;
570585
}
586+
} else {
587+
str = fallback(item);
571588
}
572589

573-
return fallback(item);
590+
return str;
574591
}
575592
}

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "angular-dual-listbox",
33
"description": "Angular 4+ component for a dual listbox control.",
4-
"version": "4.6.3",
4+
"version": "4.7.0",
55
"repository": {
66
"type": "git",
77
"url": "https://github.com/czeckd/angular-dual-listbox.git"
@@ -52,7 +52,7 @@
5252
"systemjs": "0.19.47",
5353
"ts-node": "^3.3.0",
5454
"tslint": "^5.8.0",
55-
"typescript": "~2.4.2",
55+
"typescript": "^2.6.2",
5656
"zone.js": "^0.8.18"
5757
}
5858
}

0 commit comments

Comments
 (0)