@vaadin/menu-bar 24.0.0-alpha8 → 24.0.0-beta1

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.
@@ -1,315 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright (c) 2019 - 2023 Vaadin Ltd.
4
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
- */
6
- import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
7
- import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
8
- import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
9
-
10
- /**
11
- * @polymerMixin
12
- * @mixes ResizeMixin
13
- * @mixes ControllerMixin
14
- */
15
- export const ButtonsMixin = (superClass) =>
16
- class extends ResizeMixin(ControllerMixin(superClass)) {
17
- static get properties() {
18
- return {
19
- /**
20
- * @type {boolean}
21
- * @protected
22
- */
23
- _hasOverflow: {
24
- type: Boolean,
25
- value: false,
26
- },
27
-
28
- /** @protected */
29
- _overflow: {
30
- type: Object,
31
- },
32
- };
33
- }
34
-
35
- static get observers() {
36
- return [
37
- '__hasOverflowChanged(_hasOverflow, _overflow)',
38
- '__i18nChanged(i18n, _overflow)',
39
- '_menuItemsChanged(items, _overflow, items.splices)',
40
- ];
41
- }
42
-
43
- /**
44
- * Override getter from `ResizeMixin` to observe parent.
45
- *
46
- * @protected
47
- * @override
48
- */
49
- get _observeParent() {
50
- return true;
51
- }
52
-
53
- /** @protected */
54
- ready() {
55
- super.ready();
56
-
57
- this.setAttribute('role', 'menubar');
58
-
59
- this._overflowController = new SlotController(this, 'overflow', 'vaadin-menu-bar-button', {
60
- initializer: (btn) => {
61
- btn.setAttribute('hidden', '');
62
-
63
- const dots = document.createElement('div');
64
- dots.setAttribute('aria-hidden', 'true');
65
- dots.textContent = '···';
66
- btn.appendChild(dots);
67
-
68
- this._overflow = btn;
69
- this._initButtonAttrs(btn);
70
- },
71
- });
72
- this.addController(this._overflowController);
73
- }
74
-
75
- /**
76
- * @return {!Array<!HTMLElement>}
77
- * @protected
78
- */
79
- get _buttons() {
80
- return Array.from(this.querySelectorAll('vaadin-menu-bar-button'));
81
- }
82
-
83
- /**
84
- * @return {!HTMLElement}
85
- * @protected
86
- */
87
- get _container() {
88
- return this.shadowRoot.querySelector('[part="container"]');
89
- }
90
-
91
- /** @private */
92
- __hasOverflowChanged(hasOverflow, overflow) {
93
- if (overflow) {
94
- overflow.toggleAttribute('hidden', !hasOverflow);
95
- }
96
- }
97
-
98
- /** @private */
99
- _menuItemsChanged(items, overflow) {
100
- if (!overflow) {
101
- return;
102
- }
103
-
104
- if (items !== this._oldItems) {
105
- this._oldItems = items;
106
- this.__renderButtons(items);
107
- }
108
- }
109
-
110
- /** @private */
111
- __i18nChanged(i18n, overflow) {
112
- if (overflow && i18n && i18n.moreOptions !== undefined) {
113
- if (i18n.moreOptions) {
114
- overflow.setAttribute('aria-label', i18n.moreOptions);
115
- } else {
116
- overflow.removeAttribute('aria-label');
117
- }
118
- }
119
- }
120
-
121
- /** @private */
122
- __getOverflowCount(overflow) {
123
- // We can't use optional chaining due to webpack 4
124
- return (overflow.item && overflow.item.children && overflow.item.children.length) || 0;
125
- }
126
-
127
- /** @private */
128
- __restoreButtons(buttons) {
129
- for (let i = 0; i < buttons.length; i++) {
130
- const btn = buttons[i];
131
- btn.disabled = (btn.item && btn.item.disabled) || this.disabled;
132
- btn.style.visibility = '';
133
- btn.style.position = '';
134
-
135
- // Teleport item component back from "overflow" sub-menu
136
- const item = btn.item && btn.item.component;
137
- if (item instanceof HTMLElement && item.getAttribute('role') === 'menuitem') {
138
- btn.appendChild(item);
139
- item.removeAttribute('role');
140
- }
141
- }
142
- this.__updateOverflow([]);
143
- }
144
-
145
- /** @private */
146
- __updateOverflow(items) {
147
- this._overflow.item = { children: items };
148
- this._hasOverflow = items.length > 0;
149
- }
150
-
151
- /** @private */
152
- __setOverflowItems(buttons, overflow) {
153
- const container = this._container;
154
-
155
- if (container.offsetWidth < container.scrollWidth) {
156
- this._hasOverflow = true;
157
-
158
- const isRTL = this.__isRTL;
159
-
160
- let i;
161
- for (i = buttons.length; i > 0; i--) {
162
- const btn = buttons[i - 1];
163
- const btnStyle = getComputedStyle(btn);
164
-
165
- // If this button isn't overflowing, then the rest aren't either
166
- if (
167
- (!isRTL && btn.offsetLeft + btn.offsetWidth < container.offsetWidth - overflow.offsetWidth) ||
168
- (isRTL && btn.offsetLeft >= overflow.offsetWidth)
169
- ) {
170
- break;
171
- }
172
-
173
- btn.disabled = true;
174
- btn.style.visibility = 'hidden';
175
- btn.style.position = 'absolute';
176
- // Save width for buttons with component
177
- btn.style.width = btnStyle.width;
178
- }
179
- const items = buttons.filter((_, idx) => idx >= i).map((b) => b.item);
180
- this.__updateOverflow(items);
181
- }
182
- }
183
-
184
- /** @private */
185
- __detectOverflow() {
186
- const overflow = this._overflow;
187
- const buttons = this._buttons.filter((btn) => btn !== overflow);
188
- const oldOverflowCount = this.__getOverflowCount(overflow);
189
-
190
- // Reset all buttons in the menu bar and the overflow button
191
- this.__restoreButtons(buttons);
192
-
193
- // Hide any overflowing buttons and put them in the 'overflow' button
194
- this.__setOverflowItems(buttons, overflow);
195
-
196
- const newOverflowCount = this.__getOverflowCount(overflow);
197
- if (oldOverflowCount !== newOverflowCount && this._subMenu.opened) {
198
- this._subMenu.close();
199
- }
200
-
201
- const isSingleButton = newOverflowCount === buttons.length || (newOverflowCount === 0 && buttons.length === 1);
202
- this.toggleAttribute('has-single-button', isSingleButton);
203
- }
204
-
205
- /** @protected */
206
- _removeButtons() {
207
- this._buttons.forEach((button) => {
208
- if (button !== this._overflow) {
209
- this.removeChild(button);
210
- }
211
- });
212
- }
213
-
214
- /** @protected */
215
- _initButton(item) {
216
- const button = document.createElement('vaadin-menu-bar-button');
217
-
218
- const itemCopy = { ...item };
219
- button.item = itemCopy;
220
-
221
- if (item.component) {
222
- const component = this.__getComponent(itemCopy);
223
- itemCopy.component = component;
224
- // Save item for overflow menu
225
- component.item = itemCopy;
226
- button.appendChild(component);
227
- } else if (item.text) {
228
- button.textContent = item.text;
229
- }
230
-
231
- return button;
232
- }
233
-
234
- /** @protected */
235
- _initButtonAttrs(button) {
236
- button.setAttribute('role', 'menuitem');
237
-
238
- if (button === this._overflow || (button.item && button.item.children)) {
239
- button.setAttribute('aria-haspopup', 'true');
240
- button.setAttribute('aria-expanded', 'false');
241
- }
242
- }
243
-
244
- /** @protected */
245
- _setButtonDisabled(button, disabled) {
246
- button.disabled = disabled;
247
- button.setAttribute('tabindex', disabled ? '-1' : '0');
248
- }
249
-
250
- /** @protected */
251
- _setButtonTheme(btn, hostTheme) {
252
- let theme = hostTheme;
253
-
254
- // Item theme takes precedence over host theme even if it's empty, as long as it's not undefined or null
255
- const itemTheme = btn.item && btn.item.theme;
256
- if (itemTheme != null) {
257
- theme = Array.isArray(itemTheme) ? itemTheme.join(' ') : itemTheme;
258
- }
259
-
260
- if (theme) {
261
- btn.setAttribute('theme', theme);
262
- } else {
263
- btn.removeAttribute('theme');
264
- }
265
- }
266
-
267
- /** @private */
268
- __getComponent(item) {
269
- const itemComponent = item.component;
270
- let component;
271
-
272
- const isElement = itemComponent instanceof HTMLElement;
273
- // Use existing item component, if any
274
- if (isElement && itemComponent.localName === 'vaadin-context-menu-item') {
275
- component = itemComponent;
276
- } else {
277
- component = document.createElement('vaadin-context-menu-item');
278
- component.appendChild(isElement ? itemComponent : document.createElement(itemComponent));
279
- }
280
- if (item.text) {
281
- const node = component.firstChild || component;
282
- node.textContent = item.text;
283
- }
284
- component.setAttribute('theme', 'menu-bar-item');
285
- return component;
286
- }
287
-
288
- /** @private */
289
- __renderButtons(items = []) {
290
- this._removeButtons();
291
-
292
- /* Empty array, do nothing */
293
- if (items.length === 0) {
294
- return;
295
- }
296
-
297
- items.forEach((item) => {
298
- const button = this._initButton(item);
299
- this.insertBefore(button, this._overflow);
300
- this._setButtonDisabled(button, item.disabled);
301
- this._initButtonAttrs(button);
302
- this._setButtonTheme(button, this._theme);
303
- });
304
-
305
- this.__detectOverflow();
306
- }
307
-
308
- /**
309
- * @protected
310
- * @override
311
- */
312
- _onResize() {
313
- this.__detectOverflow();
314
- }
315
- };