@vaadin/tabs 25.2.0-alpha11 → 25.2.0-alpha13
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 +10 -10
- package/src/styles/vaadin-tabs-base-styles.js +9 -0
- package/src/vaadin-tabs-mixin.js +56 -70
- package/src/vaadin-tabs.js +19 -3
- package/web-types.json +1 -1
- package/web-types.lit.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/tabs",
|
|
3
|
-
"version": "25.2.0-
|
|
3
|
+
"version": "25.2.0-alpha13",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -35,18 +35,18 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
38
|
-
"@vaadin/a11y-base": "25.2.0-
|
|
39
|
-
"@vaadin/component-base": "25.2.0-
|
|
40
|
-
"@vaadin/item": "25.2.0-
|
|
41
|
-
"@vaadin/vaadin-themable-mixin": "25.2.0-
|
|
38
|
+
"@vaadin/a11y-base": "25.2.0-alpha13",
|
|
39
|
+
"@vaadin/component-base": "25.2.0-alpha13",
|
|
40
|
+
"@vaadin/item": "25.2.0-alpha13",
|
|
41
|
+
"@vaadin/vaadin-themable-mixin": "25.2.0-alpha13",
|
|
42
42
|
"lit": "^3.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@vaadin/aura": "25.2.0-
|
|
46
|
-
"@vaadin/chai-plugins": "25.2.0-
|
|
47
|
-
"@vaadin/test-runner-commands": "25.2.0-
|
|
45
|
+
"@vaadin/aura": "25.2.0-alpha13",
|
|
46
|
+
"@vaadin/chai-plugins": "25.2.0-alpha13",
|
|
47
|
+
"@vaadin/test-runner-commands": "25.2.0-alpha13",
|
|
48
48
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
49
|
-
"@vaadin/vaadin-lumo-styles": "25.2.0-
|
|
49
|
+
"@vaadin/vaadin-lumo-styles": "25.2.0-alpha13",
|
|
50
50
|
"sinon": "^21.0.2"
|
|
51
51
|
},
|
|
52
52
|
"customElements": "custom-elements.json",
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"web-types.json",
|
|
55
55
|
"web-types.lit.json"
|
|
56
56
|
],
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "a1052aee053529ffcef1a1e9be2c855ed3e98cb2"
|
|
58
58
|
}
|
|
@@ -35,6 +35,13 @@ export const tabsStyles = css`
|
|
|
35
35
|
display: flex;
|
|
36
36
|
flex-direction: column;
|
|
37
37
|
gap: var(--vaadin-tabs-gap, var(--vaadin-gap-s));
|
|
38
|
+
animation: enable-smooth-scroll-after-first-render 1s both;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@keyframes enable-smooth-scroll-after-first-render {
|
|
42
|
+
100% {
|
|
43
|
+
scroll-behavior: smooth;
|
|
44
|
+
}
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
:host([orientation='horizontal']) [part='tabs'] {
|
|
@@ -61,6 +68,8 @@ export const tabsStyles = css`
|
|
|
61
68
|
align-items: center;
|
|
62
69
|
justify-content: center;
|
|
63
70
|
-webkit-tap-highlight-color: transparent;
|
|
71
|
+
-webkit-user-select: none;
|
|
72
|
+
user-select: none;
|
|
64
73
|
touch-action: manipulation;
|
|
65
74
|
}
|
|
66
75
|
|
package/src/vaadin-tabs-mixin.js
CHANGED
|
@@ -70,6 +70,7 @@ export const TabsMixin = (superClass) =>
|
|
|
70
70
|
ready() {
|
|
71
71
|
super.ready();
|
|
72
72
|
|
|
73
|
+
this._updateOverflow();
|
|
73
74
|
this._scrollerElement.addEventListener('scroll', () => this._updateOverflow());
|
|
74
75
|
|
|
75
76
|
this.setAttribute('role', 'tablist');
|
|
@@ -96,92 +97,77 @@ export const TabsMixin = (superClass) =>
|
|
|
96
97
|
this._updateOverflow();
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const forwardButtonVisibleWidth = this._getNavigationButtonVisibleWidth('forward-button');
|
|
103
|
-
const backButtonVisibleWidth = this._getNavigationButtonVisibleWidth('back-button');
|
|
100
|
+
_scrollToItem(idx) {
|
|
101
|
+
const item = this.items[idx];
|
|
102
|
+
const itemRect = item.getBoundingClientRect();
|
|
104
103
|
const scrollerRect = this._scrollerElement.getBoundingClientRect();
|
|
105
|
-
|
|
106
|
-
.reverse()
|
|
107
|
-
.find((item) => this._isItemVisible(item, forwardButtonVisibleWidth, backButtonVisibleWidth, scrollerRect));
|
|
108
|
-
const itemRect = itemToScrollTo.getBoundingClientRect();
|
|
104
|
+
|
|
109
105
|
// This hard-coded number accounts for the width of the mask that covers a part of the visible items.
|
|
110
106
|
// A CSS variable can be introduced to get rid of this value.
|
|
111
|
-
const overflowIndicatorCompensation = 20;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
const overflowIndicatorCompensation = this._vertical ? 10 : 20;
|
|
108
|
+
|
|
109
|
+
if (this._vertical) {
|
|
110
|
+
if (itemRect.bottom > scrollerRect.bottom - overflowIndicatorCompensation) {
|
|
111
|
+
this._scrollerElement.scrollTop =
|
|
112
|
+
item.offsetTop - (scrollerRect.height - itemRect.height) + overflowIndicatorCompensation;
|
|
113
|
+
}
|
|
114
|
+
if (itemRect.top < scrollerRect.top + overflowIndicatorCompensation) {
|
|
115
|
+
this._scrollerElement.scrollTop = item.offsetTop - overflowIndicatorCompensation;
|
|
116
|
+
}
|
|
118
117
|
} else {
|
|
119
|
-
const
|
|
120
|
-
|
|
118
|
+
const backButtonWidth = this.shadowRoot.querySelector(`[part="back-button"]`).offsetWidth;
|
|
119
|
+
const forwardButtonWidth = this.shadowRoot.querySelector(`[part="forward-button"]`).offsetWidth;
|
|
120
|
+
|
|
121
|
+
if (itemRect.right > scrollerRect.right - forwardButtonWidth - overflowIndicatorCompensation) {
|
|
122
|
+
this._scrollerElement.scrollLeft =
|
|
123
|
+
item.offsetLeft -
|
|
124
|
+
(scrollerRect.width - itemRect.width) +
|
|
125
|
+
forwardButtonWidth +
|
|
126
|
+
overflowIndicatorCompensation;
|
|
127
|
+
}
|
|
128
|
+
if (itemRect.left < scrollerRect.left + backButtonWidth + overflowIndicatorCompensation) {
|
|
129
|
+
this._scrollerElement.scrollLeft = item.offsetLeft - backButtonWidth - overflowIndicatorCompensation;
|
|
130
|
+
}
|
|
121
131
|
}
|
|
122
|
-
// It is possible that a scroll offset is calculated to be between 0 and 1. In this case, this offset
|
|
123
|
-
// can be rounded down to zero, rendering the button useless. It is also possible that the offset is
|
|
124
|
-
// calculated such that it results in scrolling backwards for a wide tab or edge cases. This is a
|
|
125
|
-
// workaround for such cases.
|
|
126
|
-
if (-this.__direction * scrollOffset < 1) {
|
|
127
|
-
scrollOffset = -this.__direction * (this._scrollOffset - totalCompensation);
|
|
128
|
-
}
|
|
129
|
-
this._scroll(scrollOffset);
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
/** @protected */
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const itemToScrollTo = this.items.find((item) =>
|
|
139
|
-
this._isItemVisible(item, forwardButtonVisibleWidth, backButtonVisibleWidth, scrollerRect),
|
|
140
|
-
);
|
|
141
|
-
const itemRect = itemToScrollTo.getBoundingClientRect();
|
|
142
|
-
// This hard-coded number accounts for the width of the mask that covers a part of the visible items.
|
|
143
|
-
// A CSS variable can be introduced to get rid of this value.
|
|
144
|
-
const overflowIndicatorCompensation = 20;
|
|
145
|
-
const totalCompensation =
|
|
146
|
-
overflowIndicatorCompensation + this.shadowRoot.querySelector('[part="forward-button"]').clientWidth;
|
|
147
|
-
let scrollOffset;
|
|
148
|
-
if (this.__isRTL) {
|
|
149
|
-
const scrollerLeftEdge = scrollerRect.left + totalCompensation;
|
|
150
|
-
scrollOffset = itemRect.left - scrollerLeftEdge;
|
|
151
|
-
} else {
|
|
152
|
-
const scrollerRightEdge = scrollerRect.right - totalCompensation;
|
|
153
|
-
scrollOffset = itemRect.right - scrollerRightEdge;
|
|
135
|
+
_scrollForward(e) {
|
|
136
|
+
// Allow setInterval loop to trigger scroll (e is undefine in that case)
|
|
137
|
+
// Allow programmatic click events to trigger scroll (__scrollTimer is undefine in that case)
|
|
138
|
+
if (e === undefined || this.__scrollTimer === undefined) {
|
|
139
|
+
this._scroll(this.__direction * (this._scrollOffset / 2) * -1);
|
|
154
140
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** @protected */
|
|
144
|
+
_scrollBack(e) {
|
|
145
|
+
// Allow setInterval loop to trigger scroll (e is undefine in that case)
|
|
146
|
+
// Allow programmatic click events to trigger scroll (__scrollTimer is undefine in that case)
|
|
147
|
+
if (e === undefined || this.__scrollTimer === undefined) {
|
|
148
|
+
this._scroll(this.__direction * (this._scrollOffset / 2));
|
|
161
149
|
}
|
|
162
|
-
this._scroll(scrollOffset);
|
|
163
150
|
}
|
|
164
151
|
|
|
165
|
-
/** @
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
|
|
152
|
+
/** @protected */
|
|
153
|
+
_startScrollForward(e) {
|
|
154
|
+
if (e.button === 0) {
|
|
155
|
+
this._scrollForward();
|
|
156
|
+
this.__scrollTimer = setInterval(this._scrollForward.bind(this), 300);
|
|
169
157
|
}
|
|
170
|
-
const buttonOnTheRightWidth = this.__isRTL ? backButtonVisibleWidth : forwardButtonVisibleWidth;
|
|
171
|
-
const buttonOnTheLeftWidth = this.__isRTL ? forwardButtonVisibleWidth : backButtonVisibleWidth;
|
|
172
|
-
const scrollerRightEdge = scrollerRect.right - buttonOnTheRightWidth;
|
|
173
|
-
const scrollerLeftEdge = scrollerRect.left + buttonOnTheLeftWidth;
|
|
174
|
-
const itemRect = item.getBoundingClientRect();
|
|
175
|
-
return scrollerRightEdge > Math.floor(itemRect.left) && scrollerLeftEdge < Math.ceil(itemRect.right);
|
|
176
158
|
}
|
|
177
159
|
|
|
178
|
-
/** @
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
160
|
+
/** @protected */
|
|
161
|
+
_startScrollBack(e) {
|
|
162
|
+
if (e.button === 0) {
|
|
163
|
+
this._scrollBack();
|
|
164
|
+
this.__scrollTimer = setInterval(this._scrollBack.bind(this), 300);
|
|
183
165
|
}
|
|
184
|
-
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** @protected */
|
|
169
|
+
_stopScroll() {
|
|
170
|
+
clearTimeout(this.__scrollTimer);
|
|
185
171
|
}
|
|
186
172
|
|
|
187
173
|
/** @private */
|
package/src/vaadin-tabs.js
CHANGED
|
@@ -75,13 +75,29 @@ class Tabs extends TabsMixin(ElementMixin(ThemableMixin(PolylitMixin(LumoInjecti
|
|
|
75
75
|
/** @protected */
|
|
76
76
|
render() {
|
|
77
77
|
return html`
|
|
78
|
-
<div
|
|
78
|
+
<div
|
|
79
|
+
@pointerdown="${this._startScrollBack}"
|
|
80
|
+
@pointerup="${this._stopScroll}"
|
|
81
|
+
@pointerleave="${this._stopScroll}"
|
|
82
|
+
@pointercancel="${this._stopScroll}"
|
|
83
|
+
@click="${this._scrollBack}"
|
|
84
|
+
part="back-button"
|
|
85
|
+
aria-hidden="true"
|
|
86
|
+
></div>
|
|
79
87
|
|
|
80
|
-
<div id="scroll" part="tabs">
|
|
88
|
+
<div id="scroll" part="tabs" tabindex="-1">
|
|
81
89
|
<slot></slot>
|
|
82
90
|
</div>
|
|
83
91
|
|
|
84
|
-
<div
|
|
92
|
+
<div
|
|
93
|
+
@pointerdown="${this._startScrollForward}"
|
|
94
|
+
@pointerup="${this._stopScroll}"
|
|
95
|
+
@pointerleave="${this._stopScroll}"
|
|
96
|
+
@pointercancel="${this._stopScroll}"
|
|
97
|
+
@click="${this._scrollForward}"
|
|
98
|
+
part="forward-button"
|
|
99
|
+
aria-hidden="true"
|
|
100
|
+
></div>
|
|
85
101
|
`;
|
|
86
102
|
}
|
|
87
103
|
}
|
package/web-types.json
CHANGED