@webitel/ui-sdk 26.8.125 → 26.8.126

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 (30) hide show
  1. package/dist/{clients-C8bQsU8l.js → clients-DV2KSEHt.js} +1 -1
  2. package/dist/{components-BBqsnlPE.js → components-BAjXjwgz.js} +441 -351
  3. package/dist/ui-sdk.css +1 -1
  4. package/dist/ui-sdk.js +1 -1
  5. package/dist/ui-sdk.umd.cjs +649 -649
  6. package/dist/{wt-chat-emoji-BBo9lHMv.js → wt-chat-emoji-DUrA-v1_.js} +1 -1
  7. package/dist/{wt-display-chip-items-D_pECxhW.js → wt-display-chip-items-CZvSZVP_.js} +1 -1
  8. package/dist/{wt-send-message-popup-f1bV8oUt.js → wt-send-message-popup-DphWte4I.js} +2 -2
  9. package/dist/{wt-type-extension-value-input-DpLKqfaa.js → wt-type-extension-value-input-BjmJrhU5.js} +2 -2
  10. package/dist/{wt-vidstack-player-CGkzRJBI.js → wt-vidstack-player-Zns27bwa.js} +1 -1
  11. package/package.json +1 -1
  12. package/src/components/index.js +9 -0
  13. package/src/components/wt-content-wrapper/__tests__/WtContentWrapper.spec.js +34 -0
  14. package/src/components/wt-content-wrapper/_variables.css +10 -0
  15. package/src/components/wt-content-wrapper/wt-content-wrapper.vue +46 -0
  16. package/src/components/wt-layout/__tests__/WtLayout.spec.js +153 -0
  17. package/src/components/wt-layout/_variables.css +15 -0
  18. package/src/components/wt-layout/wt-layout.vue +217 -0
  19. package/src/components/wt-page/__tests__/WtPage.spec.js +35 -0
  20. package/src/components/wt-page/_variables.css +15 -0
  21. package/src/components/wt-page/wt-page.vue +52 -0
  22. package/src/css/components-variables.css +3 -0
  23. package/types/.tsbuildinfo +1 -1
  24. package/types/components/index.d.ts +7 -1
  25. package/types/components/wt-content-wrapper/__tests__/WtContentWrapper.spec.d.ts +1 -0
  26. package/types/components/wt-content-wrapper/wt-content-wrapper.vue.d.ts +18 -0
  27. package/types/components/wt-layout/__tests__/WtLayout.spec.d.ts +1 -0
  28. package/types/components/wt-layout/wt-layout.vue.d.ts +36 -0
  29. package/types/components/wt-page/__tests__/WtPage.spec.d.ts +1 -0
  30. package/types/components/wt-page/wt-page.vue.d.ts +13 -0
@@ -1,4 +1,4 @@
1
- import "./components-BBqsnlPE.js";
1
+ import "./components-BAjXjwgz.js";
2
2
  import { t as e } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
3
  import { m as t } from "./enums-Xag5PrU-.js";
4
4
  import { t as n } from "./wt-button-BfNbgYHy.js";
@@ -1,4 +1,4 @@
1
- import { C as e, i as t } from "./components-BBqsnlPE.js";
1
+ import { C as e, i as t } from "./components-BAjXjwgz.js";
2
2
  import { t as n } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
3
  import { t as r } from "./utils-B4S9Yz-y.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-BBqsnlPE.js";
1
+ import { n as e } from "./components-BAjXjwgz.js";
2
2
  import "./convertDuration-B5bq1uxb.js";
3
- import { i as t, r as n } from "./clients-C8bQsU8l.js";
3
+ import { i as t, r as n } from "./clients-DV2KSEHt.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";
@@ -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-BBqsnlPE.js";
5
- import { a as p, n as m, t as h } from "./clients-C8bQsU8l.js";
4
+ import { a as l, o as u, r as d, s as f } from "./components-BAjXjwgz.js";
5
+ import { a as p, n as m, t as h } from "./clients-DV2KSEHt.js";
6
6
  import { a as g, b as _, d as v, f as y, g as b, i as x, n as S, o as C, r as w, s as T, t as E, x as D } from "./enums-Xag5PrU-.js";
