@webitel/ui-sdk 24.4.39 → 24.6.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.
Files changed (46) hide show
  1. package/dist/ui-sdk.css +1 -1
  2. package/dist/ui-sdk.mjs +3439 -3353
  3. package/dist/ui-sdk.umd.js +11 -11
  4. package/package.json +4 -6
  5. package/src/components/transitions/wt-expand-transition.vue +63 -4
  6. package/src/components/wt-button/wt-button.vue +42 -6
  7. package/src/components/wt-expansion-panel/wt-expansion-panel.vue +0 -1
  8. package/src/components/wt-item-link/wt-item-link.vue +39 -9
  9. package/src/components/wt-player/wt-player.vue +8 -20
  10. package/src/components/wt-popup/__tests__/WtPopup.spec.js +5 -9
  11. package/src/components/wt-popup/_variables.scss +5 -0
  12. package/src/components/wt-popup/wt-popup.vue +88 -39
  13. package/src/components/wt-table/wt-table.vue +41 -6
  14. package/src/locale/en/en.js +0 -1
  15. package/src/locale/ru/ru.js +0 -1
  16. package/src/locale/ua/ua.js +0 -1
  17. package/src/modules/AuditForm/components/questions/options/__tests__/audit-form-question-options-write-row.spec.js +1 -1
  18. package/src/modules/AuditForm/components/questions/options/audit-form-question-options-write-row.vue +3 -15
  19. package/src/modules/AuditForm/components/questions/score/audit-form-question-score.vue +3 -6
  20. package/src/modules/Filters/classes/BaseFilterSchema.js +140 -7
  21. package/src/modules/Filters/components/__tests__/filter-pagination.spec.js +94 -0
  22. package/src/modules/Filters/components/__tests__/filter-table-fields.spec.js +71 -0
  23. package/src/modules/Filters/components/filter-pagination.vue +8 -8
  24. package/src/modules/Filters/components/filter-table-fields.vue +4 -11
  25. package/src/modules/Filters/composables/useTableFilters.js +2 -3
  26. package/src/modules/Filters/enums/FilterEvent.enum.js +6 -0
  27. package/src/modules/Filters/scripts/getters/index.js +9 -0
  28. package/src/modules/Filters/scripts/getters/localStorageGetter.js +17 -0
  29. package/src/modules/Filters/scripts/getters/queryGetter.js +10 -0
  30. package/src/modules/Filters/scripts/getters/valueGetter.js +9 -0
  31. package/src/modules/Filters/scripts/restores/index.js +7 -0
  32. package/src/modules/Filters/scripts/restores/localStorageRestore.js +7 -0
  33. package/src/modules/Filters/scripts/restores/queryRestore.js +7 -0
  34. package/src/modules/Filters/scripts/setters/index.js +9 -0
  35. package/src/modules/Filters/scripts/setters/localStorageSetter.js +16 -0
  36. package/src/modules/Filters/scripts/setters/querySetter.js +41 -0
  37. package/src/modules/Filters/scripts/setters/valueSetter.js +6 -0
  38. package/src/modules/Filters/scripts/utils/changeRouteQuery.js +17 -0
  39. package/src/modules/Filters/store/FiltersStoreModule.js +88 -139
  40. package/src/modules/Filters/store/__tests__/FiltersStoreModule.spec.js +157 -0
  41. package/src/modules/Notifications/store/__tests__/NotificationsStoreModule.actions.spec.js +1 -1
  42. package/src/modules/TableStoreModule/composables/useTableStore.js +13 -9
  43. package/src/modules/TableStoreModule/store/TableStoreModule.js +124 -106
  44. package/src/modules/TableStoreModule/store/__tests__/TableStoreModule.spec.js +241 -0
  45. package/src/modules/Filters/restores/filterFieldsRestore.js +0 -9
  46. package/src/modules/Filters/restores/readme.md +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "24.4.39",
