@webitel/ui-sdk 24.4.31 → 24.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "24.4.31",
3
+ "version": "24.6.0",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -35,6 +35,7 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@floating-ui/vue": "^1.0.1",
38
+ "@morev/vue-transitions": "^3.0.2",
38
39
  "@vuelidate/core": "^2.0.3",
39
40
  "@vuelidate/validators": "^2.0.4",
40
41
  "@vuepic/vue-datepicker": "^4.4.0",
@@ -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 {
@@ -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 {
@@ -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);
@@ -111,7 +111,7 @@ export default class FiltersStoreModule extends BaseStoreModule {
111
111
  // note: restore fn may still return value even if there's no query value
112
112
  // from localStorage, for instance
113
113
  const value = context.state[filter].restore
114
- ? context.state[filter].restore({ query })
114
+ ? context.state[filter].restore({ query, defaultValue: context.state[filter].defaultValue })
115
115
  : query;
116
116
  if (value) {
117
117
  if (filter