7
7
  import { n as ee, t as te } from "./vue-i18n-VBiROMZ4.js";
8
8
  import "./wt-toast-BScwyGca.js";
@@ -1,4 +1,4 @@
1
- import { w as e } from "./components-BBqsnlPE.js";
1
+ import { w as e } from "./components-BAjXjwgz.js";
2
2
  import "./convertDuration-B5bq1uxb.js";
3
3
  import { t } from "./_plugin-vue_export-helper-B3ysoDQm.js";
4
4
  import { m as n } from "./enums-Xag5PrU-.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "26.8.125",
3
+ "version": "26.8.126",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "npm run docs:dev",
@@ -14,6 +14,7 @@ import WtCallMediaMetric from './wt-call-media-metric/wt-call-media-metric.vue';
14
14
  import WtCard from './wt-card/wt-card.vue';
15
15
  import WtCheckbox from './wt-checkbox/wt-checkbox.vue';
16
16
  import WtChip from './wt-chip/wt-chip.vue';
17
+ import WtContentWrapper from './wt-content-wrapper/wt-content-wrapper.vue';
17
18
  import WtDatetimeText from './wt-datetime-text/wt-datetime-text.vue';
18
19
  import WtDivider from './wt-divider/wt-divider.vue';
19
20
  import WtEmpty from './wt-empty/wt-empty.vue';
@@ -30,12 +31,14 @@ import WtInputText from './wt-input-text/wt-input-text.vue';
30
31
  import WtIntersectionObserver from './wt-intersection-observer/wt-intersection-observer.vue';
31
32
  import WtItemLink from './wt-item-link/wt-item-link.vue';
32
33
  import WtLabel from './wt-label/wt-label.vue';
34
+ import WtLayout from './wt-layout/wt-layout.vue';
33
35
  import WtLoadBar from './wt-load-bar/wt-load-bar.vue';
34
36
  import WtLoader from './wt-loader/wt-loader.vue';
35
37
  import WtLogo from './wt-logo/wt-logo.vue';
36
38
  import WtMessage from './wt-message/wt-message.vue';
37
39
  import WtMultiSelect from './wt-multi-select/wt-multi-select.vue';
38
40
  import WtNavigationBar from './wt-navigation-bar/wt-navigation-bar.vue';
41
+ import WtPage from './wt-page/wt-page.vue';
39
42
  import WtPageWrapper from './wt-page-wrapper/wt-page-wrapper.vue';
40
43
  import WtPassword from './wt-password/wt-password.vue';
41
44
  import WtPopup from './wt-popup/wt-popup.vue';