3
+ "version": "24.6.1",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -40,7 +40,7 @@
40
40
  "@vuelidate/validators": "^2.0.4",
41
41
  "@vuepic/vue-datepicker": "^4.4.0",
42
42
  "@vueuse/components": "^10.7.2",
43
- "axios": "^1.6.8",
43
+ "axios": "^1.7.1",
44
44
  "clipboard-copy": "^4.0.1",
45
45
  "csv-stringify": "^5.5.3",
46
46
  "deep-copy": "^1.4.2",
@@ -51,6 +51,7 @@
51
51
  "jszip": "^3.5.0",
52
52
  "jszip-utils": "^0.1.0",
53
53
  "lodash": "^4.17.21",
54
+ "mitt": "^3.0.1",
54
55
  "plyr": "3.6.3",
55
56
  "query-string": "^8.1.0",
56
57
  "sortablejs": "^1.15.0",
@@ -70,7 +71,7 @@
70
71
  "eslint": "^8.53.0",
71
72
  "eslint-plugin-vue": "^9.18.1",
72
73
  "globby": "^14.0.0",
73
- "happy-dom": "^12.9.1",
74
+ "happy-dom": "^14.11.0",
74
75
  "markdown-it": "^13.0.2",
75
76
  "markdown-table": "^3.0.3",
76
77
  "postcss": "^8.4.31",
@@ -86,8 +87,5 @@
86
87
  "vitest": "^1.4.0",
87
88
  "vue": "^3.4.15",
88
89
  "vuex": "^4.1.0"
89
- },
90
- "overrides": {
91
- "axios": "^1.6.8"
92
90
  }
93
91
  }
@@ -1,13 +1,72 @@
1
1
  <template>
2
- <transition-expand>
2
+ <transition
3
+ name="expand"
4
+ @after-enter="afterEnter"
5
+ @enter="enter"
6
+ @leave="leave"
7
+ >
3
8
  <slot />
4
- </transition-expand>
9
+ </transition>
5
10
  </template>
6
11
 
7
- <script setup>
8
- import { TransitionExpand } from '@morev/vue-transitions';
12
+ <script>
13
+ /* eslint-disable no-param-reassign */
14
+ // https://markus.oberlehner.net/blog/transition-to-height-auto-with-vue/
15
+ export default {
16
+ name: 'WtExpandTransition',
17
+ methods: {
18
+ afterEnter(element) {
19
+ element.style.height = 'auto';
20
+ },
21
+ enter(element) {
22
+ const { width } = getComputedStyle(element);
23
+ element.style.width = width;
24
+ element.style.position = 'absolute';
25
+ element.style.visibility = 'hidden';
26
+ element.style.height = 'auto';
27
+
28
+ const { height } = getComputedStyle(element);
29
+ element.style.width = null;
30
+ element.style.position = null;
31
+ element.style.visibility = null;
32
+ element.style.height = 0;
33
+
34
+ // Force repaint to make sure the
35
+ // animation is triggered correctly.
36
+ // eslint-disable-next-line no-unused-expressions
37
+ getComputedStyle(element).height;
38
+
39
+ // eslint-disable-next-line no-return-assign
40
+ requestAnimationFrame(() => element.style.height = height);
41
+ },
42
+ leave(element) {
43
+ const { height } = getComputedStyle(element);
44
+ element.style.height = height;
45
+ // eslint-disable-next-line no-unused-expressions
46
+ getComputedStyle(element).height;
47
+
48
+ // eslint-disable-next-line no-return-assign
49
+ requestAnimationFrame(() => element.style.height = 0);
50
+ },
51
+ },
52
+ };
9
53
  </script>
10
54
 
11
55
  <style lang="scss">
56
+ .expand-enter-active,
57
+ .expand-leave-active {
58
+ transition: height var(--transition);
59
+ overflow: hidden;
60
+ }
12
61
 
