|
43 | 43 |
|
44 | 44 | <!-- Select All and Search Section --> |
45 | 45 | <div class="controls-section"> |
46 | | - <button |
47 | | - type="button" |
48 | | - class="select-all-button" |
49 | | - @click="selectAll" |
50 | | - :disabled="filteredVariables.length === 0 || selectedVariables.length === filteredVariables.length" |
| 46 | + <b-form-checkbox |
| 47 | + :checked="allSelected" |
| 48 | + :indeterminate="someSelected" |
| 49 | + @change="toggleSelectAll" |
| 50 | + :disabled="filteredVariables.length === 0" |
| 51 | + class="select-all-checkbox" |
51 | 52 | data-cy="variables-to-submit-select-all" |
52 | 53 | > |
53 | 54 | {{ $t('Select All') }} |
54 | | - </button> |
| 55 | + </b-form-checkbox> |
55 | 56 | <button |
56 | 57 | type="button" |
57 | 58 | class="search-button" |
@@ -168,9 +169,9 @@ export default { |
168 | 169 | // Extract watcher output variables |
169 | 170 | Object.assign(variables, this.extractWatcherVariables()); |
170 | 171 | |
171 | | - // Filter: exclude _parent variables, include all others |
| 172 | + // Filter: exclude _parent variables and invalid variable names |
172 | 173 | return Object.keys(variables) |
173 | | - .filter(variable => !variable.startsWith('_parent.')) |
| 174 | + .filter(variable => this.isValidVariableName(variable)) |
174 | 175 | .sort(); |
175 | 176 | }, |
176 | 177 | |
@@ -214,6 +215,22 @@ export default { |
214 | 215 | return this.requiredVariables.filter( |
215 | 216 | variable => !this.selectedVariables.includes(variable) |
216 | 217 | ); |
| 218 | + }, |
| 219 | + |
| 220 | + /** |
| 221 | + * Check if all filtered variables are selected |
| 222 | + */ |
| 223 | + allSelected() { |
| 224 | + return this.filteredVariables.length > 0 && |
| 225 | + this.filteredVariables.every(v => this.selectedVariables.includes(v)); |
| 226 | + }, |
| 227 | + |
| 228 | + /** |
| 229 | + * Check if some (but not all) filtered variables are selected |
| 230 | + */ |
| 231 | + someSelected() { |
| 232 | + const selectedCount = this.filteredVariables.filter(v => this.selectedVariables.includes(v)).length; |
| 233 | + return selectedCount > 0 && selectedCount < this.filteredVariables.length; |
217 | 234 | } |
218 | 235 | }, |
219 | 236 | watch: { |
@@ -297,13 +314,38 @@ export default { |
297 | 314 | this.selectedVariables = this.selectedVariables.filter(v => !filteredSet.has(v)); |
298 | 315 | }, |
299 | 316 | |
| 317 | + toggleSelectAll(checked) { |
| 318 | + if (checked) { |
| 319 | + this.selectAll(); |
| 320 | + } else { |
| 321 | + this.deselectAll(); |
| 322 | + } |
| 323 | + }, |
| 324 | + |
300 | 325 | toggleSearch() { |
301 | 326 | this.showSearch = !this.showSearch; |
302 | 327 | if (!this.showSearch) { |
303 | 328 | this.searchQuery = ''; |
304 | 329 | } |
305 | 330 | }, |
306 | 331 | |
| 332 | + /** |
| 333 | + * Check if a variable name is valid |
| 334 | + * Uses same logic as dot_notation validation rule |
| 335 | + */ |
| 336 | + isValidVariableName(name) { |
| 337 | + if (!name || typeof name !== 'string') { |
| 338 | + return false; |
| 339 | + } |
| 340 | + if (name.startsWith('_parent.')) { |
| 341 | + return false; |
| 342 | + } |
| 343 | + // Same regex as dot_notation: starts with letter, followed by letters, numbers, or underscores |
| 344 | + const validPartRegex = /^[a-zA-Z][a-zA-Z0-9_]*$/; |
| 345 | + const parts = name.split('.'); |
| 346 | + return parts.every(part => validPartRegex.test(part)); |
| 347 | + }, |
| 348 | + |
307 | 349 | /** |
308 | 350 | * Extract calculated variables (computed properties) from the screen |
309 | 351 | * Searches in multiple locations: App.vue, builder, or parent components |
@@ -681,25 +723,16 @@ export default { |
681 | 723 | margin-top: 0; |
682 | 724 | } |
683 | 725 |
|
684 | | -.select-all-button { |
685 | | - background: none; |
686 | | - border: none; |
687 | | - padding: 0; |
688 | | - color: #0d6efd; |
689 | | - font-size: 15px; |
| 726 | +.select-all-checkbox { |
| 727 | + margin: 0; |
| 728 | + font-size: 14px; |
690 | 729 | font-weight: 600; |
691 | | - cursor: pointer; |
692 | | - text-decoration: none; |
693 | | -} |
694 | | -
|
695 | | -.select-all-button:hover:not(:disabled) { |
696 | | - color: #0a58ca; |
697 | | - text-decoration: none; |
| 730 | + color: #495057; |
698 | 731 | } |
699 | 732 |
|
700 | | -.select-all-button:disabled { |
701 | | - color: #adb5bd; |
702 | | - cursor: not-allowed; |
| 733 | +.select-all-checkbox >>> .custom-control-label { |
| 734 | + cursor: pointer; |
| 735 | + user-select: none; |
703 | 736 | } |
704 | 737 |
|
705 | 738 | .search-button { |
|
0 commit comments