1 | import angular from 'angular';
|
2 |
|
3 | import deepEqual from 'deep-equal';
|
4 |
|
5 | class Options {
|
6 | static OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+select\s+as\s+(.*?))?(?:\s+describe\sas\s+(.*?))?(?:\s+for\s+)?([$\w]+)\s+in\s+(.*?)(?:\s+track\sby\s+(.*?))?$/;
|
7 |
|
8 | static MATCHES = {
|
9 | ITEM: 1,
|
10 | LABEL: 2,
|
11 | SELECTED_LABEL: 3,
|
12 | DESCRIPTION: 4,
|
13 | OPTION: 5,
|
14 | ITEMS: 6,
|
15 | TRACK: 7
|
16 | };
|
17 |
|
18 | static defaultKeyField = 'key';
|
19 | static defaultLabelField = 'label';
|
20 | static defaultSelectedLabelField = 'selectedLabel';
|
21 | static defaultDescriptionField = 'description';
|
22 |
|
23 | constructor(scope, optionsString) {
|
24 | this.scope = scope;
|
25 | const $parse = this.constructor.$parse;
|
26 | const MATCHES = this.constructor.MATCHES;
|
27 |
|
28 | let match;
|
29 | if (!(match = optionsString.match(this.constructor.OPTIONS_REGEXP))) {
|
30 | throw new Error('Bad rgSelect expression format. Expected: [{item}] [[as] item.text] [select as item.selectLabel]' +
|
31 | ` [describe as {item.description}] [for] {item} in {items|dataSource(query)} [track by item.id], Received: ${optionsString}`);
|
32 | }
|
33 |
|
34 | |
35 |
|
36 |
|
37 |
|
38 | this.hasItemGetter = Boolean(match[MATCHES.ITEM] && match[MATCHES.LABEL]);
|
39 |
|
40 | this.itemGetter = $parse(match[MATCHES.ITEM]);
|
41 | this.labelGetter = (match[MATCHES.LABEL] && $parse(match[MATCHES.LABEL])) || this.itemGetter;
|
42 | this.selectedLabelGetter =
|
43 | match[MATCHES.SELECTED_LABEL] &&
|
44 | $parse(match[MATCHES.SELECTED_LABEL]);
|
45 | this.descriptionGetter = match[MATCHES.DESCRIPTION] && $parse(match[MATCHES.DESCRIPTION]);
|
46 | this.optionVariableName = match[MATCHES.OPTION];
|
47 | this.datasourceGetter = $parse(match[MATCHES.ITEMS]);
|
48 | this.trackByGetter = match[MATCHES.TRACK] && $parse(match[MATCHES.TRACK]);
|
49 | this.datasourceIsFunction = match[MATCHES.ITEMS].indexOf('(') > 0;
|
50 | }
|
51 |
|
52 | getProperty(option, getter) {
|
53 | if (getter) {
|
54 | const locals = {};
|
55 | locals[this.optionVariableName] = option;
|
56 | return getter.call(this, this.scope, locals);
|
57 | }
|
58 |
|
59 | return undefined;
|
60 | }
|
61 |
|
62 | |
63 |
|
64 |
|
65 |
|
66 | getValue(option) {
|
67 | if (!this.hasItemGetter) {
|
68 | return option;
|
69 | }
|
70 |
|
71 | const value = this.getProperty(option, this.itemGetter);
|
72 |
|
73 | return value === undefined ? option : value;
|
74 | }
|
75 |
|
76 | |
77 |
|
78 |
|
79 |
|
80 |
|
81 | getOptionByValue(value, options) {
|
82 | |
83 |
|
84 |
|
85 |
|
86 | function toString(it) {
|
87 | return typeof it === 'object' ? JSON.stringify(it) : String(it);
|
88 | }
|
89 |
|
90 | if (!this.hasItemGetter) {
|
91 | return value;
|
92 | }
|
93 |
|
94 | const matchedOptions = options.filter(option => {
|
95 | const optionValue = this.getValue(option);
|
96 |
|
97 | if (typeof value === 'object') {
|
98 | return deepEqual(optionValue, value);
|
99 | }
|
100 |
|
101 | return optionValue === value;
|
102 | });
|
103 |
|
104 | if (matchedOptions.length > 1) {
|
105 | throw new Error(`Error(rg-select): You can not have two options with same value(${toString(value)})`);
|
106 | }
|
107 |
|
108 | return matchedOptions[0];
|
109 | }
|
110 |
|
111 | getKey(option) {
|
112 | return this.getProperty(option, this.trackByGetter) ||
|
113 | option[this.constructor.defaultKeyField] ||
|
114 | option;
|
115 | }
|
116 |
|
117 | getLabel(option) {
|
118 | const optionStringValue = typeof option === 'string' ? option : null;
|
119 | return this.getProperty(option, this.labelGetter) ||
|
120 | option[this.constructor.defaultLabelField] ||
|
121 | optionStringValue;
|
122 | }
|
123 |
|
124 | getSelectedLabel(option) {
|
125 | return this.getProperty(option, this.selectedLabelGetter) ||
|
126 | option[this.constructor.defaultSelectedLabelField];
|
127 | }
|
128 |
|
129 | getDescription(option) {
|
130 | return this.getProperty(option, this.descriptionGetter) ||
|
131 | option[this.constructor.defaultDescriptionField];
|
132 | }
|
133 |
|
134 | getOptions(query, skip) {
|
135 | return this.datasourceGetter(this.scope, {query, skip});
|
136 | }
|
137 | }
|
138 |
|
139 |
|
140 | const angularModule = angular.module('Ring.select.options', []);
|
141 |
|
142 | angularModule.factory('SelectOptions', function SelectOptionsFactory($parse) {
|
143 | Options.$parse = $parse;
|
144 | return Options;
|
145 | });
|
146 |
|
147 | export default angularModule.name;
|