62
+ .expand-enter,
63
+ .expand-leave-to {
64
+ height: 0;
65
+ }
66
+ /*
67
+ // expand animation optimization
68
+ transform: translateZ(0);
69
+ backface-visibility: hidden;
70
+ perspective: 1000px;
71
+ */
13
72
  </style>
@@ -8,21 +8,24 @@
8
8
  'wt-button--contains-icon': containsIcon,
9
9
  'wt-button--wide': wide,
10
10
  'wt-button--disabled': disabled,
11
- 'wt-button--loading': loading,
11
+ 'wt-button--loading': showLoader,
12
12
  }
13
13
  ]"
14
14
  type="button"
15
15
  :disabled="disabled"
16
16
  @click="$emit('click', $event)"
17
17
  >
18
+ <!-- Show loader and button contents at the same time to prevent width shift if content > min-width of button -->
18
19
  <wt-loader
19
- v-if="loading"
20
+ v-if="showLoader"
20
21
  size="sm"
21
22
  :color="loaderColor"
22
23
  />
23
- <slot v-else>
24
- no content provided
25
- </slot>
24
+ <div class="wt-button__contents">
25
+ <slot>
26
+ no content provided
27
+ </slot>
28
+ </div>
26
29
  </button>
27
30
  </template>
28
31
 
@@ -72,17 +75,35 @@ export default {
72
75
  },
73
76
  },
74
77
  emits: ['click'],
78
+ data: () => ({
79
+ showLoader: false,
80
+ }),
75
81
  computed: {
76
82
  colorClass() {
77
83
  if (!this.disabled) return `${this.color}`;
78
84
  return '';
79
85
  },
80
86
  loaderColor() {
81
- return 'main';
87
+ return 'on-dark';
82
88
  // if (['success', 'transfer', 'error', 'job'].includes(this.color)) return 'on-dark';
83
89
  // return 'on-light';
84
90
  },
85
91
  },
92
+ watch: {
93
+ loading: {
94
+ immediate: true,
95
+ handler(value) {
96
+ if (value) {
97
+ this.showLoader = true;
98
+ } else {
99
+ setTimeout(() => {
100
+ this.showLoader = value;
101
+ }, 1000); // why 1s? https://ux.stackexchange.com/a/104782
102
+ }
103
+ },
104
+ },
105
+
106
+ },
86
107
  };
87
108
  </script>
88
109
 
