@gitlab/ui 126.3.4 → 126.4.0

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.
Files changed (90) hide show
  1. package/dist/components/base/alert/alert.js +9 -0
  2. package/dist/components/base/breadcrumb/breadcrumb.js +3 -0
  3. package/dist/components/base/breadcrumb/breadcrumb_item.js +15 -0
  4. package/dist/components/base/datepicker/datepicker.js +54 -0
  5. package/dist/components/base/daterange_picker/daterange_picker.js +70 -1
  6. package/dist/components/base/filtered_search/filtered_search.js +1 -1
  7. package/dist/components/base/form/form_input_group/form_input_group.js +9 -1
  8. package/dist/components/base/path/data.js +1 -1
  9. package/dist/components/base/search_box_by_click/search_box_by_click.js +9 -1
  10. package/dist/components/dashboards/dashboard_panel/dashboard_panel.js +10 -2
  11. package/dist/directives/resize_observer/resize_observer.js +10 -3
  12. package/dist/index.css.map +1 -1
  13. package/dist/tailwind.css +1 -1
  14. package/dist/tailwind.css.map +1 -1
  15. package/dist/vendor/bootstrap-vue/src/components/modal/modal.js +7 -2
  16. package/dist/vendor/bootstrap-vue/src/components/table/helpers/mixin-filtering.js +8 -0
  17. package/dist/vendor/bootstrap-vue/src/components/table/helpers/mixin-items.js +4 -0
  18. package/dist/vendor/bootstrap-vue/src/components/table/helpers/mixin-provider.js +8 -4
  19. package/dist/vendor/bootstrap-vue/src/components/table/helpers/mixin-selectable.js +3 -19
  20. package/dist/vendor/bootstrap-vue/src/components/table/helpers/mixin-sorting.js +1 -16
  21. package/dist/vendor/bootstrap-vue/src/components/table/helpers/mixin-tbody.js +8 -2
  22. package/dist/vendor/bootstrap-vue/src/components/table/helpers/mixin-thead.js +4 -0
  23. package/dist/vendor/bootstrap-vue/src/components/toast/helpers/bv-toast.js +15 -7
  24. package/dist/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-popper.js +15 -13
  25. package/dist/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip.js +24 -21
  26. package/dist/vendor/bootstrap-vue/src/components/tooltip/tooltip.js +10 -9
  27. package/dist/vendor/bootstrap-vue/src/components/transition/bv-transition.js +3 -0
  28. package/dist/vendor/bootstrap-vue/src/components/transporter/transporter.js +29 -21
  29. package/dist/vendor/bootstrap-vue/src/constants/events.js +1 -5
  30. package/dist/vendor/bootstrap-vue/src/directives/tooltip/tooltip.js +15 -13
  31. package/dist/vendor/bootstrap-vue/src/directives/visible/visible.js +15 -16
  32. package/dist/vendor/bootstrap-vue/src/mixins/attrs.js +1 -18
  33. package/dist/vendor/bootstrap-vue/src/mixins/dropdown.js +8 -3
  34. package/dist/vendor/bootstrap-vue/src/mixins/has-listener.js +1 -1
  35. package/dist/vendor/bootstrap-vue/src/mixins/listeners.js +13 -12
  36. package/dist/vendor/bootstrap-vue/src/utils/cache.js +35 -13
  37. package/dist/vendor/bootstrap-vue/src/utils/config-set.js +16 -11
  38. package/dist/vendor/bootstrap-vue/src/utils/config.js +4 -10
  39. package/dist/vendor/bootstrap-vue/src/utils/create-new-child-component.js +102 -3
  40. package/dist/vendor/bootstrap-vue/src/utils/element-to-vue-instance-registry.js +3 -9
  41. package/dist/vendor/bootstrap-vue/src/utils/get-instance-from-directive.js +1 -3
  42. package/dist/vendor/bootstrap-vue/src/utils/on-instance-destroy.js +22 -0
  43. package/dist/vendor/bootstrap-vue/src/utils/plugins.js +2 -21
  44. package/dist/vendor/bootstrap-vue/src/utils/router.js +15 -1
  45. package/dist/vendor/bootstrap-vue/src/utils/safe-vue-instance.js +1 -1
  46. package/dist/vendor/bootstrap-vue/src/vue.js +16 -81
  47. package/package.json +6 -6
  48. package/src/components/base/alert/alert.vue +9 -0
  49. package/src/components/base/breadcrumb/breadcrumb.vue +3 -0
  50. package/src/components/base/breadcrumb/breadcrumb_item.vue +15 -0
  51. package/src/components/base/datepicker/datepicker.vue +54 -0
  52. package/src/components/base/daterange_picker/daterange_picker.vue +70 -1
  53. package/src/components/base/filtered_search/filtered_search.vue +1 -1
  54. package/src/components/base/form/form_input_group/form_input_group.vue +9 -1
  55. package/src/components/base/path/data.js +1 -1
  56. package/src/components/base/search_box_by_click/search_box_by_click.vue +9 -0
  57. package/src/components/dashboards/dashboard_panel/dashboard_panel.vue +27 -9
  58. package/src/directives/resize_observer/resize_observer.js +6 -0
  59. package/src/vendor/bootstrap-vue/src/components/modal/modal.js +6 -1
  60. package/src/vendor/bootstrap-vue/src/components/table/helpers/mixin-filtering.js +8 -0
  61. package/src/vendor/bootstrap-vue/src/components/table/helpers/mixin-items.js +4 -0
  62. package/src/vendor/bootstrap-vue/src/components/table/helpers/mixin-provider.js +9 -4
  63. package/src/vendor/bootstrap-vue/src/components/table/helpers/mixin-selectable.js +3 -24
  64. package/src/vendor/bootstrap-vue/src/components/table/helpers/mixin-sorting.js +1 -20
  65. package/src/vendor/bootstrap-vue/src/components/table/helpers/mixin-tbody.js +8 -2
  66. package/src/vendor/bootstrap-vue/src/components/table/helpers/mixin-thead.js +4 -0
  67. package/src/vendor/bootstrap-vue/src/components/toast/helpers/bv-toast.js +15 -13
  68. package/src/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-popper.js +14 -13
  69. package/src/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip.js +16 -23
  70. package/src/vendor/bootstrap-vue/src/components/tooltip/tooltip.js +10 -9
  71. package/src/vendor/bootstrap-vue/src/components/transition/bv-transition.js +4 -0
  72. package/src/vendor/bootstrap-vue/src/components/transporter/transporter.js +36 -26
  73. package/src/vendor/bootstrap-vue/src/constants/events.js +0 -5
  74. package/src/vendor/bootstrap-vue/src/directives/tooltip/tooltip.js +17 -13
  75. package/src/vendor/bootstrap-vue/src/directives/visible/visible.js +13 -9
  76. package/src/vendor/bootstrap-vue/src/mixins/attrs.js +1 -17
  77. package/src/vendor/bootstrap-vue/src/mixins/dropdown.js +8 -3
  78. package/src/vendor/bootstrap-vue/src/mixins/has-listener.js +1 -1
  79. package/src/vendor/bootstrap-vue/src/mixins/listeners.js +14 -14
  80. package/src/vendor/bootstrap-vue/src/utils/cache.js +27 -7
  81. package/src/vendor/bootstrap-vue/src/utils/config-set.js +16 -11
  82. package/src/vendor/bootstrap-vue/src/utils/config.js +4 -10
  83. package/src/vendor/bootstrap-vue/src/utils/create-new-child-component.js +85 -2
  84. package/src/vendor/bootstrap-vue/src/utils/element-to-vue-instance-registry.js +3 -10
  85. package/src/vendor/bootstrap-vue/src/utils/get-instance-from-directive.js +1 -4
  86. package/src/vendor/bootstrap-vue/src/utils/on-instance-destroy.js +21 -0
  87. package/src/vendor/bootstrap-vue/src/utils/plugins.js +1 -26
  88. package/src/vendor/bootstrap-vue/src/utils/router.js +13 -1
  89. package/src/vendor/bootstrap-vue/src/utils/safe-vue-instance.js +1 -1
  90. package/src/vendor/bootstrap-vue/src/vue.js +11 -98
