@signal24/vue-foundation 3.3.4 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signal24/vue-foundation",
3
- "version": "3.3.4",
3
+ "version": "3.7.1",
4
4
  "description": "Common components, directives, and helpers for Vue 3 apps",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -8,21 +8,21 @@
8
8
  },
9
9
  "license": "MIT",
10
10
  "dependencies": {
11
- "axios": "^0.21.1",
11
+ "axios": "^0.26.0",
12
12
  "jquery": "^3.6.0",
13
13
  "lodash": "^4.17.21",
14
14
  "moment": "^2.29.1",
15
- "vue": "^3.2.29",
16
15
  "vue-stash-nested": "fergusean/vue-stash-nested#vue-foundation-3"
17
16
  },
18
17
  "devDependencies": {
19
- "@vue/eslint-config-prettier": "^6.0.0",
20
- "eslint": "^7.32.0",
21
- "eslint-plugin-prettier": "^3.4.1",
18
+ "@vue/eslint-config-prettier": "^7.0.0",
19
+ "eslint": "^8.10.0",
20
+ "eslint-plugin-prettier": "^4.0.0",
22
21
  "eslint-plugin-simple-import-sort": "^7.0.0",
23
- "eslint-plugin-vue": "^7.20.0",
22
+ "eslint-plugin-vue": "^8.5.0",
24
23
  "prettier": "^2.5.1",
25
- "sass": "^1.49.7",
26
- "sass-loader": "^10"
24
+ "sass": "^1.49.9",
25
+ "sass-loader": "^12",
26
+ "vue": "^3.2.31"
27
27
  }
28
28
  }
@@ -41,14 +41,16 @@ import debounce from 'lodash/debounce';
41
41
  const nullSymbol = Symbol(null);
42
42
  const createSymbol = Symbol('create');
43
43
 
44
- const VALID_KEYS = `\`1234567890-=[]\;',./~!@#$%^&*()_+{}|:"<>?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM`;
44
+ const VALID_KEYS = `\`1234567890-=[]\\;',./~!@#$%^&*()_+{}|:"<>?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM`;
45
45
 