@@ -239,9 +242,11 @@ const Components = {
239
242
  WtButton,
240
243
  WtChip,
241
244
  WtConfirmDialog,
245
+ WtContentWrapper,
242
246
  WtDivider,
243
247
  WtTooltip,
244
248
  WtLabel,
249
+ WtLayout,
245
250
  WtLoader,
246
251
  WtRoundedAction,
247
252
  WtReplaceTransition,
@@ -274,6 +279,7 @@ const Components = {
274
279
  WtHeaderActions,
275
280
  WtErrorPage,
276
281
  WtNotificationsBar,
282
+ WtPage,
277
283
  WtPageWrapper,
278
284
  WtDualPanel,
279
285
  WtPagination,
@@ -331,6 +337,7 @@ export {
331
337
  WtCheckbox,
332
338
  WtChip,
333
339
  WtConfirmDialog,
340
+ WtContentWrapper,
334
341
  WtContextMenu,
335
342
  WtCopyAction,
336
343
  WtDatepicker,
@@ -359,6 +366,7 @@ export {
359
366
  WtIntersectionObserver,
360
367
  WtItemLink,
361
368
  WtLabel,
369
+ WtLayout,
362
370
  WtLoadBar,
363
371
  WtLoader,
364
372
  WtLogo,
@@ -367,6 +375,7 @@ export {
367
375
  WtNavigationBar,
368
376
  WtNavigationMenu,
369
377
  WtNotificationsBar,
378
+ WtPage,
370
379
  WtPageHeader,
371
380
  WtPageWrapper,
372
381
  WtPagination,
@@ -0,0 +1,34 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+
3
+ import WtContentWrapper from '../wt-content-wrapper.vue';
4
+
5
+ describe('WtContentWrapper', () => {
6
+ it('renders a component', () => {
7
+ const wrapper = shallowMount(WtContentWrapper);
8
+ expect(wrapper.classes('wt-content-wrapper')).toBe(true);
9
+ });
10
+
11
+ it('renders content via default slot', () => {
12
+ const content = 'Content';
13
+ const wrapper = shallowMount(WtContentWrapper, {
14
+ slots: {
15
+ default: content,
16
+ },
17
+ });
18
+ expect(wrapper.text()).toBe(content);
19
+ });
20
+
21
+ it('aligns content to the left by default', () => {
22
+ const wrapper = shallowMount(WtContentWrapper);
23
+ expect(wrapper.classes('wt-content-wrapper--align-left')).toBe(true);
24
+ });
25
+
26
+ it('applies align prop', () => {
27
+ const wrapper = shallowMount(WtContentWrapper, {
28
+ props: {
29
+ align: 'stretch',
30
+ },
31
+ });
32
+ expect(wrapper.classes('wt-content-wrapper--align-stretch')).toBe(true);
33
+ });
34
+ });
@@ -0,0 +1,10 @@
1
+ :root {
2
+ --wt-content-wrapper-background-color: var(--p-surface-0);
3
+ --wt-content-wrapper-border-radius: var(--p-border-radius-lg);
4
+ --wt-content-wrapper-padding: var(--spacing-sm);
5
+ --wt-content-wrapper-gap: var(--spacing-sm);
6
+ }
7
+
8
+ :root.theme--dark {
9
+ --wt-content-wrapper-background-color: var(--p-surface-800);
10
+ }
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <div :class="`wt-content-wrapper--align-${align}`" class="wt-content-wrapper">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ /**
9
+ * Structural container inside `wt-layout`: owns inner padding, gap and
10
+ * horizontal alignment of its content. No business logic, no own visuals
11
+ * beyond the surface.
12
+ */
13
+ interface Props {
14
+ align?: 'left' | 'center' | 'stretch';
15
+ }
16
+
17
+ withDefaults(defineProps<Props>(), {
18
+ align: 'left',
19
+ });
20
+ </script>
21
+
22
+ <style scoped>
23
+ .wt-content-wrapper {
24
+ display: flex;
25
+ flex: 0 0 auto;
26
+ flex-direction: column;
27
+ gap: var(--wt-content-wrapper-gap);
28
+ box-sizing: border-box;
29
+ border-radius: var(--wt-content-wrapper-border-radius);
30
+ background: var(--wt-content-wrapper-background-color);
31
+ padding: var(--wt-content-wrapper-padding);
32
+ min-width: 0;
33
+ }
34
+
35
+ .wt-content-wrapper--align-left {
36
+ align-items: flex-start;
37
+ }
38
+
39
+ .wt-content-wrapper--align-center {
40
+ align-items: center;
41
+ }
42
+
43
+ .wt-content-wrapper--align-stretch {
44
+ align-items: stretch;
45
+ }
46
+ </style>
@@ -0,0 +1,153 @@
1
+ import { mount, shallowMount } from '@vue/test-utils';
2
+
3
+ import WtLayout from '../wt-layout.vue';
4
+
5
+ const mockRect = (el, width) => {
6
+ el.getBoundingClientRect = () => ({
7
+ width,
8
+ });
9
+ el.style.minWidth = '320px';
10
+ };
11
+
12
+ const mountPair = (props) => {
13
+ const Host = {
14
+ components: {
15
+ WtLayout,
16
+ },
17
+ template: `
18
+ <div>
19
+ <wt-layout v-bind="layoutProps" @resize="onResize" />
20
+ <wt-layout class="neighbor" />
21
+ </div>
22
+ `,
23
+ data: () => ({
24
+ layoutProps: props,
25
+ resized: [],
26
+ }),
27
+ methods: {
28
+ onResize(width) {
29
+ this.resized.push(width);
30
+ },
31
+ },
32
+ };
33
+ const wrapper = mount(Host, {
34
+ attachTo: document.body,
35
+ });
36
+ const [layout, neighbor] = wrapper.findAll('.wt-layout');
37
+ mockRect(layout.element, 400);
38
+ mockRect(neighbor.element, 600);
39
+ return {
40
+ wrapper,
41
+ layout,
42
+ };
43
+ };
44
+
45
+ describe('WtLayout', () => {
46
+ it('renders a component', () => {
47
+ const wrapper = shallowMount(WtLayout);
48
+ expect(wrapper.classes('wt-layout')).toBe(true);
49
+ });
50
+
51
+ it('renders content via default slot', () => {
52
+ const content = 'Layout content';
53
+ const wrapper = shallowMount(WtLayout, {
54
+ slots: {
55
+ default: content,
56
+ },
57
+ });
58
+ expect(wrapper.text()).toBe(content);
59
+ });
60
+
61
+ it('is fluid without defaultWidth', () => {
62
+ const wrapper = shallowMount(WtLayout);
63
+ expect(wrapper.classes('wt-layout--fixed')).toBe(false);
64
+ expect(wrapper.attributes('style')).toBeUndefined();
65
+ });
66
+
67
+ it('starts at defaultWidth', () => {
68
+ const wrapper = shallowMount(WtLayout, {
69
+ props: {
70
+ defaultWidth: 320,
71
+ },
72
+ });
73
+ expect(wrapper.classes('wt-layout--fixed')).toBe(true);
74
+ expect(wrapper.element.style.flexBasis).toBe('320px');
75
+ });
76
+
77
+ it('renders resizer only when resizable', async () => {
78
+ const wrapper = shallowMount(WtLayout);
79
+ expect(wrapper.find('.wt-layout__resizer').exists()).toBe(false);
80
+ await wrapper.setProps({
81
+ resizable: true,
82
+ });
83
+ expect(wrapper.find('.wt-layout__resizer').exists()).toBe(true);
84
+ });
85
+
86
+ it('resizes by dragging, bounded by the neighbor min width', async () => {
87
+ const { wrapper, layout } = mountPair({
88
+ resizable: true,
89
+ });
90
+ const resizer = layout.find('.wt-layout__resizer');
91
+
92
+ await resizer.trigger('pointerdown', {
93
+ button: 0,
94
+ clientX: 400,
95
+ });
96
+ window.dispatchEvent(
97
+ new PointerEvent('pointermove', {
98
+ clientX: 500,
99
+ }),
100
+ );
101
+ await wrapper.vm.$nextTick();
102
+ expect(layout.element.style.flexBasis).toBe('500px');
103
+
104
+ // neighbor is 600px wide with 320px min => max 400 + 280 = 680
105
+ window.dispatchEvent(
106
+ new PointerEvent('pointermove', {
107
+ clientX: 2000,
108
+ }),
109
+ );
110
+ window.dispatchEvent(new PointerEvent('pointerup'));
111
+ await wrapper.vm.$nextTick();
112
+ expect(layout.element.style.flexBasis).toBe('680px');
113
+ expect(wrapper.vm.resized).toEqual([
114
+ 680,
115
+ ]);
116
+ wrapper.unmount();
117
+ });
118
+
119
+ it('does not shrink below its min width', async () => {
120
+ const { wrapper, layout } = mountPair({
121
+ resizable: true,
122
+ });
123
+ const resizer = layout.find('.wt-layout__resizer');
124
+
125
+ await resizer.trigger('pointerdown', {
126
+ button: 0,
127
+ clientX: 400,
128
+ });
129
+ window.dispatchEvent(
130
+ new PointerEvent('pointermove', {
131
+ clientX: 0,
132
+ }),
133
+ );
134
+ window.dispatchEvent(new PointerEvent('pointerup'));
135
+ await wrapper.vm.$nextTick();
136
+ expect(layout.element.style.flexBasis).toBe('320px');
137
+ wrapper.unmount();
138
+ });
139
+
140
+ it('resizes with arrow keys', async () => {
141
+ const { wrapper, layout } = mountPair({
142
+ resizable: true,
143
+ });
144
+ await layout.find('.wt-layout__resizer').trigger('keydown', {
145
+ key: 'ArrowRight',
146
+ });
147
+ expect(layout.element.style.flexBasis).toBe('416px');
148
+ expect(wrapper.vm.resized).toEqual([
149
+ 416,
150
+ ]);
151
+ wrapper.unmount();
152
+ });
153
+ });
@@ -0,0 +1,15 @@
1
+ :root {
2
+ --wt-layout-background-color: var(--p-surface-0);
3
+ --wt-layout-border-radius: var(--p-border-radius-lg);
4
+ --wt-layout-padding: 0;
5
+ --wt-layout-gap: 0;
6
+ --wt-layout-min-width: 320px;
7
+
8
+ --wt-layout-resizer-width: var(--spacing-xs);
9
+ --wt-layout-resizer-line-width: 2px;
10
+ --wt-layout-resizer-active-color: var(--p-primary-color);
11
+ }
12
+
13
+ :root.theme--dark {
14
+ --wt-layout-background-color: var(--p-surface-800);
15
+ }
@@ -0,0 +1,217 @@
1
+ <template>
2
+ <section
3
+ ref="root"
4
+ :class="{
5
+ 'wt-layout--fixed': width !== undefined,
6
+ 'wt-layout--resizing': resizing,
7
+ }"
8
+ :style="width !== undefined ? { flexBasis: `${width}px` } : undefined"
9
+ class="wt-layout"
10
+ >
11
+ <slot />
12
+ <div
13
+ v-if="resizable"
14
+ :aria-valuenow="width"
15
+ :class="`wt-layout__resizer--${resizeEdge}`"
16
+ aria-orientation="vertical"
17
+ class="wt-layout__resizer"
18
+ role="separator"
19
+ tabindex="0"
20
+ @keydown="handleResizerKeydown"
21
+ @pointerdown="startResize"
22
+ />
23
+ </section>
24
+ </template>
25
+
26
+ <script setup lang="ts">
27
+ import { onBeforeUnmount, ref, useTemplateRef, watch } from 'vue';
28
+
29
+ /**
30
+ * Structural container inside `wt-page` body. Fills available width
31
+ * (min 320px) unless `defaultWidth` is set; can be resized by dragging
32
+ * its edge, the adjacent layout compensates. To hide a layout, the parent
33
+ * (i.e. its sibling's owner) removes it with `v-if` / `v-show` — the rest
34
+ * redistribute the space automatically.
35
+ */
36
+ interface Props {
37
+ /** Initial width in px. Without it, the layout is fluid. */
38
+ defaultWidth?: number;
39
+ /** Allows the user to change the width by dragging the edge. */
40
+ resizable?: boolean;
41
+ /** Which edge carries the resize handle. The neighbor on that side compensates. */
42
+ resizeEdge?: 'start' | 'end';
43
+ }
44
+
45
+ const props = withDefaults(defineProps<Props>(), {
46
+ defaultWidth: undefined,
47
+ resizable: false,
48
+ resizeEdge: 'end',
49
+ });
50
+
51
+ const emit = defineEmits<{
52
+ /** Emitted once a resize is finished, with the new width in px. */
53
+ resize: [
54
+ width: number,
55
+ ];
56
+ }>();
57
+
58
+ const KEYBOARD_STEP = 16;
59
+
60
+ const root = useTemplateRef<HTMLElement>('root');
61
+ const width = ref<number | undefined>(props.defaultWidth);
62
+ const resizing = ref(false);
63
+
64
+ watch(
65
+ () => props.defaultWidth,
66
+ (value) => {
67
+ width.value = value;
68
+ },
69
+ );
70
+
71
+ const getWidthBounds = () => {
72
+ const el = root.value as HTMLElement;
73
+ const current = el.getBoundingClientRect().width;
74
+ const min = Number.parseFloat(getComputedStyle(el).minWidth) || 0;
75
+ const neighbor =
76
+ props.resizeEdge === 'end'
77
+ ? el.nextElementSibling
78
+ : el.previousElementSibling;
79
+ if (!(neighbor instanceof HTMLElement)) {
80
+ return {
81
+ current,
82
+ min,
83
+ max: Number.POSITIVE_INFINITY,
84
+ };
85
+ }
86
+ const neighborMin =
87
+ Number.parseFloat(getComputedStyle(neighbor).minWidth) || 0;
88
+ const max = current + neighbor.getBoundingClientRect().width - neighborMin;
89
+ return {
90
+ current,
91
+ min,
92
+ max: Math.max(min, max),
93
+ };
94
+ };
95
+
96
+ const clamp = (value: number, min: number, max: number) =>
97
+ Math.min(Math.max(value, min), max);
98
+
99
+ let stopResize: (() => void) | null = null;
100
+
101
+ const startResize = (event: PointerEvent) => {
102
+ if (!root.value || event.button !== 0) return;
103
+ event.preventDefault();
104
+
105
+ const { current, min, max } = getWidthBounds();
106
+ const startX = event.clientX;
107
+ const direction = props.resizeEdge === 'end' ? 1 : -1;
108
+ width.value = current;
109
+ resizing.value = true;
110
+
111
+ const onMove = (moveEvent: PointerEvent) => {
112
+ const delta = (moveEvent.clientX - startX) * direction;
113
+ width.value = clamp(current + delta, min, max);
114
+ };
115
+
116
+ const onUp = () => {
117
+ stopResize?.();
118
+ emit('resize', width.value as number);
119
+ };
120
+
121
+ stopResize = () => {
122
+ window.removeEventListener('pointermove', onMove);
123
+ window.removeEventListener('pointerup', onUp);
124
+ window.removeEventListener('pointercancel', onUp);
125
+ resizing.value = false;
126
+ stopResize = null;
127
+ };
128
+
129
+ window.addEventListener('pointermove', onMove);
130
+ window.addEventListener('pointerup', onUp);
131
+ window.addEventListener('pointercancel', onUp);
132
+ };
133
+
134
+ const handleResizerKeydown = (event: KeyboardEvent) => {
135
+ if (!root.value) return;
136
+ const keyDirection = {
137
+ ArrowLeft: -1,
138
+ ArrowRight: 1,
139
+ }[event.key];
140
+ if (!keyDirection) return;
141
+ event.preventDefault();
142
+
143
+ const { current, min, max } = getWidthBounds();
144
+ const direction = props.resizeEdge === 'end' ? 1 : -1;
145
+ width.value = clamp(
146
+ current + keyDirection * direction * KEYBOARD_STEP,
147
+ min,
148
+ max,
149
+ );
150
+ emit('resize', width.value);
151
+ };
152
+
153
+ onBeforeUnmount(() => stopResize?.());
154
+ </script>
155
+
156
+ <style scoped>
157
+ .wt-layout {
158
+ display: flex;
159
+ position: relative;
160
+ flex: 1 1 0;
161
+ flex-direction: column;
162
+ gap: var(--wt-layout-gap);
163
+ box-sizing: border-box;
164
+ border-radius: var(--wt-layout-border-radius);
165
+ background: var(--wt-layout-background-color);
166
+ padding: var(--wt-layout-padding);
167
+ min-width: var(--wt-layout-min-width);
168
+ min-height: 0;
169
+ }
170
+
171
+ .wt-layout--fixed {
172
+ flex-grow: 0;
173
+ }
174
+
175
+ .wt-layout--resizing {
176
+ user-select: none;
177
+ }
178
+
179
+ .wt-layout__resizer {
180
+ position: absolute;
181
+ top: 0;
182
+ bottom: 0;
183
+ z-index: 1;
184
+ cursor: col-resize;
185
+ width: var(--wt-layout-resizer-width);
186
+ touch-action: none;
187
+ }
188
+
189
+ .wt-layout__resizer--end {
190
+ right: calc(var(--wt-layout-resizer-width) * -1);
191
+ }
192
+
193
+ .wt-layout__resizer--start {
194
+ left: calc(var(--wt-layout-resizer-width) * -1);
195
+ }
196
+
197
+ .wt-layout__resizer::after {
198
+ position: absolute;
199
+ top: 0;
200
+ bottom: 0;
201
+ left: calc(50% - var(--wt-layout-resizer-line-width) / 2);
202
+ transition: var(--transition);
203
+ background: transparent;
204
+ width: var(--wt-layout-resizer-line-width);
205
+ content: '';
206
+ }
207
+
208
+ .wt-layout__resizer:hover::after,
209
+ .wt-layout__resizer:focus-visible::after,
210
+ .wt-layout--resizing .wt-layout__resizer::after {
211
+ background: var(--wt-layout-resizer-active-color);
212
+ }
213
+
214
+ .wt-layout__resizer:focus-visible {
215
+ outline: none;
216
+ }
217
+ </style>
@@ -0,0 +1,35 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+
3
+ import WtPage from '../wt-page.vue';
4
+
5
+ describe('WtPage', () => {
6
+ it('renders a component', () => {
7
+ const wrapper = shallowMount(WtPage);
8
+ expect(wrapper.classes('wt-page')).toBe(true);
9
+ });
10
+
11
+ it('renders layouts into the body via default slot', () => {
12
+ const content = 'Page layouts';
13
+ const wrapper = shallowMount(WtPage, {
14
+ slots: {
15
+ default: content,
16
+ },
17
+ });
18
+ expect(wrapper.find('.wt-page__body').text()).toBe(content);
19
+ });
20
+
21
+ it('renders header via header slot', () => {
22
+ const content = 'Page header';
23
+ const wrapper = shallowMount(WtPage, {
24
+ slots: {
25
+ header: content,
26
+ },
27
+ });
28
+ expect(wrapper.find('.wt-page__header').text()).toBe(content);
29
+ });
30
+
31
+ it('omits header section without header slot', () => {
32
+ const wrapper = shallowMount(WtPage);
33
+ expect(wrapper.find('.wt-page__header').exists()).toBe(false);
34
+ });
35
+ });
@@ -0,0 +1,15 @@
1
+ :root {
2
+ --wt-page-background-color: var(--p-surface-50);
3
+ --wt-page-padding-x: 0;
4
+ --wt-page-padding-y: 0;
5
+ --wt-page-gap: 0;
6
+
7
+ --wt-page-body-background-color: transparent;
8
+ --wt-page-body-padding-x: var(--spacing-sm);
9
+ --wt-page-body-padding-y: var(--spacing-sm);
10
+ --wt-page-body-gap: var(--spacing-sm);
11
+ }
12
+
13
+ :root.theme--dark {
14
+ --wt-page-background-color: var(--p-surface-850);
15
+ }