@webitel/ui-sdk 26.8.139 → 26.8.141
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/dist/{clients-Bk3DA6vL.js → clients-7bjVyMfb.js} +10 -7
- package/dist/{components-Bzxyigqb.js → components-BGwoOBLk.js} +1235 -1153
- package/dist/ui-sdk.css +1 -1
- package/dist/ui-sdk.js +1 -1
- package/dist/ui-sdk.umd.cjs +524 -524
- package/dist/{wt-chat-emoji-DqLArBt8.js → wt-chat-emoji-CQt62UaT.js} +1 -1
- package/dist/{wt-display-chip-items-CYABNzDH.js → wt-display-chip-items-D4sngyGn.js} +1 -1
- package/dist/{wt-send-message-popup-B6VaAW53.js → wt-send-message-popup-BlKGak9b.js} +2 -2
- package/dist/{wt-type-extension-value-input-DAPDUmhp.js → wt-type-extension-value-input-PyuOjjR2.js} +8 -8
- package/dist/{wt-vidstack-player-DSgFPUrJ.js → wt-vidstack-player-CHjMJ7iv.js} +1 -1
- package/package.json +1 -1
- package/src/components/index.js +3 -0
- package/src/components/wt-navigation-rail/__tests__/WtNavigationRail.spec.js +90 -0
- package/src/components/wt-navigation-rail/_internals/navigation-rail-button.vue +79 -0
- package/src/components/wt-navigation-rail/_variables.css +45 -0
- package/src/components/wt-navigation-rail/types/WtNavigationRail.ts +19 -0
- package/src/components/wt-navigation-rail/wt-navigation-rail.vue +108 -0
- package/src/components/wt-page/__tests__/WtPage.spec.js +60 -1
- package/src/components/wt-page/wt-page.vue +40 -3
- package/src/css/components-variables.css +1 -0
- package/src/locale/es/es.js +1 -1
- package/src/locale/kz/kz.js +1 -1
- package/src/locale/pl/pl.js +1 -1
- package/src/locale/ro/ro.js +1 -1
- package/src/locale/uz/uz.js +1 -1
- package/src/locale/vi/vi.js +1 -1
- package/types/.tsbuildinfo +1 -1
- package/types/components/index.d.ts +3 -1
- package/types/components/wt-navigation-rail/__tests__/WtNavigationRail.spec.d.ts +1 -0
- package/types/components/wt-navigation-rail/_internals/navigation-rail-button.vue.d.ts +12 -0
- package/types/components/wt-navigation-rail/types/WtNavigationRail.d.ts +17 -0
- package/types/components/wt-navigation-rail/wt-navigation-rail.vue.d.ts +25 -0
- package/types/components/wt-page/wt-page.vue.d.ts +20 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as e, i as t } from "./components-
|
|
1
|
+
import { C as e, i as t } from "./components-BGwoOBLk.js";
|
|
2
2
|
import { t as n } from "./_plugin-vue_export-helper-B3ysoDQm.js";
|
|
3
3
|
import { t as r } from "./utils-DQQQIpjz.js";
|
|
4
4
|
import { Fragment as i, computed as a, createCommentVNode as o, createElementBlock as s, createTextVNode as c, createVNode as l, defineComponent as u, openBlock as d, renderList as f, renderSlot as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as e } from "./components-
|
|
1
|
+
import { n as e } from "./components-BGwoOBLk.js";
|
|
2
2
|
import "./convertDuration-CIPonKKW.js";
|
|
3
|
-
import { i as t, r as n } from "./clients-
|
|
3
|
+
import { i as t, r as n } from "./clients-7bjVyMfb.js";
|
|
4
4
|
import { n as r } from "./vue-i18n-VBiROMZ4.js";
|
|
5
5
|
import { n as i } from "./useEventBus-B-ZeklLu.js";
|
|
6
6
|
import { createBlock as a, createTextVNode as o, createVNode as s, defineComponent as c, mergeProps as l, onMounted as u, openBlock as d, ref as f, resolveComponent as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
|
package/dist/{wt-type-extension-value-input-DAPDUmhp.js → wt-type-extension-value-input-PyuOjjR2.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as e, c as t, i as n, n as r, r as i, s as a, t as o } from "./dist-EXdgfpvN.js";
|
|
2
2
|
import { n as s } from "./genClient-CvWlQ0TZ.js";
|
|
3
3
|
import { a as c } from "./caseConverters-DGri4uJo.js";
|
|
4
|
-
import { a as l, o as u, r as d, s as f } from "./components-
|
|
5
|
-
import { a as p, n as m, t as h } from "./clients-
|
|
4
|
+
import { a as l, o as u, r as d, s as f } from "./components-BGwoOBLk.js";
|
|
5
|
+
import { a as p, n as m, t as h } from "./clients-7bjVyMfb.js";
|
|
6
6
|
import { S as g, _, a as v, c as y, f as b, i as x, n as S, o as C, p as w, r as T, s as E, t as D, x as O } from "./enums-CKer6GJR.js";
|
|
7
7
|
import { n as ee, t as te } from "./vue-i18n-VBiROMZ4.js";
|
|
8
8
|
import "./wt-toast-CAoQkSuJ.js";
|
|
@@ -9346,7 +9346,7 @@ var J = /* @__PURE__ */ function(e) {
|
|
|
9346
9346
|
[T.Agent]: { name: "Espacio de trabajo del agente" },
|
|
9347
9347
|
[T.Audit]: {
|
|
9348
9348
|
name: "Auditoría",
|
|
9349
|
-
sections: { [E.Scorecards]: "
|
|
9349
|
+
sections: { [E.Scorecards]: "Hojas de puntuación" }
|
|
9350
9350
|
},
|
|
9351
9351
|
[T.Crm]: {
|
|
9352
9352
|
name: "CRM",
|
|
@@ -10058,7 +10058,7 @@ var J = /* @__PURE__ */ function(e) {
|
|
|
10058
10058
|
[T.Agent]: { name: "Оператор жұмыс орны" },
|
|
10059
10059
|
[T.Audit]: {
|
|
10060
10060
|
name: "Аудит",
|
|
10061
|
-
sections: { [E.Scorecards]: "
|
|
10061
|
+
sections: { [E.Scorecards]: "Сауалнамалар" }
|
|
10062
10062
|
},
|
|
10063
10063
|
[T.Crm]: {
|
|
10064
10064
|
name: "CRM",
|
|
@@ -10770,7 +10770,7 @@ var J = /* @__PURE__ */ function(e) {
|
|
|
10770
10770
|
[T.Agent]: { name: "Panel Agenta" },
|
|
10771
10771
|
[T.Audit]: {
|
|
10772
10772
|
name: "Audyt",
|
|
10773
|
-
sections: { [E.Scorecards]: "
|
|
10773
|
+
sections: { [E.Scorecards]: "Arkusze ocen" }
|
|
10774
10774
|
},
|
|
10775
10775
|
[T.Crm]: {
|
|
10776
10776
|
name: "CRM",
|
|
@@ -11482,7 +11482,7 @@ var J = /* @__PURE__ */ function(e) {
|
|
|
11482
11482
|
[T.Agent]: { name: "Spațiu Agent" },
|
|
11483
11483
|
[T.Audit]: {
|
|
11484
11484
|
name: "Audit",
|
|
11485
|
-
sections: { [E.Scorecards]: "Fișe evaluare" }
|
|
11485
|
+
sections: { [E.Scorecards]: "Fișe de evaluare" }
|
|
11486
11486
|
},
|
|
11487
11487
|
[T.Crm]: {
|
|
11488
11488
|
name: "CRM",
|
|
@@ -13631,7 +13631,7 @@ te({
|
|
|
13631
13631
|
[T.Agent]: { name: "Agent ish joyi" },
|
|
13632
13632
|
[T.Audit]: {
|
|
13633
13633
|
name: "Audit",
|
|
13634
|
-
sections: { [E.Scorecards]: "Baholash
|
|
13634
|
+
sections: { [E.Scorecards]: "Baholash varaqalari" }
|
|
13635
13635
|
},
|
|
13636
13636
|
[T.Crm]: {
|
|
13637
13637
|
name: "CRM",
|
|
@@ -14344,7 +14344,7 @@ te({
|
|
|
14344
14344
|
[T.Agent]: { name: "Không gian làm việc của tổng đài viên" },
|
|
14345
14345
|
[T.Audit]: {
|
|
14346
14346
|
name: "Kiểm toán",
|
|
14347
|
-
sections: { [E.Scorecards]: "
|
|
14347
|
+
sections: { [E.Scorecards]: "Các phiếu chấm điểm" }
|
|
14348
14348
|
},
|
|
14349
14349
|
[T.Crm]: {
|
|
14350
14350
|
name: "CRM",
|
package/package.json
CHANGED
package/src/components/index.js
CHANGED
|
@@ -38,6 +38,7 @@ import WtLogo from './wt-logo/wt-logo.vue';
|
|
|
38
38
|
import WtMessage from './wt-message/wt-message.vue';
|
|
39
39
|
import WtMultiSelect from './wt-multi-select/wt-multi-select.vue';
|
|
40
40
|
import WtNavigationBar from './wt-navigation-bar/wt-navigation-bar.vue';
|
|
41
|
+
import WtNavigationRail from './wt-navigation-rail/wt-navigation-rail.vue';
|
|
41
42
|
import WtPage from './wt-page/wt-page.vue';
|
|
42
43
|
import WtPageWrapper from './wt-page-wrapper/wt-page-wrapper.vue';
|
|
43
44
|
import WtPassword from './wt-password/wt-password.vue';
|
|
@@ -279,6 +280,7 @@ const Components = {
|
|
|
279
280
|
WtHeaderActions,
|
|
280
281
|
WtErrorPage,
|
|
281
282
|
WtNotificationsBar,
|
|
283
|
+
WtNavigationRail,
|
|
282
284
|
WtPage,
|
|
283
285
|
WtPageWrapper,
|
|
284
286
|
WtDualPanel,
|
|
@@ -374,6 +376,7 @@ export {
|
|
|
374
376
|
WtMultiSelect,
|
|
375
377
|
WtNavigationBar,
|
|
376
378
|
WtNavigationMenu,
|
|
379
|
+
WtNavigationRail,
|
|
377
380
|
WtNotificationsBar,
|
|
378
381
|
WtPage,
|
|
379
382
|
WtPageHeader,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
|
|
3
|
+
import WtNavigationRail from '../wt-navigation-rail.vue';
|
|
4
|
+
|
|
5
|
+
const top = [
|
|
6
|
+
{
|
|
7
|
+
id: 'home',
|
|
8
|
+
icon: 'home',
|
|
9
|
+
label: 'Home',
|
|
10
|
+
badge: {
|
|
11
|
+
value: 1,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'calls',
|
|
16
|
+
icon: 'call',
|
|
17
|
+
label: 'Calls',
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
const bottom = [
|
|
21
|
+
{
|
|
22
|
+
id: 'dialer',
|
|
23
|
+
icon: 'call',
|
|
24
|
+
label: 'Dialer',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const mountRail = (props) =>
|
|
29
|
+
mount(WtNavigationRail, {
|
|
30
|
+
props,
|
|
31
|
+
global: {
|
|
32
|
+
stubs: {
|
|
33
|
+
WtButton: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('WtNavigationRail', () => {
|
|
39
|
+
it('renders top and bottom buttons', () => {
|
|
40
|
+
const wrapper = mountRail({
|
|
41
|
+
topItems: top,
|
|
42
|
+
bottomItems: bottom,
|
|
43
|
+
});
|
|
44
|
+
const groups = wrapper.findAll('.wt-navigation-rail__group');
|
|
45
|
+
expect(groups[0].findAll('.navigation-rail-button')).toHaveLength(2);
|
|
46
|
+
expect(groups[1].findAll('.navigation-rail-button')).toHaveLength(1);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('marks active item', () => {
|
|
50
|
+
const wrapper = mountRail({
|
|
51
|
+
topItems: top,
|
|
52
|
+
bottomItems: bottom,
|
|
53
|
+
activeItemId: 'calls',
|
|
54
|
+
});
|
|
55
|
+
const active = wrapper.findAll('.navigation-rail-button--active');
|
|
56
|
+
expect(active).toHaveLength(1);
|
|
57
|
+
expect(active[0].attributes('aria-label')).toBe('Calls');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('renders badge only for items with badge', () => {
|
|
61
|
+
const wrapper = mountRail({
|
|
62
|
+
topItems: top,
|
|
63
|
+
});
|
|
64
|
+
expect(wrapper.findAll('.wt-badge')).toHaveLength(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders nothing while both lists are empty', () => {
|
|
68
|
+
const wrapper = mountRail({});
|
|
69
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('renders when only one list is not empty', () => {
|
|
73
|
+
const wrapper = mountRail({
|
|
74
|
+
bottomItems: bottom,
|
|
75
|
+
});
|
|
76
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(true);
|
|
77
|
+
expect(wrapper.findAll('.wt-navigation-rail__group')).toHaveLength(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('emits select on click', async () => {
|
|
81
|
+
const wrapper = mountRail({
|
|
82
|
+
topItems: top,
|
|
83
|
+
bottomItems: bottom,
|
|
84
|
+
});
|
|
85
|
+
await wrapper.findAll('button')[2].trigger('click');
|
|
86
|
+
expect(wrapper.emitted('select')[0]).toEqual([
|
|
87
|
+
bottom[0],
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<wt-tooltip :disabled="!item.label" placement="right">
|
|
3
|
+
<template #activator>
|
|
4
|
+
<wt-button
|
|
5
|
+
:class="{ 'navigation-rail-button--active': active }"
|
|
6
|
+
:aria-label="item.label"
|
|
7
|
+
:aria-current="active ? 'page' : undefined"
|
|
8
|
+
:badge="item.badge ? String(item.badge.value) : undefined"
|
|
9
|
+
:badge-severity="item.badge?.severity ?? 'success'"
|
|
10
|
+
:disabled="item.disabled"
|
|
11
|
+
:icon="item.icon"
|
|
12
|
+
:size="ComponentSize.SM"
|
|
13
|
+
:variant="ButtonVariant.TEXT"
|
|
14
|
+
class="navigation-rail-button"
|
|
15
|
+
@click="emit('select', item)"
|
|
16
|
+
/>
|
|
17
|
+
</template>
|
|
18
|
+
{{ item.label }}
|
|
19
|
+
</wt-tooltip>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { ButtonVariant, ComponentSize } from '../../../enums';
|
|
24
|
+
import WtButton from '../../wt-button/wt-button.vue';
|
|
25
|
+
import WtTooltip from '../../wt-tooltip/wt-tooltip.vue';
|
|
26
|
+
import type { NavigationRailItem } from '../types/WtNavigationRail';
|
|
27
|
+
|
|
28
|
+
defineProps<{
|
|
29
|
+
item: NavigationRailItem;
|
|
30
|
+
active?: boolean;
|
|
31
|
+
}>();
|
|
32
|
+
|
|
33
|
+
const emit = defineEmits<{
|
|
34
|
+
select: [
|
|
35
|
+
item: NavigationRailItem,
|
|
36
|
+
];
|
|
37
|
+
}>();
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<style scoped>
|
|
41
|
+
/* wt-button theme colors are overridden with navigation rail tokens */
|
|
42
|
+
.navigation-rail-button.p-button.p-button-text {
|
|
43
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color);
|
|
44
|
+
background: transparent;
|
|
45
|
+
color: var(--wt-navigation-rail-button-icon-color);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.navigation-rail-button.p-button.p-button-text:not(:disabled):hover {
|
|
49
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color);
|
|
50
|
+
background: var(--wt-navigation-rail-button-background--hover);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.navigation-rail-button.p-button.p-button-text:not(:disabled):active {
|
|
54
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color);
|
|
55
|
+
background: var(--wt-navigation-rail-button-background--pressed);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.navigation-rail-button--active.p-button.p-button-text,
|
|
59
|
+
.navigation-rail-button--active.p-button.p-button-text:not(:disabled):active {
|
|
60
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color--active);
|
|
61
|
+
background: var(--wt-navigation-rail-button-background--active);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.navigation-rail-button--active.p-button.p-button-text:not(:disabled):hover {
|
|
65
|
+
--icon-color: var(--wt-navigation-rail-button-icon-color--active-hover);
|
|
66
|
+
background: var(--wt-navigation-rail-button-background--active-hover);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.navigation-rail-button :deep(.wt-badge) {
|
|
70
|
+
position: absolute;
|
|
71
|
+
top: var(--wt-navigation-rail-badge-offset-y);
|
|
72
|
+
left: var(--wt-navigation-rail-badge-offset-x);
|
|
73
|
+
/* p-badge sets own width/min-width (1.5rem), override to match design */
|
|
74
|
+
width: auto;
|
|
75
|
+
min-width: var(--wt-navigation-rail-badge-size);
|
|
76
|
+
height: var(--wt-navigation-rail-badge-size);
|
|
77
|
+
padding: 0 var(--wt-navigation-rail-badge-padding-x);
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--wt-navigation-rail-width: 48px;
|
|
3
|
+
--wt-navigation-rail-padding-x: 8px;
|
|
4
|
+
--wt-navigation-rail-padding-y: 8px;
|
|
5
|
+
--wt-navigation-rail-gap: 8px;
|
|
6
|
+
--wt-navigation-rail-border-radius: 0;
|
|
7
|
+
--wt-navigation-rail-background: linear-gradient(
|
|
8
|
+
to top right,
|
|
9
|
+
rgba(20, 34, 184, 1) 0%,
|
|
10
|
+
rgba(192, 66, 215, 1) 100%
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
--wt-navigation-rail-button-icon-color: var(--p-indigo-50);
|
|
14
|
+
--wt-navigation-rail-button-background--hover: var(--p-deep-purple-600);
|
|
15
|
+
--wt-navigation-rail-button-background--pressed: var(--p-deep-purple-500);
|
|
16
|
+
--wt-navigation-rail-button-icon-color--active: var(--p-deep-purple-600);
|
|
17
|
+
--wt-navigation-rail-button-background--active: var(--p-gray-50);
|
|
18
|
+
--wt-navigation-rail-button-icon-color--active-hover: var(
|
|
19
|
+
--p-deep-purple-550
|
|
20
|
+
);
|
|
21
|
+
--wt-navigation-rail-button-background--active-hover: var(--p-white);
|
|
22
|
+
|
|
23
|
+
--wt-navigation-rail-badge-size: 16px;
|
|
24
|
+
--wt-navigation-rail-badge-padding-x: var(--spacing-2xs);
|
|
25
|
+
--wt-navigation-rail-badge-offset-x: 16px;
|
|
26
|
+
--wt-navigation-rail-badge-offset-y: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
:root.theme--dark {
|
|
30
|
+
--wt-navigation-rail-background: linear-gradient(
|
|
31
|
+
to top right,
|
|
32
|
+
rgba(10, 17, 92, 1) 0%,
|
|
33
|
+
rgba(111, 27, 126, 1) 100%
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
--wt-navigation-rail-button-icon-color: var(--p-deep-purple-150);
|
|
37
|
+
--wt-navigation-rail-button-background--hover: var(--p-deep-purple-650);
|
|
38
|
+
--wt-navigation-rail-button-background--pressed: var(--p-deep-purple-500);
|
|
39
|
+
--wt-navigation-rail-button-icon-color--active: var(--p-white);
|
|
40
|
+
--wt-navigation-rail-button-background--active: var(--p-deep-purple-800);
|
|
41
|
+
--wt-navigation-rail-button-icon-color--active-hover: var(--p-deep-purple-50);
|
|
42
|
+
--wt-navigation-rail-button-background--active-hover: var(
|
|
43
|
+
--p-deep-purple-750
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BadgeSeverity } from '../../wt-badge-new/types/WtBadge';
|
|
2
|
+
|
|
3
|
+
export interface NavigationRailBadge {
|
|
4
|
+
value: string | number;
|
|
5
|
+
/** @default 'success' */
|
|
6
|
+
severity?: BadgeSeverity;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface NavigationRailItem {
|
|
10
|
+
/** Unique id, emitted on select and matched against `active` */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Icon name from icons repository */
|
|
13
|
+
icon: string;
|
|
14
|
+
/** Accessible label (aria-label + tooltip) */
|
|
15
|
+
label?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** Renders badge over the button when set */
|
|
18
|
+
badge?: NavigationRailBadge | null;
|
|
19
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<nav
|
|
3
|
+
v-if="hasItems"
|
|
4
|
+
class="wt-navigation-rail"
|
|
5
|
+
aria-label="Navigation rail"
|
|
6
|
+
>
|
|
7
|
+
<template v-for="section of sections" :key="section.name">
|
|
8
|
+
<div
|
|
9
|
+
v-if="section.items.length"
|
|
10
|
+
:class="['wt-navigation-rail__group', `wt-navigation-rail__group--${section.name}`]"
|
|
11
|
+
>
|
|
12
|
+
<ul class="wt-navigation-rail__list">
|
|
13
|
+
<li v-for="item of section.items" :key="item.id">
|
|
14
|
+
<navigation-rail-button
|
|
15
|
+
:item="item"
|
|
16
|
+
:active="item.id === activeItemId"
|
|
17
|
+
@select="emit('select', $event)"
|
|
18
|
+
/>
|
|
19
|
+
</li>
|
|
20
|
+
</ul>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
</nav>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import { computed } from 'vue';
|
|
28
|
+
|
|
29
|
+
import NavigationRailButton from './_internals/navigation-rail-button.vue';
|
|
30
|
+
import type { NavigationRailItem } from './types/WtNavigationRail';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Vertical app navigation. Holds no routing logic: emits `select`,
|
|
34
|
+
* host application decides what to do (navigate, open panel, etc.).
|
|
35
|
+
* Lists are data-driven by props. Renders nothing while both lists are empty.
|
|
36
|
+
*/
|
|
37
|
+
const props = withDefaults(
|
|
38
|
+
defineProps<{
|
|
39
|
+
/** Buttons pinned to the top */
|
|
40
|
+
topItems?: NavigationRailItem[];
|
|
41
|
+
/** Buttons pinned to the bottom */
|
|
42
|
+
bottomItems?: NavigationRailItem[];
|
|
43
|
+
/** Id of the active item */
|
|
44
|
+
activeItemId?: string;
|
|
45
|
+
}>(),
|
|
46
|
+
{
|
|
47
|
+
topItems: () => [],
|
|
48
|
+
bottomItems: () => [],
|
|
49
|
+
activeItemId: '',
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const hasItems = computed(
|
|
54
|
+
() => !!props.topItems.length || !!props.bottomItems.length,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const sections = computed(
|
|
58
|
+
() =>
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
name: 'top',
|
|
62
|
+
items: props.topItems,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'bottom',
|
|
66
|
+
items: props.bottomItems,
|
|
67
|
+
},
|
|
68
|
+
] as const,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const emit = defineEmits<{
|
|
72
|
+
/** Fires on click of enabled item */
|
|
73
|
+
select: [
|
|
74
|
+
item: NavigationRailItem,
|
|
75
|
+
];
|
|
76
|
+
}>();
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<style scoped>
|
|
80
|
+
.wt-navigation-rail {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
flex: 0 0 auto;
|
|
84
|
+
gap: var(--wt-navigation-rail-gap);
|
|
85
|
+
box-sizing: border-box;
|
|
86
|
+
background: var(--wt-navigation-rail-background);
|
|
87
|
+
padding: var(--wt-navigation-rail-padding-y) var(--wt-navigation-rail-padding-x);
|
|
88
|
+
width: var(--wt-navigation-rail-width);
|
|
89
|
+
min-height: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.wt-navigation-rail__group--bottom {
|
|
93
|
+
margin-top: auto;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.wt-navigation-rail__group,
|
|
97
|
+
.wt-navigation-rail__list {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
gap: var(--wt-navigation-rail-gap);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.wt-navigation-rail__list {
|
|
104
|
+
margin: 0;
|
|
105
|
+
padding: 0;
|
|
106
|
+
list-style: none;
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shallowMount } from '@vue/test-utils';
|
|
1
|
+
import { mount, shallowMount } from '@vue/test-utils';
|
|
2
2
|
|
|
3
3
|
import WtPage from '../wt-page.vue';
|
|
4
4
|
|
|
@@ -32,4 +32,63 @@ describe('WtPage', () => {
|
|
|
32
32
|
const wrapper = shallowMount(WtPage);
|
|
33
33
|
expect(wrapper.find('.wt-page__header').exists()).toBe(false);
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
describe('navigation rail', () => {
|
|
37
|
+
const navigationRail = {
|
|
38
|
+
topItems: [
|
|
39
|
+
{
|
|
40
|
+
id: 'home',
|
|
41
|
+
icon: 'home',
|
|
42
|
+
label: 'Home',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
activeItemId: 'home',
|
|
46
|
+
};
|
|
47
|
+
const mountPage = (props) =>
|
|
48
|
+
mount(WtPage, {
|
|
49
|
+
props,
|
|
50
|
+
global: {
|
|
51
|
+
stubs: {
|
|
52
|
+
WtButton: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('does not render rail without navigationRail prop', () => {
|
|
58
|
+
const wrapper = mountPage({});
|
|
59
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does not render rail while both lists are empty', () => {
|
|
63
|
+
const wrapper = mountPage({
|
|
64
|
+
navigationRail: {
|
|
65
|
+
topItems: [],
|
|
66
|
+
bottomItems: [],
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
expect(wrapper.find('.wt-navigation-rail').exists()).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('renders rail outside of the body when a list is not empty', () => {
|
|
73
|
+
const wrapper = mountPage({
|
|
74
|
+
navigationRail,
|
|
75
|
+
});
|
|
76
|
+
expect(
|
|
77
|
+
wrapper.find('.wt-page__main > .wt-navigation-rail').exists(),
|
|
78
|
+
).toBe(true);
|
|
79
|
+
expect(wrapper.find('.wt-page__body .wt-navigation-rail').exists()).toBe(
|
|
80
|
+
false,
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('emits navigation-select on rail item click', async () => {
|
|
85
|
+
const wrapper = mountPage({
|
|
86
|
+
navigationRail,
|
|
87
|
+
});
|
|
88
|
+
await wrapper.find('button').trigger('click');
|
|
89
|
+
expect(wrapper.emitted('navigation-select')[0]).toEqual([
|
|
90
|
+
navigationRail.topItems[0],
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
35
94
|
});
|
|
@@ -3,18 +3,48 @@
|
|
|
3
3
|
<div v-if="$slots.header" class="wt-page__header">
|
|
4
4
|
<slot name="header" />
|
|
5
5
|
</div>
|
|
6
|
-
<div class="wt-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
<div class="wt-page__main">
|
|
7
|
+
<wt-navigation-rail
|
|
8
|
+
v-if="navigationRail"
|
|
9
|
+
:top-items="navigationRail.topItems"
|
|
10
|
+
:bottom-items="navigationRail.bottomItems"
|
|
11
|
+
:active-item-id="navigationRail.activeItemId"
|
|
12
|
+
@select="emit('navigation-select', $event)"
|
|
13
|
+
/>
|
|
14
|
+
<div class="wt-page__body">
|
|
15
|
+
<!-- @slot Layouts of the page -->
|
|
16
|
+
<slot />
|
|
17
|
+
</div>
|
|
9
18
|
</div>
|
|
10
19
|
</div>
|
|
11
20
|
</template>
|
|
12
21
|
|
|
13
22
|
<script setup lang="ts">
|
|
23
|
+
import type { NavigationRailItem } from '../wt-navigation-rail/types/WtNavigationRail';
|
|
24
|
+
import WtNavigationRail from '../wt-navigation-rail/wt-navigation-rail.vue';
|
|
25
|
+
|
|
14
26
|
/**
|
|
15
27
|
* Top structural level of the interface: header + body, where the body
|
|
16
28
|
* distributes its width between one or more `wt-layout`s.
|
|
29
|
+
* Optional navigation rail is shown at the left of the body and
|
|
30
|
+
* is hidden while both of its lists are empty.
|
|
17
31
|
*/
|
|
32
|
+
defineProps<{
|
|
33
|
+
/** Navigation rail config, rail is not rendered when omitted */
|
|
34
|
+
navigationRail?: {
|
|
35
|
+
topItems?: NavigationRailItem[];
|
|
36
|
+
bottomItems?: NavigationRailItem[];
|
|
37
|
+
activeItemId?: string;
|
|
38
|
+
};
|
|
39
|
+
}>();
|
|
40
|
+
|
|
41
|
+
const emit = defineEmits<{
|
|
42
|
+
/** Fires on click of enabled navigation rail item */
|
|
43
|
+
'navigation-select': [
|
|
44
|
+
item: NavigationRailItem,
|
|
45
|
+
];
|
|
46
|
+
}>();
|
|
47
|
+
|
|
18
48
|
defineSlots<{
|
|
19
49
|
header?: () => unknown;
|
|
20
50
|
default?: () => unknown;
|
|
@@ -38,6 +68,13 @@ defineSlots<{
|
|
|
38
68
|
flex: 0 0 auto;
|
|
39
69
|
}
|
|
40
70
|
|
|
71
|
+
.wt-page__main {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex: 1 1 auto;
|
|
74
|
+
min-width: 0;
|
|
75
|
+
min-height: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
.wt-page__body {
|
|
42
79
|
display: flex;
|
|
43
80
|
position: relative;
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
@import "../components/wt-loader/_variables.css";
|
|
32
32
|
@import "../components/wt-logo/_variables.css";
|
|
33
33
|
@import "../components/wt-navigation-bar/_variables.css";
|
|
34
|
+
@import "../components/wt-navigation-rail/_variables.css";
|
|
34
35
|
@import "../components/wt-notifications-bar/_variables.css";
|
|
35
36
|
@import "../components/wt-page/_variables.css";
|
|
36
37
|
@import "../components/wt-page-wrapper/_variables.css";
|
package/src/locale/es/es.js
CHANGED
package/src/locale/kz/kz.js
CHANGED
package/src/locale/pl/pl.js
CHANGED
package/src/locale/ro/ro.js
CHANGED
package/src/locale/uz/uz.js
CHANGED
package/src/locale/vi/vi.js
CHANGED