46
46
  export default {
47
- emits: ['optionsLoaded', 'createItem'],
47
+ emits: ['optionsLoaded', 'createItem', 'update:modelValue'],
48
48
 
49
49
  props: [
50
50
  'modelValue',
51
51
  'options',
52
+ 'prependOptions',
53
+ 'appendOptions',
52
54
  'preload',
53
55
  'url',
54
56
  'urlParams',
@@ -71,8 +73,7 @@ export default {
71
73
  data() {
72
74
  return {
73
75
  isLoaded: false,
74
- resolvedOptions: [],
75
- decoratedOptions: [],
76
+ loadedOptions: [],
76
77
  isSearching: false,
77
78
  searchText: '',
78
79
  selectedOption: null,
@@ -84,8 +85,11 @@ export default {
84
85
  },
85
86
 
86
87
  computed: {
88
+ /**
89
+ * EFFECTIVE PROPS
90
+ */
87
91
  effectiveDisabled() {
88
- return this.disabled || !this.resolvedOptions;
92
+ return this.disabled || !this.loadedOptions;
89
93
  },
90
94
 
91
95
  effectivePlaceholder() {
@@ -94,8 +98,73 @@ export default {
94
98
  return this.placeholder || '';
95
99
  },
96
100
 
101
+ effectiveIdKey() {
102
+ return this.idKey || 'id';
103
+ },
104
+
105
+ effectiveTitleKey() {
106
+ return this.titleKey || 'name';
107
+ },
108
+
109
+ effectiveValueKey() {
110
+ if (this.valueKey) return this.valueKey;
111
+ if (this.options && !Array.isArray(this.options)) return this.effectiveIdKey;
112
+ return undefined;
113
+ },
114
+
115
+ effectiveNoResultsText() {
116
+ return this.noResultsText || 'No options match your search.';
117
+ },
118
+
119
+ /**
120
+ * OPTIONS GENERATION
121
+ */
122
+
123
+ loadedOptionsArray() {
124
+ return this.arrayifyOptions(this.loadedOptions);
125
+ },
126
+
127
+ prependOptionsArray() {
128
+ return this.prependOptions ? this.arrayifyOptions(this.prependOptions) : [];
129
+ },
130
+
131
+ appendOptionsArray() {
132
+ return this.appendOptions ? this.arrayifyOptions(this.appendOptions) : [];
133
+ },
134
+
135
+ fullOptionsArray() {
136
+ return [...this.prependOptionsArray, ...this.loadedOptionsArray, ...this.appendOptionsArray];
137
+ },
138
+
139
+ optionsDescriptors() {
140
+ return this.fullOptionsArray.map((option, index) => {
141
+ const title = this.getOptionTitle(option);
142
+ const subtitle = this.getOptionSubtitle(option);
143
+ const strippedTitle = title ? title.text.trim().toLowerCase() : '';
144
+ const strippedSubtitle = subtitle ? subtitle.text.trim().toLowerCase() : '';
145
+
146
+ let searchContent = [];
147
+ if (this.searchFields) {
148
+ this.searchFields.forEach(field => {
149
+ option[field] && searchContent.push(String(option[field]).toLowerCase());
150
+ });
151
+ } else {
152
+ searchContent.push(strippedTitle);
153
+ strippedSubtitle && searchContent.push(strippedSubtitle);
154
+ }
155
+
156
+ return {
157
+ key: typeof option == 'object' ? option[this.effectiveIdKey] || index : option,
158
+ titleHtml: title.html,
159
+ subtitleHtml: subtitle?.html,
160
+ searchContent: searchContent.join(''),
161
+ ref: option
162
+ };
163
+ });
164
+ },
165
+
97
166
  effectiveOptions() {
98
- let options = [...this.decoratedOptions];
167
+ let options = [...this.optionsDescriptors];
99
168
 
100
169
  if (this.isSearching) {
101
170
  const strippedSearchText = this.searchText.trim().toLowerCase();
@@ -133,18 +202,6 @@ export default {
133
202
  }
134
203
 
135
204
  return options;
136
- },
137
-
138
- effectiveIdKey() {
139
- return this.idKey || 'id';
140
- },
141
-
142
- effectiveTitleKey() {
143
- return this.titleKey || 'name';
144
- },
145
-
146
- effectiveNoResultsText() {
147
- return this.noResultsText || 'No options match your search.';
148
205
  }
149
206
  },
150
207
 
@@ -156,7 +213,7 @@ export default {
156
213
  },
157
214
 
158
215
  options() {
159
- this.resolvedOptions = this.options;
216
+ this.loadedOptions = this.options;
160
217
  },
161
218
 
162
219
  url() {
@@ -169,32 +226,7 @@ export default {
169
226
 
170
227
  // data
171
228
 
172
- resolvedOptions() {
173
- this.decoratedOptions = this.resolvedOptions.map((option, index) => {
174
- const title = this.getOptionTitle(option);
175
- const subtitle = this.getOptionSubtitle(option);
176
- const strippedTitle = title ? title.text.trim().toLowerCase() : '';
177
- const strippedSubtitle = subtitle ? subtitle.text.trim().toLowerCase() : '';
178
-
179
- let searchContent = [];
180
- if (this.searchFields) {
181
- this.searchFields.forEach(field => {
182
- option[field] && searchContent.push(String(option[field]).toLowerCase());
183
- });
184
- } else {
185
- searchContent.push(strippedTitle);
186
- strippedSubtitle && searchContent.push(strippedSubtitle);
187
- }
188
-
189
- return {
190
- key: typeof option == 'object' ? option[this.effectiveIdKey] || index : option,
191
- titleHtml: title.html,
192
- subtitleHtml: subtitle?.html,
193
- searchContent: searchContent.join(''),
194
- ref: option
195
- };
196
- });
197
-
229
+ optionsDescriptors() {
198
230
  this.shouldDisplayOptions && setTimeout(this.highlightInitialOption, 0);
199
231
  },
200
232
 
@@ -225,17 +257,19 @@ export default {
225
257
  this.shouldShowCreateOption = this.$attrs['onCreateItem'] !== undefined;
226
258
 
227
259
  if (this.options) {
228
- this.resolvedOptions = this.options;
260
+ this.loadedOptions = this.options;
229
261
  this.isLoaded = true;
230
262
  } else if (this.$isPropTruthy(this.preload)) {
231
- this.performInitialLoad();
263
+ await this.performInitialLoad();
232
264
  }
233
265
 
234
266
  this.handleValueChanged();
235
267
 
236
268
  this.$watch('selectedOption', () => {
237
269
  const newValue =
238
- this.selectedOption && this.valueKey ? this.selectedOption[this.valueKey] : this.selectedOption;
270
+ this.selectedOption && this.effectiveValueKey
271
+ ? this.selectedOption[this.effectiveValueKey]
272
+ : this.selectedOption;
239
273
  newValue === this.modelValue || this.$emit('update:modelValue', newValue);
240
274
  });
241
275
  },
@@ -243,7 +277,7 @@ export default {
243
277
  methods: {
244
278
  async performInitialLoad() {
245
279
  await this.reloadOptions();
246
- this.$emit('optionsLoaded', this.resolvedOptions);
280
+ this.$emit('optionsLoaded', this.loadedOptions);
247
281
 
248
282
  if (this.$isPropTruthy(this.remoteSearch)) {
249
283
  this.$watch('searchText', debounce(this.reloadOptionsIfSearching, 250));
@@ -254,7 +288,7 @@ export default {
254
288
  if (this.preload) return this.reloadOptions();
255
289
  if (!this.isLoaded) return;
256
290
  this.isLoaded = false;
257
- this.resolvedOptions = [];
291
+ this.loadedOptions = [];
258
292
  },
259
293
 
260
294
  async reloadOptions() {
@@ -263,7 +297,7 @@ export default {
263
297
  this.$isPropTruthy(this.remoteSearch) && this.searchText && (params.q = this.searchText);
264
298
 
265
299
  const result = await this.$http.get(this.url, { params: params });
266
- this.resolvedOptions = result.data;
300
+ this.loadedOptions = result.data;
267
301
  this.isLoaded = true;
268
302
  },
269
303
 
@@ -429,10 +463,10 @@ export default {
429
463
  this.selectedOptionTitle = null;
430
464
  this.$emit('createItem', createText);
431
465
  } else {
432
- const optionIndex = this.decoratedOptions.findIndex(
466
+ const selectedDecoratedOption = this.optionsDescriptors.find(
433
467
  decoratedOption => decoratedOption.key == option.key
434
468
  );
435
- const realOption = this.resolvedOptions[optionIndex];
469
+ const realOption = selectedDecoratedOption.ref;
436
470
  this.selectedOption = realOption;
437
471
  this.selectedOptionTitle = this.getOptionTitle(this.selectedOption).text;
438
472
  this.searchText = this.selectedOptionTitle || '';
@@ -443,9 +477,9 @@ export default {
443
477
 
444
478
  handleValueChanged() {
445
479
  if (this.modelValue) {
446
- if (this.valueKey) {
447
- this.selectedOption = this.resolvedOptions.find(
448
- option => option[this.valueKey] === this.modelValue
480
+ if (this.effectiveValueKey) {
481
+ this.selectedOption = this.fullOptionsArray.find(
482
+ option => option[this.effectiveValueKey] === this.modelValue
449
483
  );
450
484
  } else {
451
485
  this.selectedOption = this.modelValue;
@@ -509,7 +543,16 @@ export default {
509
543
  },
510
544
 
511
545
  addRemoteOption(option) {
512
- this.resolvedOptions.push(option);
546
+ this.loadedOptions.push(option);
547
+ },
548
+
549
+ arrayifyOptions(options) {
550
+ return Array.isArray(options)
551
+ ? options
552
+ : Object.entries(options).map(entry => ({
553
+ [this.effectiveIdKey]: entry[0],
554
+ [this.effectiveTitleKey]: entry[1]
555
+ }));
513
556
  }
514
557
  }
515
558
  };