@@ -105,6 +126,17 @@ export default {
105
126
  transition: var(--transition);
106
127
  cursor: pointer;
107
128
 
129
+ &__contents {
130
+ display: contents;
131
+ }
132
+
133
+ .wt-loader {
134
+ position: absolute;
135
+ top: 50%;
136
+ left: 50%;
137
+ transform: translate(-50%, -50%);
138
+ }
139
+
108
140
  &--wide {
109
141
  width: 100%;
110
142
  }
@@ -116,6 +148,10 @@ export default {
116
148
 
117
149
  &--loading {
118
150
  pointer-events: none;
151
+
152
+ .wt-button__contents {
153
+ visibility: hidden;
154
+ }
119
155
  }
120
156
 
121
157
  &--size {
@@ -36,7 +36,6 @@
36
36
 
37
37
  <script setup>
38
38
  import { ref, watch } from 'vue';
39
- import WtExpandTransition from '../transitions/wt-expand-transition.vue';
40
39
 
41
40
  const props = defineProps({
42
41
  size: {
@@ -1,8 +1,11 @@
1
1
  <template>
2
2
  <router-link
3
+ :class="{
4
+ 'wt-item-link--disabled': disabled,
5
+ 'wt-item-link--invisible': invisible,
6
+ }"
3
7
  :target="target"
4
8
  :to="to"
5
- :class="{ 'wt-item-link--disabled': disabled }"
6
9
  class="wt-item-link"
7
10
  >
8
11
  <slot />
@@ -17,16 +20,40 @@ const props = defineProps({
17
20
  type: [String, Object],
18
21
  default: '',
19
22
  },
23
+
20
24
  target: {
21
25
  type: String,
22
26
  default: '_self',
23
27
  },
28
+
29
+ /**
30
+ * DEPRECAPTED, use :link, or make a wrapper component
31
+ *
32
+ * @deprecated
33
+ */
24
34
  routeName: {
25
35
  type: String,
36
+ default: '',
26
37
  },
38
+
39
+ /**
40
+ * DEPRECAPTED, use :link, or make a wrapper component
41
+ *
42
+ * @deprecated
43
+ */
27
44
  id: {
28
45
  type: [String, Number],
46
+ default: '',
47
+ },
48
+
49
+ /**
50
+ * Hide styles
51
+ */
52
+ invisible: {
53
+ type: Boolean,
54
+ default: false,
29
55
  },
56
+
30
57
  disabled: {
31
58
  type: Boolean,
32
59
  default: false,
@@ -34,9 +61,9 @@ const props = defineProps({
34
61
  });
35
62
 
36
63
  const to = computed(() => props.link || {
37
- name: `${props.routeName}-edit`,
38
- params: { id: props.id },
39
- });
64
+ name: `${props.routeName}-edit`,
65
+ params: { id: props.id },
66
+ });
40
67
  </script>
41
68
 
42
69
  <style lang="scss">
@@ -47,12 +74,15 @@ const to = computed(() => props.link || {
47
74
  .wt-item-link {
48
75
  display: flex;
49
76
  align-items: center;
50
- cursor: pointer;
51
- color: var(--wt-item-link-text-color);
52
- transition: var(--transition);
53
77
 
54
- &:hover {
55
- text-decoration: underline;
78
+ :not(&--invisible) {
79
+ cursor: pointer;
80
+ transition: var(--transition);
81
+ color: var(--wt-item-link-text-color);
82
+
83
+ &:hover {
84
+ text-decoration: underline;
85
+ }
56
86
  }
57
87
 
58
88
  &--disabled {
@@ -41,10 +41,6 @@ export default {
41
41
  type: Boolean,
42
42
  default: false,
43
43
  },
44
- hideDuration: {
45
- type: Boolean,
46
- default: false,
47
- },
48
44
  download: {
49
45
  type: [String, Function, Boolean],
50
46
  default: () => (url) => url.replace('/stream', '/download'),
@@ -111,17 +107,11 @@ export default {
111
107
  const baseURL = this.$baseURL || process.env.BASE_URL || import.meta.env.BASE_URL;
112
108
  const iconUrl = createPlyrURL(baseURL);
113
109
  if (this.player) this.player.destroy();
114
-
115
- const defaultControls = [
110
+ const controls = [
116
111
  'play-large', 'play', 'progress', 'current-time',
117
- 'duration', 'mute', 'volume', 'captions', 'settings',
118
- 'pip', 'airplay', 'fullscreen'
112
+ 'mute', 'volume', 'captions', 'settings', 'pip',
113
+ 'airplay', 'fullscreen',
119
114
  ];
120
-
121
- const controls = this.hideDuration
122
- ? defaultControls.filter(control => control !== 'duration')
123
- : defaultControls;
124
-
125
115
  if (this.download) controls.push('download');
126
116
  this.player = new Plyr(this.$refs.player, {
127
117
  // this.player = new Plyr('.wt-player__player', {
@@ -196,15 +186,13 @@ export default {
196
186
  box-shadow: var(--elevation-10);
197
187
  }
198
188
 
199
- .plyr__controls > .plyr__control,
200
- .plyr__controls > .plyr__controls__item > .plyr__control {
189
+ .plyr__controls .plyr__control {
201
190
  padding: var(--plyr-controls-icon-padding);
202
- }
203
191
 
204
- .plyr__controls > .plyr__control > svg,
205
- .plyr__controls > .plyr__controls__item > .plyr__control > svg {
206
- height: var(--plyr-controls-icon-size);
207
- width: var(--plyr-controls-icon-size);
192
+ svg {
193
+ height: var(--plyr-controls-icon-size);
194
+ width: var(--plyr-controls-icon-size);
195
+ }
208
196
  }
209
197
 
210
198
  .plyr__control--overlaid svg {
@@ -1,4 +1,4 @@
1
- import { shallowMount } from '@vue/test-utils';
1
+ import { shallowMount, mount } from '@vue/test-utils';
2
2
  import WtPopup from '../wt-popup.vue';
3
3
 
4
4
  describe('WtPopup', () => {
@@ -11,8 +11,7 @@ describe('WtPopup', () => {
11
11
 
12
12
  it('renders popup header via header slot', () => {
13
13
  const content = 'Popup header';
14
- const wrapper = shallowMount(WtPopup, {
15
- stubs: { WtIconBtn: true },
14
+ const wrapper = mount(WtPopup, {
16
15
  slots: { header: content },
17
16
  });
18
17
  expect(wrapper.find('.wt-popup__header').text()).toBe(content);
@@ -20,8 +19,7 @@ describe('WtPopup', () => {
20
19
 
21
20
  it('renders popup header via title slot', () => {
22
21
  const content = 'Popup title';
23
- const wrapper = shallowMount(WtPopup, {
24
- stubs: { WtIconBtn: true },
22
+ const wrapper = mount(WtPopup, {
25
23
  slots: { title: content },
26
24
  });
27
25
  expect(wrapper.find('.wt-popup__title').text()).toBe(content);
@@ -29,8 +27,7 @@ describe('WtPopup', () => {
29
27
 
30
28
  it('renders popup main via main slot', () => {
31
29
  const content = 'Popup main content';
32
- const wrapper = shallowMount(WtPopup, {
33
- stubs: { WtIconBtn: true },
30
+ const wrapper = mount(WtPopup, {
34
31
  slots: { main: content },
35
32
  });
36
33
  expect(wrapper.find('.wt-popup__main').text()).toBe(content);
@@ -38,8 +35,7 @@ describe('WtPopup', () => {
38
35
 
39
36
  it('renders popup actions via actions slot', () => {
40
37
  const content = 'Popup actions';
41
- const wrapper = shallowMount(WtPopup, {
42
- stubs: { WtIconBtn: true },
38
+ const wrapper = mount(WtPopup, {
43
39
  slots: { actions: content },
44
40
  });
45
41
  expect(wrapper.find('.wt-popup__actions').text()).toBe(content);
@@ -5,6 +5,11 @@
5
5
  --popup-shadow-opacity: 0.8;
6
6
  --wt-popup-shadow-color: hsla(0, 0%, 0%, var(--popup-shadow-opacity));
7
7
 
8
+ --wt-popup-size-xs: 300px;
9
+ --wt-popup-size-sm: 500px;
10
+ --wt-popup-size-md: 800px;
11
+ --wt-popup-size-lg: 1140px;
12
+
8
13
  --popup-max-height: 90%;
9
14
 
10
15
  --wt-popup-background-color: var(--dp-24-surface-color);
@@ -1,49 +1,65 @@
1
1
  <template>
2
2
  <div
3
- :class="{ 'wt-popup--overflow': overflow }"
3
+ v-show="wrapperShown"
4
+ :class="[
5
+ `wt-popup--size-${size}`,
6
+ { 'wt-popup--overflow': overflow }
7
+ ]"
4
8
  class="wt-popup"
5
9
  >
6
- <aside
7
- :style="popupStyle"
8
- class="wt-popup__popup"
10
+ <transition-slide
11
+ :offset="[0, -1440/2]"
9
12
  >
10
- <header class="wt-popup__header">
11
- <slot name="header">
12
- <h3 class="wt-popup__title">
13
- <slot name="title" />
14
- </h3>
15
- </slot>
16
- <wt-icon-btn
17
- class="wt-popup__close-btn"
18
- icon="close"
19
- @click="$emit('close')"
20
- />
21
- </header>
22
- <section
23
- v-if="$slots.main"
24
- class="wt-popup__main"
13
+ <aside
14
+ v-if="shown"
15
+ class="wt-popup__popup"
25
16
  >
26
- <slot name="main" />
27
- </section>
28
- <footer
29
- v-if="$slots.actions"
30
- class="wt-popup__actions"
31
- >
32
- <slot name="actions" />
33
- </footer>
34
- </aside>
17
+ <header class="wt-popup__header">
18
+ <slot name="header">
19
+ <h3 class="wt-popup__title">
20
+ <slot name="title" />
21
+ </h3>
22
+ </slot>
23
+ <wt-icon-btn
24
+ class="wt-popup__close-btn"
25
+ icon="close"
26
+ @click="$emit('close')"
27
+ />
28
+ </header>
29
+ <section
30
+ v-if="$slots.main"
31
+ class="wt-popup__main"
32
+ >
33
+ <slot name="main" />
34
+ </section>
35
+ <footer
36
+ v-if="$slots.actions"
37
+ class="wt-popup__actions"
38
+ >
39
+ <slot name="actions" />
40
+ </footer>
41
+ </aside>
42
+ </transition-slide>
35
43
  </div>
36
44
  </template>
37
45
 
38
46
  <script>
47
+ import { TransitionSlide } from '@morev/vue-transitions';
48
+
39
49
  export default {
40
50
  name: 'WtPopup',
51
+ components: {
52
+ TransitionSlide,
53
+ },
41
54
  props: {
42
- minWidth: {
43
- type: [Number, String],
55
+ shown: {
56
+ type: Boolean,
57
+ default: true, // TODO: change me to false after refactor
44
58
  },
45
- width: {
46
- type: [Number, String],
59
+ size: {
60
+ type: String,
61
+ default: 'md',
62
+ validator: (v) => ['xs', 'sm', 'md', 'lg'].includes(v),
47
63
  },
48
64
  overflow: {
49
65
  type: Boolean,
@@ -51,12 +67,21 @@ export default {
51
67
  },
52
68
  },
53
69
  emits: ['close'],
54
- computed: {
55
- popupStyle() {
56
- let style = '';
57
- style += this.minWidth ? `min-width: ${this.minWidth}px;` : '';
58
- style += this.width ? `width: ${this.width}px;` : '';
59
- return style;
70
+ data: () => ({
71
+ wrapperShown: false,
72
+ }),
73
+ watch: {
74
+ // overlay should be shown before popup to show animation properly
75
+ shown: {
76
+ handler(value) {
77
+ if (value) {
78
+ this.wrapperShown = true;
79
+ } else {
80
+ setTimeout(() => {
81
+ this.wrapperShown = value;
82
+ }, 200); // 200 -> 0.2s css var(--transition); duration
83
+ }
84
+ }, immediate: true,
60
85
  },
61
86
  },
62
87
  };
@@ -79,6 +104,30 @@ export default {
79
104
  align-items: center;
80
105
  justify-content: center;
81
106
  background: var(--wt-popup-shadow-color);
107
+
108
+ &--size {
109
+ &-xs {
110
+ .wt-popup__popup {
111
+ width: var(--wt-popup-size-xs);
112
+ }
113
+ }
114
+ &-sm {
115
+ .wt-popup__popup {
116
+ width: var(--wt-popup-size-sm);
117
+ }
118
+ }
119
+ &-md {
120
+ .wt-popup__popup {
121
+ width: var(--wt-popup-size-md);
122
+ }
123
+ }
124
+ &-lg {
125
+ .wt-popup__popup {
126
+ width: var(--wt-popup-size-lg);
127
+ }
128
+ }
129
+
130
+ }
82
131
  }
83
132
 
84
133
  .wt-popup__popup {
@@ -100,8 +149,8 @@ export default {
100
149
  display: flex;
101
150
  align-items: center;
102
151
  justify-content: space-between;
103
- color: var(--wt-popup-header-text-color);
104
152
  padding: var(--popup-header-padding);
153
+ color: var(--wt-popup-header-text-color);
105
154
  border-radius: var(--border-radius);
106
155
  background: var(--wt-popup-header-background-color);
107
156
  gap: var(--popup-header-padding);
@@ -63,8 +63,8 @@
63
63
  class="wt-table__td wt-table__td--checkbox"
64
64
  >
65
65
  <wt-checkbox
66
- :selected="row._isSelected"
67
- @change="row._isSelected = !row._isSelected"
66
+ :selected="_selected.includes(row)"
67
+ @change="handleSelection(row, $event)"
68
68
  />
69
69
  </td>
70
70
 
@@ -147,17 +147,30 @@ export default {
147
147
  type: Boolean,
148
148
  default: true,
149
149
  },
150
+ selected: {
151
+ type: Array,
152
+ // no default! because we need to know if it's passed or not
153
+ },
150
154
  gridActions: {
151
155
  type: Boolean,
152
156
  default: true,
153
157
  },
154
158
  },
159
+ emits: [
160
+ 'sort',
161
+ 'update:selected',
162
+ ],
155
163
 
156
164
  data: () => ({}),
157
165
 
158
166
  computed: {
167
+ _selected() {
168
+ // _isSelected for backwards compatibility
169
+ return this.selected || this.data.filter((item) => item._isSelected);
170
+ },
171
+
159
172
  isAllSelected() {
160
- return this.data.every((item) => item._isSelected);
173
+ return this._selected.length === this.data.length && this.data.length > 0;
161
174
  },
162
175
 
163
176
  dataHeaders() {
@@ -206,11 +219,33 @@ export default {
206
219
  return this.sortable && sort !== undefined;
207
220
  },
208
221
  selectAll() {
209
- const { isAllSelected } = this;
210
- // eslint-disable-next-line no-param-reassign,no-return-assign
211
- this.data.forEach((item) => item._isSelected = !isAllSelected);
222
+ if (this.selected) {
223
+ if (this.isAllSelected) {
224
+ this.$emit('update:selected', []);
225
+ } else {
226
+ this.$emit('update:selected', [...this.data]);
227
+ }
228
+ } else {
229
+ // for backwards compatibility
230
+ this.data.forEach((item) => {
231
+ item._isSelected = !this.isAllSelected;
232
+ });
233
+ }
212
234
  },
235
+ handleSelection(row, remove) {
236
+ if (this.selected) {
237
+ if (remove) {
238
+ this.$emit('update:selected', this._selected.filter((item) => item !== row));
239
+ } else {
240
+ this.$emit('update:selected', [...this._selected, row]);
241
+ }
242
+ } else {
243
+ // for backwards compatibility
244
+ row._isSelected = !row._isSelected;
245
+ }
246
+ }
213
247
  },
248
+
214
249
  };
215
250
  </script>
216
251
 
@@ -344,7 +344,6 @@ export default {
344
344
  score: 'Score',
345
345
  },
346
346
  clearSelection: 'Clear selection',
347
- scoreInputTooltip: 'Value should be more than {min} and less than {max}',
348
347
  },
349
348
  deleteConfirmationPopup: {
350
349
  title: 'Confirm deletion',
@@ -342,7 +342,6 @@ export default {
342
342
  score: 'Бал',
343
343
  },
344
344
  clearSelection: 'Очистить выбор',
345
- scoreInputTooltip: 'Значение должно быть не меньше {min} и не больше {max}',
346
345
  },
347
346
  deleteConfirmationPopup: {
348
347
  title: 'Подтвердите удаление',
@@ -342,7 +342,6 @@ export default {
342
342
  score: 'Бал',
343
343
  },
344
344
  clearSelection: 'Очистити вибір',
345
- scoreInputTooltip: 'Значення повинно бути не менше {min} та не більше {max}',
346
345
  },
347
346
  deleteConfirmationPopup: {
348
347
  title: 'Підтвердіть видалення',