@webitel/ui-sdk 24.12.61 → 24.12.63
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/CHANGELOG.md +30 -0
- package/dist/img/sprite/comment.svg +5 -0
- package/dist/img/sprite/index.js +4 -0
- package/dist/img/sprite/tree-corner.svg +3 -0
- package/dist/ui-sdk.css +1 -1
- package/dist/ui-sdk.js +6648 -6465
- package/dist/ui-sdk.umd.cjs +16 -16
- package/package.json +1 -1
- package/src/assets/icons/sprite/comment.svg +5 -0
- package/src/assets/icons/sprite/index.js +4 -0
- package/src/assets/icons/sprite/tree-corner.svg +3 -0
- package/src/components/index.js +12 -10
- package/src/components/wt-table/wt-table.vue +10 -1
- package/src/components/wt-tree/__tests__/WtTree.spec.js +13 -0
- package/src/components/wt-tree/types/wt-tree-mode.ts +4 -0
- package/src/components/wt-tree/wt-tree.vue +178 -0
- package/src/components/wt-tree-line/_variables.scss +21 -0
- package/src/components/wt-tree-line/types/wt-tree-nested-icons.ts +4 -0
- package/src/components/wt-tree-line/wt-tree-line.vue +222 -0
package/package.json
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M6.80054 10C6.80054 9.44772 7.24825 9 7.80054 9H15.8005C16.3528 9 16.8005 9.44772 16.8005 10C16.8005 10.5523 16.3528 11 15.8005 11H7.80054C7.24825 11 6.80054 10.5523 6.80054 10Z"/>
|
|
3
|
+
<path d="M7.80054 13C7.24825 13 6.80054 13.4477 6.80054 14C6.80054 14.5523 7.24825 15 7.80054 15H15.8005C16.3528 15 16.8005 14.5523 16.8005 14C16.8005 13.4477 16.3528 13 15.8005 13H7.80054Z"/>
|
|
4
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.14676 21.3111C11.7353 22.7173 15.9725 21.9707 18.8721 19.0711C22.7773 15.1658 22.7773 8.83418 18.8721 4.92893C14.9669 1.02369 8.6352 1.02369 4.72996 4.92893C1.83037 7.82852 1.08368 12.0658 2.4899 15.6543C2.80314 16.4536 2.47133 18.8022 2.19383 20.3804C2.06808 21.0955 2.7055 21.7329 3.42064 21.6072C4.9988 21.3297 7.34742 20.9979 8.14676 21.3111ZM17.4579 6.34315C14.3337 3.21895 9.26837 3.21895 6.14417 6.34315C3.8267 8.66062 3.22539 12.0495 4.35203 14.9246C4.56348 15.4641 4.60441 16.0523 4.61423 16.4598C4.62533 16.9206 4.59833 17.4201 4.55556 17.9021C4.51101 18.4041 4.44555 18.9274 4.37293 19.4281C4.87365 19.3555 5.39689 19.29 5.89895 19.2455C6.38088 19.2027 6.88042 19.1757 7.34117 19.1868C7.74876 19.1966 8.33689 19.2375 8.87647 19.449C11.7515 20.5756 15.1404 19.9743 17.4579 17.6569C20.5821 14.5327 20.5821 9.46734 17.4579 6.34315Z"/>
|
|
5
|
+
</svg>
|
|
@@ -63,6 +63,7 @@ import close from './close.svg';
|
|
|
63
63
|
import closeFilled from './close--filled.svg';
|
|
64
64
|
import collapse from './collapse.svg';
|
|
65
65
|
import columnSelect from './column-select.svg';
|
|
66
|
+
import comment from './comment.svg';
|
|
66
67
|
import conference from './conference.svg';
|
|
67
68
|
import contacts from './contacts.svg';
|
|
68
69
|
import copy from './copy.svg';
|
|
@@ -179,6 +180,7 @@ import sttSearch from './stt-search.svg';
|
|
|
179
180
|
import telegramBot from './telegram-bot.svg';
|
|
180
181
|
import tick from './tick.svg';
|
|
181
182
|
import treeCollapse from './tree-collapse.svg';
|
|
183
|
+
import treeCorner from './tree-corner.svg';
|
|
182
184
|
import treeCross from './tree-cross.svg';
|
|
183
185
|
import treeExpand from './tree-expand.svg';
|
|
184
186
|
import treeLine from './tree-line.svg';
|
|
@@ -268,6 +270,7 @@ export default objCamelToKebab({
|
|
|
268
270
|
minus,
|
|
269
271
|
collapse,
|
|
270
272
|
columnSelect,
|
|
273
|
+
comment,
|
|
271
274
|
variableSelect,
|
|
272
275
|
contacts,
|
|
273
276
|
copy,
|
|
@@ -354,6 +357,7 @@ export default objCamelToKebab({
|
|
|
354
357
|
zoomOut,
|
|
355
358
|
ttsDownload,
|
|
356
359
|
treeLine,
|
|
360
|
+
treeCorner,
|
|
357
361
|
treeCross,
|
|
358
362
|
stt,
|
|
359
363
|
sttDownload,
|
package/src/components/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import WtNavigationMenu from './on-demand/wt-navigation-menu/components/wt-navigation-menu.vue';
|
|
2
|
+
import WtSelectionPopup from './on-demand/wt-selection-popup/wt-selection-popup.vue';
|
|
3
|
+
import WtStartPage from './on-demand/wt-start-page/components/wt-start-page.vue';
|
|
1
4
|
import WtActionBar from './wt-action-bar/wt-action-bar.vue';
|
|
2
5
|
import WtAppHeader from './wt-app-header/wt-app-header.vue';
|
|
3
6
|
import WtAppNavigator from './wt-app-header/wt-app-navigator.vue';
|
|
4
7
|
import WtHeaderActions from './wt-app-header/wt-header-actions.vue';
|
|
5
8
|
import WtAvatar from './wt-avatar/wt-avatar.vue';
|
|
6
9
|
import WtBadge from './wt-badge/wt-badge.vue';
|
|
7
|
-
import WtButtonSelect from './wt-button-select/wt-button-select.vue';
|
|
8
10
|
import WtButton from './wt-button/wt-button.vue';
|
|
11
|
+
import WtButtonSelect from './wt-button-select/wt-button-select.vue';
|
|
9
12
|
import WtCheckbox from './wt-checkbox/wt-checkbox.vue';
|
|
10
13
|
import WtChip from './wt-chip/wt-chip.vue';
|
|
11
14
|
import WtConfirmDialog from './wt-confirm-dialog/wt-confirm-dialog.vue';
|
|
@@ -13,21 +16,22 @@ import WtContextMenu from './wt-context-menu/wt-context-menu.vue';
|
|
|
13
16
|
import WtCopyAction from './wt-copy-action/wt-copy-action.vue';
|
|
14
17
|
import WtDatepicker from './wt-datepicker/wt-datepicker.vue';
|
|
15
18
|
import WtDivider from './wt-divider/wt-divider.vue';
|
|
19
|
+
import WtDualPanel from './wt-dual-panel/wt-dual-panel.vue';
|
|
16
20
|
import WtDummy from './wt-dummy/wt-dummy.vue';
|
|
17
21
|
import WtEmpty from './wt-empty/wt-empty.vue';
|
|
18
22
|
import WtErrorPage from './wt-error-page/wt-error-page.vue';
|
|
19
23
|
import WtExpansionPanel from './wt-expansion-panel/wt-expansion-panel.vue';
|
|
20
24
|
import WtFiltersPanelWrapper from './wt-filters-panel-wrapper/wt-filters-panel-wrapper.vue';
|
|
21
|
-
import WtHeadlineNav from './wt-headline-nav/wt-headline-nav.vue';
|
|
22
25
|
import WtHeadline from './wt-headline/wt-headline.vue';
|
|
26
|
+
import WtHeadlineNav from './wt-headline-nav/wt-headline-nav.vue';
|
|
23
27
|
import WtHint from './wt-hint/wt-hint.vue';
|
|
28
|
+
import WtIcon from './wt-icon/wt-icon.vue';
|
|
24
29
|
import WtIconAction from './wt-icon-action/wt-icon-action.vue';
|
|
25
30
|
import WtIconBtn from './wt-icon-btn/wt-icon-btn.vue';
|
|
26
|
-
import WtIcon from './wt-icon/wt-icon.vue';
|
|
27
31
|
import WtImage from './wt-image/wt-image.vue';
|
|
28
32
|
import WtIndicator from './wt-indicator/wt-indicator.vue';
|
|
29
|
-
import WtInputInfo from './wt-input-info/wt-input-info.vue';
|
|
30
33
|
import WtInput from './wt-input/wt-input.vue';
|
|
34
|
+
import WtInputInfo from './wt-input-info/wt-input-info.vue';
|
|
31
35
|
import WtIntersectionObserver from './wt-intersection-observer/wt-intersection-observer.vue';
|
|
32
36
|
import WtItemLink from './wt-item-link/wt-item-link.vue';
|
|
33
37
|
import WtLabel from './wt-label/wt-label.vue';
|
|
@@ -39,7 +43,6 @@ import WtNotification from './wt-notification/wt-notification.vue';
|
|
|
39
43
|
import WtNotificationsBar from './wt-notifications-bar/wt-notifications-bar.vue';
|
|
40
44
|
import WtPageHeader from './wt-page-header/wt-page-header.vue';
|
|
41
45
|
import WtPageWrapper from './wt-page-wrapper/wt-page-wrapper.vue';
|
|
42
|
-
import WtDualPanel from './wt-dual-panel/wt-dual-panel.vue';
|
|
43
46
|
import WtPagination from './wt-pagination/wt-pagination.vue';
|
|
44
47
|
import WtPlayer from './wt-player/wt-player.vue';
|
|
45
48
|
import WtPopup from './wt-popup/wt-popup.vue';
|
|
@@ -52,19 +55,17 @@ import WtSlider from './wt-slider/wt-slider.vue';
|
|
|
52
55
|
import WtStatusSelect from './wt-status-select/wt-status-select.vue';
|
|
53
56
|
import WtStepper from './wt-stepper/wt-stepper.vue';
|
|
54
57
|
import WtSwitcher from './wt-switcher/wt-switcher.vue';
|
|
58
|
+
import WtTable from './wt-table/wt-table.vue';
|
|
55
59
|
import WtTableActions from './wt-table-actions/wt-table-actions.vue';
|
|
56
60
|
import WtTableColumnSelect from './wt-table-column-select/wt-table-column-select.vue';
|
|
57
|
-
import WtTable from './wt-table/wt-table.vue';
|
|
58
|
-
import WtTreeTable from './wt-tree-table/wt-tree-table.vue';
|
|
59
61
|
import WtTabs from './wt-tabs/wt-tabs.vue';
|
|
60
62
|
import WtTagsInput from './wt-tags-input/wt-tags-input.vue';
|
|
61
63
|
import WtTextarea from './wt-textarea/wt-textarea.vue';
|
|
62
64
|
import WtTimeInput from './wt-time-input/wt-time-input.vue';
|
|
63
65
|
import WtTimepicker from './wt-timepicker/wt-timepicker.vue';
|
|
64
66
|
import WtTooltip from './wt-tooltip/wt-tooltip.vue';
|
|
65
|
-
import
|
|
66
|
-
import
|
|
67
|
-
import WtSelectionPopup from './on-demand/wt-selection-popup/wt-selection-popup.vue';
|
|
67
|
+
import WtTree from './wt-tree/wt-tree.vue';
|
|
68
|
+
import WtTreeTable from './wt-tree-table/wt-tree-table.vue';
|
|
68
69
|
|
|
69
70
|
const Components = {
|
|
70
71
|
WtActionBar,
|
|
@@ -118,6 +119,7 @@ const Components = {
|
|
|
118
119
|
WtPlayer,
|
|
119
120
|
WtStatusSelect,
|
|
120
121
|
WtTable,
|
|
122
|
+
WtTree,
|
|
121
123
|
WtTreeTable,
|
|
122
124
|
WtTableActions,
|
|
123
125
|
WtTableColumnSelect,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="wt-table">
|
|
3
3
|
<table class="wt-table__table">
|
|
4
|
-
<thead
|
|
4
|
+
<thead
|
|
5
|
+
v-if="!headless"
|
|
6
|
+
class="wt-table__head">
|
|
5
7
|
<tr
|
|
6
8
|
:style="columnsStyle"
|
|
7
9
|
class="wt-table__tr wt-table__tr__head"
|
|
@@ -183,6 +185,13 @@ export default {
|
|
|
183
185
|
type: Boolean,
|
|
184
186
|
default: true,
|
|
185
187
|
},
|
|
188
|
+
/**
|
|
189
|
+
* 'If true, displays table without header.'
|
|
190
|
+
*/
|
|
191
|
+
headless: {
|
|
192
|
+
type: Boolean,
|
|
193
|
+
default: false,
|
|
194
|
+
},
|
|
186
195
|
},
|
|
187
196
|
emits: ['sort', 'update:selected'],
|
|
188
197
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { mount, shallowMount } from '@vue/test-utils';
|
|
2
|
+
import WtTree from '../wt-tree.vue';
|
|
3
|
+
|
|
4
|
+
describe('WtTree', () => {
|
|
5
|
+
it('renders a component', () => {
|
|
6
|
+
const wrapper = shallowMount(WtTree, {
|
|
7
|
+
stubs: {
|
|
8
|
+
WtTree: true,
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
expect(wrapper.classes('wt-tree')).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="wt-tree">
|
|
3
|
+
<div
|
|
4
|
+
v-if="mode === WtTreeMode.TREE"
|
|
5
|
+
class="wt-tree__content"
|
|
6
|
+
>
|
|
7
|
+
<wt-tree-line
|
|
8
|
+
v-for="(item, index) in data"
|
|
9
|
+
:key="index"
|
|
10
|
+
:model-value="modelValue"
|
|
11
|
+
:item-label="itemLabel"
|
|
12
|
+
:item-data="itemData"
|
|
13
|
+
:data="item"
|
|
14
|
+
:children-prop="childrenProp"
|
|
15
|
+
@update:model-value="emit('update:modelValue', $event)"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
<div
|
|
19
|
+
v-if="mode === WtTreeMode.LIST"
|
|
20
|
+
class="wt-tree__list-content"
|
|
21
|
+
>
|
|
22
|
+
<span
|
|
23
|
+
v-for="(item, index) in allData"
|
|
24
|
+
:key="index"
|
|
25
|
+
class="wt-tree__label-wrapper"
|
|
26
|
+
:class="{ active: compareSelectElement(item) }"
|
|
27
|
+
>
|
|
28
|
+
<p
|
|
29
|
+
class="wt-tree__label"
|
|
30
|
+
@click="selectElement(item)"
|
|
31
|
+
>
|
|
32
|
+
{{ itemLabel ? item[itemLabel] : item }}
|
|
33
|
+
</p>
|
|
34
|
+
<wt-icon
|
|
35
|
+
v-if="compareSelectElement(item)"
|
|
36
|
+
icon="chat-message-status-sent"
|
|
37
|
+
class="wt-tree__label-icon"
|
|
38
|
+
/>
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script setup lang="ts">
|
|
45
|
+
import deepEqual from 'deep-equal';
|
|
46
|
+
import { computed } from 'vue';
|
|
47
|
+
|
|
48
|
+
import WtTreeLine from '../wt-tree-line/wt-tree-line.vue';
|
|
49
|
+
import { WtTreeMode } from './types/wt-tree-mode.ts';
|
|
50
|
+
|
|
51
|
+
const props = withDefaults(
|
|
52
|
+
defineProps<{
|
|
53
|
+
/**
|
|
54
|
+
* Selected element, it can be an object or a string, related to itemData props
|
|
55
|
+
*/
|
|
56
|
+
modelValue?: null | any;
|
|
57
|
+
/**
|
|
58
|
+
* You need to pass an array of objects that will be displayed in the tree
|
|
59
|
+
*/
|
|
60
|
+
data: any[];
|
|
61
|
+
/**
|
|
62
|
+
* You can pass the name of the property that will be used as the label of the selected item
|
|
63
|
+
*/
|
|
64
|
+
itemLabel?: string;
|
|
65
|
+
/**
|
|
66
|
+
* You can pass the name of the property that will be used as the value of the selected item
|
|
67
|
+
*/
|
|
68
|
+
itemData?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Select mode for display tree data
|
|
71
|
+
* @example 'tree', 'list'
|
|
72
|
+
*/
|
|
73
|
+
mode?: string;
|
|
74
|
+
/**
|
|
75
|
+
* You can pass the name of the property that will be used for getting children elements
|
|
76
|
+
*/
|
|
77
|
+
childrenProp?: string;
|
|
78
|
+
}>(),
|
|
79
|
+
{
|
|
80
|
+
childrenProp: 'children',
|
|
81
|
+
mode: WtTreeMode.TREE,
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const emit = defineEmits<{
|
|
86
|
+
(e: 'select', data: any): void;
|
|
87
|
+
(e: 'update:modelValue', value: any): void;
|
|
88
|
+
}>();
|
|
89
|
+
|
|
90
|
+
const allData = computed(() => {
|
|
91
|
+
const result = [];
|
|
92
|
+
|
|
93
|
+
const getNestedItems = (item: any) => {
|
|
94
|
+
result.push(item);
|
|
95
|
+
|
|
96
|
+
if (item[props.childrenProp]) {
|
|
97
|
+
item[props.childrenProp].forEach((child: any) => {
|
|
98
|
+
getNestedItems(child);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
props.data.forEach((item) => {
|
|
104
|
+
getNestedItems(item);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return result;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const selectElement = (item: any) => {
|
|
111
|
+
emit('update:modelValue', props.itemData ? item[props.itemData] : item);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const compareSelectElement = (item: any) => {
|
|
115
|
+
if (props.itemData) {
|
|
116
|
+
return item[props.itemData] === props.modelValue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return deepEqual(props.modelValue, item);
|
|
120
|
+
};
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<style lang="scss">
|
|
124
|
+
@use '../../css/main.scss' as *;
|
|
125
|
+
|
|
126
|
+
.wt-tree {
|
|
127
|
+
padding: var(--spacing-sm);
|
|
128
|
+
background: var(--content-wrapper-color);
|
|
129
|
+
border-radius: var(--border-radius);
|
|
130
|
+
overflow: auto;
|
|
131
|
+
|
|
132
|
+
&__content {
|
|
133
|
+
@extend %wt-scrollbar;
|
|
134
|
+
|
|
135
|
+
display: flex;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
overflow: auto;
|
|
138
|
+
height: 100%;
|
|
139
|
+
padding-right: var(--spacing-2xs);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
&__list-content {
|
|
143
|
+
@extend %wt-scrollbar;
|
|
144
|
+
|
|
145
|
+
display: flex;
|
|
146
|
+
flex-direction: column;
|
|
147
|
+
padding-right: var(--spacing-2xs);
|
|
148
|
+
gap: var(--spacing-xs);
|
|
149
|
+
overflow: auto;
|
|
150
|
+
align-items: flex-start;
|
|
151
|
+
height: 100%;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&__label-wrapper {
|
|
155
|
+
display: flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
cursor: pointer;
|
|
158
|
+
padding: 0 var(--spacing-2xs);
|
|
159
|
+
border-radius: var(--border-radius);
|
|
160
|
+
color: var(--wt-tree-item-on);
|
|
161
|
+
transition: var(--transition);
|
|
162
|
+
|
|
163
|
+
&:hover {
|
|
164
|
+
background: var(--wt-tree-item-hover);
|
|
165
|
+
color: var(--wt-tree-item-hover-on);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
&.active {
|
|
169
|
+
background: var(--wt-tree-item-active);
|
|
170
|
+
color: var(--wt-tree-item-active-on);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
&__label-icon {
|
|
175
|
+
flex-shrink: 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
:root {
|
|
3
|
+
--wt-tree-item-active: var(--primary-light-color);
|
|
4
|
+
--wt-tree-item-active-on: var(--grey-darken-4);
|
|
5
|
+
|
|
6
|
+
--wt-tree-item: var(--primary-light-color);
|
|
7
|
+
--wt-tree-item-on: var(--grey-darken-1);
|
|
8
|
+
|
|
9
|
+
--wt-tree-item-hover: var(--dp-24-surface-color);
|
|
10
|
+
--wt-tree-item-hover-on: var(--grey-darken-1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
:root.theme--dark {
|
|
14
|
+
--wt-tree-item-active: var(--primary-light-color);
|
|
15
|
+
--wt-tree-item-active-on: var(--grey-lighten-4);
|
|
16
|
+
|
|
17
|
+
--wt-tree-item: var(--primary-light-color);
|
|
18
|
+
--wt-tree-item-on: var(--grey-lighten-3);
|
|
19
|
+
|
|
20
|
+
--wt-tree-item-hover-on: var(--grey-lighten-3);
|
|
21
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="wt-tree-line">
|
|
3
|
+
<div class="wt-tree-line__icon-wrapper">
|
|
4
|
+
<wt-icon
|
|
5
|
+
v-for="(icon, index) in nestedIcons"
|
|
6
|
+
:key="index"
|
|
7
|
+
:icon="icon.icon"
|
|
8
|
+
:class="{ hidden: icon.hidden }"
|
|
9
|
+
/>
|
|
10
|
+
<wt-icon
|
|
11
|
+
v-if="nestedLevel >= 1"
|
|
12
|
+
:icon="lastChild ? 'tree-corner' : 'tree-cross'"
|
|
13
|
+
/>
|
|
14
|
+
<wt-icon-btn
|
|
15
|
+
v-if="data[childrenProp] && data[childrenProp].length"
|
|
16
|
+
:icon="collapsed ? 'plus' : 'minus'"
|
|
17
|
+
@click="collapsed = !collapsed"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
<div
|
|
21
|
+
:class="{ active: isSelected }"
|
|
22
|
+
class="wt-tree-line__label-wrapper"
|
|
23
|
+
>
|
|
24
|
+
<p
|
|
25
|
+
class="wt-tree-line__label"
|
|
26
|
+
@click="selectElement"
|
|
27
|
+
>
|
|
28
|
+
{{ label }}
|
|
29
|
+
</p>
|
|
30
|
+
<wt-icon
|
|
31
|
+
v-if="isSelected"
|
|
32
|
+
icon="chat-message-status-sent"
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<wt-expand-transition v-show="!collapsed">
|
|
37
|
+
<div>
|
|
38
|
+
<wt-tree-line
|
|
39
|
+
v-for="(child, index) in data[childrenProp]"
|
|
40
|
+
:key="index"
|
|
41
|
+
:model-value="modelValue"
|
|
42
|
+
:data="child"
|
|
43
|
+
:children-prop="childrenProp"
|
|
44
|
+
:item-label="itemLabel"
|
|
45
|
+
:item-data="itemData"
|
|
46
|
+
:nested-level="nestedLevel + 1"
|
|
47
|
+
:next-element="!!data[childrenProp][index + 1]"
|
|
48
|
+
:nested-icons="displayIcons"
|
|
49
|
+
:last-child="index === data[childrenProp].length - 1"
|
|
50
|
+
@open-parent="onOpenParent"
|
|
51
|
+
@update:model-value="emit('update:modelValue', $event)"
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
</wt-expand-transition>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script setup lang="ts">
|
|
58
|
+
import deepEqual from 'deep-equal';
|
|
59
|
+
import { computed, onMounted, ref, watch } from 'vue';
|
|
60
|
+
|
|
61
|
+
import WtExpandTransition from '../transitions/wt-expand-transition.vue';
|
|
62
|
+
import type { WtTreeNestedIcons } from './types/wt-tree-nested-icons.ts';
|
|
63
|
+
|
|
64
|
+
const props = withDefaults(
|
|
65
|
+
defineProps<{
|
|
66
|
+
modelValue: null | any;
|
|
67
|
+
data: any;
|
|
68
|
+
itemLabel?: string | undefined;
|
|
69
|
+
itemData?: string | undefined;
|
|
70
|
+
childrenProp?: string;
|
|
71
|
+
nestedLevel?: number;
|
|
72
|
+
lastChild?: boolean;
|
|
73
|
+
nestedIcons?: WtTreeNestedIcons[];
|
|
74
|
+
nextElement?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* 'It's a key in data object, which contains field what display searched elements. By this field, table will be opened to elements with this field value. '
|
|
77
|
+
*/
|
|
78
|
+
searchedProp?: string;
|
|
79
|
+
}>(),
|
|
80
|
+
{
|
|
81
|
+
nestedLevel: 0,
|
|
82
|
+
childrenProp: 'children',
|
|
83
|
+
lastChild: false,
|
|
84
|
+
nextElement: false,
|
|
85
|
+
searchedProp: 'searched',
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const emit = defineEmits<{
|
|
90
|
+
(e: 'openParent'): void;
|
|
91
|
+
(e: 'update:modelValue', value: any): void;
|
|
92
|
+
}>();
|
|
93
|
+
|
|
94
|
+
const label = computed(() =>
|
|
95
|
+
props.itemLabel ? props.data[props.itemLabel] : props.data,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const displayIcons = computed(() => {
|
|
99
|
+
const icons = props.nestedIcons ? [...props.nestedIcons] : [];
|
|
100
|
+
|
|
101
|
+
if (props.nestedLevel === 0) {
|
|
102
|
+
return icons;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
icons.push({
|
|
106
|
+
icon: 'tree-line',
|
|
107
|
+
hidden: !props.nextElement,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return icons;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const isSelected = computed(() => {
|
|
114
|
+
if (props.itemData) {
|
|
115
|
+
return props.data[props.itemData] === props.modelValue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return deepEqual(props.modelValue, props.data);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const selectElement = () => {
|
|
122
|
+
if (props.data[props.childrenProp]?.length) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
emit(
|
|
127
|
+
'update:modelValue',
|
|
128
|
+
props.itemData ? props.data[props.itemData] : props.data,
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const collapsed = ref(true);
|
|
133
|
+
const openParent = () => {
|
|
134
|
+
if (props.nestedLevel > 0) {
|
|
135
|
+
emit('openParent');
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const onOpenParent = () => {
|
|
140
|
+
collapsed.value = false;
|
|
141
|
+
openParent();
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const hasSearchedElement = (data: Record<string, any>, nestedLevel = 0) => {
|
|
145
|
+
// Check if the object itself has searched
|
|
146
|
+
if (data[props.searchedProp] && nestedLevel) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Check if the object has children
|
|
151
|
+
if (Array.isArray(data[props.childrenProp])) {
|
|
152
|
+
// Iterate through the array
|
|
153
|
+
for (const child of data[props.childrenProp]) {
|
|
154
|
+
// Recursively check nested objects
|
|
155
|
+
if (hasSearchedElement(child, nestedLevel + 1)) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If no match is found, return false
|
|
162
|
+
return false;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
onMounted(() => {
|
|
166
|
+
if (isSelected.value) {
|
|
167
|
+
openParent();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (props.data[props.searchedProp]) {
|
|
171
|
+
openParent();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
watch(
|
|
176
|
+
() => props.modelValue,
|
|
177
|
+
() => {
|
|
178
|
+
if (isSelected.value) {
|
|
179
|
+
openParent();
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
);
|
|
183
|
+
</script>
|
|
184
|
+
|
|
185
|
+
<style lang="scss">
|
|
186
|
+
@use '../../css/main.scss' as *;
|
|
187
|
+
@use './variables.scss' as *;
|
|
188
|
+
|
|
189
|
+
.wt-tree-line {
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: flex-start;
|
|
192
|
+
|
|
193
|
+
&__icon-wrapper {
|
|
194
|
+
display: flex;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
&__label-wrapper {
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
padding: 0 var(--spacing-2xs);
|
|
202
|
+
border-radius: var(--border-radius);
|
|
203
|
+
color: var(--wt-tree-item-on);
|
|
204
|
+
transition: var(--transition);
|
|
205
|
+
|
|
206
|
+
&:hover {
|
|
207
|
+
background: var(--wt-tree-item-hover);
|
|
208
|
+
color: var(--wt-tree-item-hover-on);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
&.active {
|
|
212
|
+
background: var(--wt-tree-item-active);
|
|
213
|
+
color: var(--wt-tree-item-active-on);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
&__label {
|
|
218
|
+
@extend %typo-body-1;
|
|
219
|
+
text-wrap: nowrap;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
</style>
|