@@ -24,111 +24,177 @@ export default {
24
24
  GlTooltip: GlTooltipDirective,
25
25
  },
26
26
  props: {
27
+ /**
28
+ * Label text for the start date picker.
29
+ */
27
30
  fromLabel: {
28
31
  type: String,
29
32
  required: false,
30
33
  default: 'From',
31
34
  },
35
+ /**
36
+ * Label text for the end date picker.
37
+ */
32
38
  toLabel: {
33
39
  type: String,
34
40
  required: false,
35
41
  default: 'To',
36
42
  },
43
+ /**
44
+ * Accessible label for the start date input.
45
+ */
37
46
  fromAriaLabel: {
38
47
  type: String,
39
48
  required: false,
40
49
  default: 'From date',
41
50
  },
51
+ /**
52
+ * Accessible label for the end date input.
53
+ */
42
54
  toAriaLabel: {
43
55
  type: String,
44
56
  required: false,
45
57
  default: 'To date',
46
58
  },
59
+ /**
60
+ * The selected date range object containing `startDate` and `endDate` properties.
61
+ */
47
62
  value: {
48
63
  type: Object,
49
64
  required: false,
50
65
  default: null,
51
66
  },
67
+ /**
68
+ * Internationalization object for Pikaday labels and month/day names.
69
+ */
52
70
  i18n: {
53
71
  type: Object,
54
72
  required: false,
55
73
  default: null,
56
74
  },
75
+ /**
76
+ * The earliest selectable date for the start date picker.
77
+ */
57
78
  defaultMinDate: {
58
79
  type: Date,
59
80
  required: false,
60
81
  default: null,
61
82
  },
83
+ /**
84
+ * The latest selectable date for the end date picker.
85
+ */
62
86
  defaultMaxDate: {
63
87
  type: Date,
64
88
  required: false,
65
89
  default: null,
66
90
  },
91
+ /**
92
+ * Initial value for the start date.
93
+ */
67
94
  defaultStartDate: {
68
95
  type: Date,
69
96
  required: false,
70
97
  default: null,
71
98
  },
99
+ /**
100
+ * Initial value for the end date.
101
+ */
72
102
  defaultEndDate: {
73
103
  type: Date,
74
104
  required: false,
75
105
  default: null,
76
106
  },
107
+ /**
108
+ * Maximum number of days allowed between start and end date. Set to 0 for no limit.
109
+ */
77
110
  maxDateRange: {
78
111
  type: Number,
79
112
  required: false,
80
113
  default: 0,
81
114
  },
115
+ /**
116
+ * Additional CSS class(es) for the start date picker container.
117
+ */
82
118
  startPickerClass: {
83
119
  type: String,
84
120
  required: false,
85
121
  default: '',
86
122
  },
123
+ /**
124
+ * Selector of element that triggers the start date picker.
125
+ */
87
126
  startPickerTarget: {
88
127
  type: String,
89
128
  required: false,
90
129
  default: '',
91
130
  },
131
+ /**
132
+ * DOM node selector to render the start date calendar into.
133
+ */
92
134
  startPickerContainer: {
93
135
  type: String,
94
136
  required: false,
95
137
  default: '',
96
138
  },
139
+ /**
140
+ * Validation state for the start date picker input.
141
+ */
97
142
  startPickerState: {
98
143
  type: Boolean,
99
144
  required: false,
100
145
  default: null,
101
146
  },
147
+ /**
148
+ * Additional CSS class(es) for the end date picker container.
149
+ */
102
150
  endPickerClass: {
103
151
  type: String,
104
152
  required: false,
105
153
  default: '',
106
154
  },
155
+ /**
156
+ * Selector of element that triggers the end date picker.
157
+ */
107
158
  endPickerTarget: {
108
159
  type: String,
109
160
  required: false,
110
161
  default: '',
111
162
  },
163
+ /**
164
+ * DOM node selector to render the end date calendar into.
165
+ */
112
166
  endPickerContainer: {
113
167
  type: String,
114
168
  required: false,
115
169
  default: '',
116
170
  },
171
+ /**
172
+ * Validation state for the end date picker input.
173
+ */
117
174
  endPickerState: {
118
175
  type: Boolean,
119
176
  required: false,
120
177
  default: null,
121
178
  },
179
+ /**
180
+ * Additional CSS class(es) for the label elements.
181
+ */
122
182
  labelClass: {
123
183
  type: String,
124
184
  required: false,
125
185
  default: '',
126
186
  },
187
+ /**
188
+ * Additional CSS class for the Pikaday calendar theme.
189
+ */
127
190
  theme: {
128
191
  type: String,
129
192
  required: false,
130
193
  default: '',
131
194
  },
195
+ /**
196
+ * Whether to allow selecting the same date for start and end.
197
+ */
132
198
  sameDaySelection: {
133
199
  type: Boolean,
134
200
  required: false,
@@ -150,13 +216,16 @@ export default {
150
216
  required: false,
151
217
  default: '',
152
218
  },
219
+ /**
220
+ * Whether the start date picker should be open on mount.
221
+ */
153
222
  startOpened: {
154
223
  type: Boolean,
155
224
  required: false,
156
225
  default: false,
157
226
  },
158
227
  /**
159
- * Maximum width of the Datepicker
228
+ * Maximum width of the Datepicker.
160
229
  */
161
230
  width: {
162
231
  type: String,
@@ -408,7 +408,7 @@ export default {
408
408
  :search-button-attributes="searchButtonAttributes"
409
409
  :show-search-button="showSearchButton"
410
410
  :disabled="viewOnly"
411
- data-testid="filtered-search-input"
411
+ :wrapper-attributes="{ 'data-testid': 'filtered-search-input' }"
412
412
  @submit="submit"
413
413
  @input="applyNewValue"
414
414
  @history-item-selected="$emit('history-item-selected', $event)"
@@ -47,6 +47,14 @@ export default {
47
47
  required: false,
48
48
  default: '',
49
49
  },
50
+ /**
51
+ * HTML attributes to apply to the wrapper element.
52
+ */
53
+ wrapperAttributes: {
54
+ type: Object,
55
+ required: false,
56
+ default: () => ({}),
57
+ },
50
58
  },
51
59
  data() {
52
60
  return {
@@ -68,7 +76,7 @@ export default {
68
76
  };
69
77
  </script>
70
78
  <template>
71
- <div role="group" class="input-group">
79
+ <div role="group" class="input-group" v-bind="wrapperAttributes">
72
80
  <div v-if="activeOption || $scopedSlots.prepend" class="input-group-prepend">
73
81
  <!-- @slot Is rendered in front of the input field. -->
74
82
  <slot name="prepend"></slot>
@@ -1,4 +1,4 @@
1
- export const mockPathItems = [
1
+ export const mockPathItems = () => [
2
2
  {
3
3
  title: 'First',
4
4
  metric: '1d',
@@ -125,6 +125,14 @@ export default {
125
125
  required: false,
126
126
  default: true,
127
127
  },
128
+ /**
129
+ * HTML attributes to apply to the wrapper element.
130
+ */
131
+ wrapperAttributes: {
132
+ type: Object,
133
+ required: false,
134
+ default: () => ({}),
135
+ },
128
136
  },
129
137
  data() {
130
138
  return {
@@ -206,6 +214,7 @@ export default {
206
214
  <gl-form-input-group
207
215
  class="gl-search-box-by-click"
208
216
  :class="{ 'gl-search-box-by-click-with-search-button': showSearchButton }"
217
+ :wrapper-attributes="wrapperAttributes"
209
218
  >
210
219
  <template v-if="historyItems" #prepend>
211
220
  <gl-disclosure-dropdown
@@ -94,6 +94,14 @@ export default {
94
94
  required: false,
95
95
  default: '',
96
96
  },
97
+ /**
98
+ * The string to render as the panel subtitle.
99
+ */
100
+ subtitle: {
101
+ type: String,
102
+ required: false,
103
+ default: '',
104
+ },
97
105
  /**
98
106
  * Set to `true` to show the loading state.
99
107
  */
@@ -197,8 +205,8 @@ export default {
197
205
  :class="containerClasses"
198
206
  >
199
207
  <div class="gl-flex gl-h-full gl-flex-col">
200
- <div class="gl-flex gl-items-start gl-justify-between" data-testid="panel-title">
201
- <div class="gl-flex gl-items-center gl-overflow-hidden gl-pb-3">
208
+ <div class="gl-mb-3 gl-flex gl-items-start gl-justify-between" data-testid="panel-title">
209
+ <div class="gl-flex gl-items-center gl-overflow-hidden">
202
210
  <gl-icon
203
211
  v-if="hasTitleIcon"
204
212
  class="gl-mr-2"
@@ -207,12 +215,22 @@ export default {
207
215
  data-testid="panel-title-icon"
208
216
  />
209
217
 
210
- <gl-truncate
211
- v-if="hasTitle"
212
- class="gl-min-w-0 gl-font-bold gl-text-default"
213
- :text="title"
214
- with-tooltip
215
- />
218
+ <div class="gl-min-w-0">
219
+ <gl-truncate
220
+ v-if="hasTitle"
221
+ class="gl-font-bold gl-text-default"
222
+ :text="title"
223
+ with-tooltip
224
+ />
225
+
226
+ <p
227
+ v-if="subtitle"
228
+ class="gl-mb-0 gl-mt-1 gl-text-sm gl-text-subtle"
229
+ data-testid="panel-subtitle"
230
+ >
231
+ {{ subtitle }}
232
+ </p>
233
+ </div>
216
234
 
217
235
  <template v-if="hasTitlePopover">
218
236
  <gl-icon
@@ -280,7 +298,7 @@ export default {
280
298
 
281
299
  <div
282
300
  v-if="$scopedSlots.filters"
283
- class="gl-flex gl-items-center gl-justify-end gl-pb-2"
301
+ class="gl-flex gl-items-center gl-justify-end"
284
302
  data-testid="panel-filters-container"
285
303
  >
286
304
  <!-- @slot The filter section to add additional UI elements for filtering, grouping, etc. -->
@@ -2,6 +2,12 @@ import isFunction from 'lodash/isFunction';
2
2
 
3
3
  let observer = null;
4
4
 
5
+ // Exported for testing purposes only
6
+ export const resetObserver = () => {
7
+ observer?.disconnect();
8
+ observer = null;
9
+ };
10
+
5
11
  const attachObserver = (el, resizeHandler) => {
6
12
  if (!isFunction(resizeHandler)) {
7
13
  throw TypeError('directive value must be a function');
@@ -386,7 +386,7 @@ export const BModal = /*#__PURE__*/ extend({
386
386
  if (this.isClosing) {
387
387
  // If we are in the process of closing, wait until hidden before re-opening
388
388
  /* istanbul ignore next */
389
- this.$once(EVENT_NAME_HIDDEN, this.show)
389
+ this.$_showOnHidden = true
390
390
  /* istanbul ignore next */
391
391
  return
392
392
  }
@@ -545,6 +545,11 @@ export const BModal = /*#__PURE__*/ extend({
545
545
  // TODO: Need to find a way to pass the `trigger` property
546
546
  // to the `hidden` event, not just only the `hide` event
547
547
  this.emitEvent(this.buildEvent(EVENT_NAME_HIDDEN))
548
+ // Handle pending show request (show was called while closing)
549
+ if (this.$_showOnHidden) {
550
+ this.$_showOnHidden = false
551
+ this.show()
552
+ }
548
553
  })
549
554
  },
550
555
  emitEvent(bvEvent) {
@@ -142,6 +142,10 @@ export const filteringMixin = extend({
142
142
  }
143
143
  if (isFiltered) {
144
144
  this.$emit(EVENT_NAME_FILTERED, filteredItems, filteredItems.length)
145
+ // Clear selection on filter change if selectable (defined in mixin-selectable.js)
146
+ if (this.hasSelectableRowClick) {
147
+ this.clearSelected()
148
+ }
145
149
  }
146
150
  this.isFiltered = isFiltered
147
151
  },
@@ -151,6 +155,10 @@ export const filteringMixin = extend({
151
155
  // `false` so that users can update their pagination controls
152
156
  const { localItems } = this
153
157
  this.$emit(EVENT_NAME_FILTERED, localItems, localItems.length)
158
+ // Clear selection on filter change if selectable (defined in mixin-selectable.js)
159
+ if (this.hasSelectableRowClick) {
160
+ this.clearSelected()
161
+ }
154
162
  }
155
163
  }
156
164
  },
@@ -127,6 +127,10 @@ export const itemsMixin = extend({
127
127
  // Emit context information for external paging/filtering/sorting handling
128
128
  if (!looseEqual(newValue, oldValue)) {
129
129
  this.$emit(EVENT_NAME_CONTEXT_CHANGED, newValue)
130
+ // Clear selection on context change if selectable (defined in mixin-selectable.js)
131
+ if (this.hasSelectableRowClick) {
132
+ this.clearSelected()
133
+ }
130
134
  }
131
135
  }
132
136
  },
@@ -101,15 +101,15 @@ export const providerMixin = extend({
101
101
  },
102
102
  methods: {
103
103
  refresh() {
104
- const { items, refresh, computedBusy } = safeVueInstance(this)
104
+ const { items, computedBusy } = safeVueInstance(this)
105
105
 
106
106
  // Public Method: Force a refresh of the provider function
107
- this.$off(EVENT_NAME_REFRESHED, refresh)
107
+ this.$_pendingRefresh = false
108
108
  if (computedBusy) {
109
109
  // Can't force an update when forced busy by user (busy prop === true)
110
110
  if (this.localBusy && this.hasProvider) {
111
111
  // But if provider running (localBusy), re-schedule refresh once `refreshed` emitted
112
- this.$on(EVENT_NAME_REFRESHED, refresh)
112
+ this.$_pendingRefresh = true
113
113
  }
114
114
  } else {
115
115
  this.clearSelected()
@@ -130,6 +130,11 @@ export const providerMixin = extend({
130
130
  if (this.id) {
131
131
  this.emitOnRoot(ROOT_EVENT_NAME_REFRESHED, this.id)
132
132
  }
133
+ // Handle pending refresh request
134
+ if (this.$_pendingRefresh) {
135
+ this.$_pendingRefresh = false
136
+ this.refresh()
137
+ }
133
138
  },
134
139
  _providerUpdate() {
135
140
  // Refresh the provider function items.
@@ -180,7 +185,7 @@ export const providerMixin = extend({
180
185
  // and clear the busy state
181
186
  warn(`Provider function error [${e.name}] ${e.message}.`, NAME_TABLE)
182
187
  this.localBusy = false
183
- this.$off(EVENT_NAME_REFRESHED, this.refresh)
188
+ this.$_pendingRefresh = false
184
189
  }
185
190
  })
186
191
  }
@@ -1,10 +1,5 @@
1
1
  import { extend } from '../../../vue'
2
- import {
3
- EVENT_NAME_CONTEXT_CHANGED,
4
- EVENT_NAME_FILTERED,
5
- EVENT_NAME_ROW_CLICKED,
6
- EVENT_NAME_ROW_SELECTED
7
- } from '../../../constants/events'
2
+ import { EVENT_NAME_ROW_SELECTED } from '../../../constants/events'
8
3
  import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../../constants/props'
9
4
  import { arrayIncludes, createArray } from '../../../utils/array'
10
5
  import { identity } from '../../../utils/identity'
@@ -104,16 +99,14 @@ export const selectableMixin = extend({
104
99
  this.clearSelected()
105
100
  }
106
101
  },
107
- selectable(newValue) {
102
+ selectable() {
108
103
  this.clearSelected()
109
- this.setSelectionHandlers(newValue)
110
104
  },
111
105
  selectMode() {
112
106
  this.clearSelected()
113
107
  },
114
- hasSelectableRowClick(newValue) {
108
+ hasSelectableRowClick() {
115
109
  this.clearSelected()
116
- this.setSelectionHandlers(!newValue)
117
110
  },
118
111
  selectedRows(selectedRows, oldValue) {
119
112
  if (this.isSelectable && !looseEqual(selectedRows, oldValue)) {
@@ -128,12 +121,6 @@ export const selectableMixin = extend({
128
121
  }
129
122
  }
130
123
  },
131
- beforeMount() {
132
- // Set up handlers if needed
133
- if (this.isSelectable) {
134
- this.setSelectionHandlers(true)
135
- }
136
- },
137
124
  methods: {
138
125
  // Public methods
139
126
  selectRow(index) {
@@ -193,14 +180,6 @@ export const selectableMixin = extend({
193
180
  'aria-selected': !this.isSelectable ? null : this.isRowSelected(index) ? 'true' : 'false'
194
181
  }
195
182
  },
196
- setSelectionHandlers(on) {
197
- const method = on && !this.noSelectOnClick ? '$on' : '$off'
198
- // Handle row-clicked event
199
- this[method](EVENT_NAME_ROW_CLICKED, this.selectionHandler)
200
- // Clear selection on filter, pagination, and sort changes
201
- this[method](EVENT_NAME_FILTERED, this.clearSelected)
202
- this[method](EVENT_NAME_CONTEXT_CHANGED, this.clearSelected)
203
- },
204
183
  selectionHandler(item, index, event) {
205
184
  /* istanbul ignore if: should never happen */
206
185
  if (!this.isSelectable || this.noSelectOnClick) {
@@ -1,9 +1,5 @@
1
1
  import { extend } from '../../../vue'
2
- import {
3
- EVENT_NAME_HEAD_CLICKED,
4
- EVENT_NAME_SORT_CHANGED,
5
- MODEL_EVENT_NAME_PREFIX
6
- } from '../../../constants/events'
2
+ import { EVENT_NAME_SORT_CHANGED, MODEL_EVENT_NAME_PREFIX } from '../../../constants/events'
7
3
  import {
8
4
  PROP_TYPE_ARRAY_STRING,
9
5
  PROP_TYPE_BOOLEAN,
@@ -140,16 +136,6 @@ export const sortingMixin = extend({
140
136
  }
141
137
  },
142
138
  watch: {
143
- /* istanbul ignore next: pain in the butt to test */
144
- isSortable(newValue) {
145
- if (newValue) {
146
- if (this.isSortable) {
147
- this.$on(EVENT_NAME_HEAD_CLICKED, this.handleSort)
148
- }
149
- } else {
150
- this.$off(EVENT_NAME_HEAD_CLICKED, this.handleSort)
151
- }
152
- },
153
139
  [MODEL_PROP_NAME_SORT_DESC](newValue) {
154
140
  /* istanbul ignore next */
155
141
  if (newValue === this.localSortDesc) {
@@ -177,11 +163,6 @@ export const sortingMixin = extend({
177
163
  }
178
164
  }
179
165
  },
180
- created() {
181
- if (this.isSortable) {
182
- this.$on(EVENT_NAME_HEAD_CLICKED, this.handleSort)
183
- }
184
- },
185
166
  methods: {
186
167
  // Handlers
187
168
  // Need to move from thead-mixin
@@ -71,12 +71,18 @@ export const tbodyMixin = extend({
71
71
  },
72
72
  // Emits a row event, with the item object, row index and original event
73
73
  emitTbodyRowEvent(type, event) {
74
- if (type && this.hasListener(type) && event && event.target) {
74
+ if (type && event && event.target) {
75
75
  const rowIndex = this.getTbodyTrIndex(event.target)
76
76
  if (rowIndex > -1) {
77
77
  // The array of TRs correlate to the `computedItems` array
78
78
  const item = this.computedItems[rowIndex]
79
- this.$emit(type, item, rowIndex, event)
79
+ if (this.hasListener(type)) {
80
+ this.$emit(type, item, rowIndex, event)
81
+ }
82
+ // Call selectionHandler directly if selectable (defined in mixin-selectable.js)
83
+ if (type === EVENT_NAME_ROW_CLICKED && this.hasSelectableRowClick) {
84
+ this.selectionHandler(item, rowIndex, event)
85
+ }
80
86
  }
81
87
  }
82
88
  },
@@ -59,6 +59,10 @@ export const theadMixin = extend({
59
59
  }
60
60
  stopEvent(event)
61
61
  this.$emit(EVENT_NAME_HEAD_CLICKED, field.key, field, event, isFoot)
62
+ // Call handleSort directly if sortable (defined in mixin-sorting.js)
63
+ if (this.isSortable) {
64
+ this.handleSort(field.key, field, event, isFoot)
65
+ }
62
66
  },
63
67
  renderThead(isFoot = false) {
64
68
  const {
@@ -3,13 +3,8 @@
3
3
  */
4
4
 
5
5
  import { NAME_TOAST, NAME_TOASTER, NAME_TOAST_POP } from '../../../constants/components'
6
- import {
7
- EVENT_NAME_DESTROYED,
8
- EVENT_NAME_HIDDEN,
9
- EVENT_NAME_HIDE,
10
- EVENT_NAME_SHOW,
11
- HOOK_EVENT_NAME_DESTROYED
12
- } from '../../../constants/events'
6
+ import { EVENT_NAME_DESTROYED, EVENT_NAME_HIDE, EVENT_NAME_SHOW } from '../../../constants/events'
7
+ import { onInstanceDestroy } from '../../../utils/on-instance-destroy'
13
8
  import { useParentMixin } from '../../../mixins/use-parent'
14
9
  import { concat } from '../../../utils/array'
15
10
  import { getComponentConfig } from '../../../utils/config'
@@ -77,8 +72,8 @@ const plugin = Vue => {
77
72
  }
78
73
  },
79
74
  mounted() {
80
- // Self destruct handler
81
- const handleDestroy = () => {
75
+ // Self destruct handler (stored as instance property for use in onAfterLeave)
76
+ this.$_handleDestroy = () => {
82
77
  // Ensure the toast has been force hidden
83
78
  this.localShow = false
84
79
  this.doRender = false
@@ -93,16 +88,23 @@ const plugin = Vue => {
93
88
  })
94
89
  }
95
90
  // Self destruct if parent destroyed
96
- this.bvParent.$once(HOOK_EVENT_NAME_DESTROYED, handleDestroy)
97
- // Self destruct after hidden
98
- this.$once(EVENT_NAME_HIDDEN, handleDestroy)
91
+ onInstanceDestroy(this.bvParent, this.$_handleDestroy)
99
92
  // Self destruct when toaster is destroyed
100
93
  this.listenOnRoot(getRootEventName(NAME_TOASTER, EVENT_NAME_DESTROYED), toaster => {
101
94
  /* istanbul ignore next: hard to test */
102
95
  if (toaster === this.toaster) {
103
- handleDestroy()
96
+ this.$_handleDestroy()
104
97
  }
105
98
  })
99
+ },
100
+ methods: {
101
+ // Override parent's onAfterLeave to self-destruct after hidden
102
+ onAfterLeave() {
103
+ // Call parent's implementation
104
+ BToast.options.methods.onAfterLeave.call(this)
105
+ // Self destruct after hidden
106
+ this.$_handleDestroy()
107
+ }
106
108
  }
107
109
  })
108
110
 
@@ -12,9 +12,9 @@ import {
12
12
  EVENT_NAME_HIDDEN,
13
13
  EVENT_NAME_HIDE,
14
14
  EVENT_NAME_SHOW,
15
- EVENT_NAME_SHOWN,
16
- HOOK_EVENT_NAME_DESTROYED
15
+ EVENT_NAME_SHOWN
17
16
  } from '../../../constants/events'
17
+ import { onInstanceDestroy } from '../../../utils/on-instance-destroy'
18
18
  import {
19
19
  PROP_TYPE_ARRAY_STRING,
20
20
  PROP_TYPE_NUMBER_STRING,
@@ -136,12 +136,8 @@ export const BVPopper = /*#__PURE__*/ extend({
136
136
  this.$_popper = null
137
137
  // Ensure we show as we mount
138
138
  this.localShow = true
139
- // Create popper instance before shown
140
- this.$on(EVENT_NAME_SHOW, el => {
141
- this.popperCreate(el)
142
- })
143
- // Self destruct handler
144
- const handleDestroy = () => {
139
+ // Self destruct handler (stored as instance property for use in afterLeave)
140
+ this.$_handleDestroy = () => {
145
141
  this.$nextTick(() => {
146
142
  // In a `requestAF()` to release control back to application
147
143
  requestAF(() => {
@@ -150,9 +146,7 @@ export const BVPopper = /*#__PURE__*/ extend({
150
146
  })
151
147
  }
152
148
  // Self destruct if parent destroyed
153
- this.bvParent.$once(HOOK_EVENT_NAME_DESTROYED, handleDestroy)
154
- // Self destruct after hidden
155
- this.$once(EVENT_NAME_HIDDEN, handleDestroy)
149
+ onInstanceDestroy(this.bvParent, this.$_handleDestroy)
156
150
  },
157
151
  beforeMount() {
158
152
  // Ensure that the attachment position is correct before mounting
@@ -236,10 +230,17 @@ export const BVPopper = /*#__PURE__*/ extend({
236
230
  props: { appear: true, noFade },
237
231
  on: {
238
232
  // Events used by parent component/instance
239
- beforeEnter: el => this.$emit(EVENT_NAME_SHOW, el),
233
+ beforeEnter: el => {
234
+ this.popperCreate(el)
235
+ this.$emit(EVENT_NAME_SHOW, el)
236
+ },
240
237
  afterEnter: el => this.$emit(EVENT_NAME_SHOWN, el),
241
238
  beforeLeave: el => this.$emit(EVENT_NAME_HIDE, el),
242
- afterLeave: el => this.$emit(EVENT_NAME_HIDDEN, el)
239
+ afterLeave: el => {
240
+ this.$emit(EVENT_NAME_HIDDEN, el)
241
+ // Self destruct after hidden
242
+ this.$_handleDestroy()
243
+ }
243
244
  }
244
245
  },
245
246
  [this.localShow ? this.renderTemplate(h) : h()]