@api-client/ui 0.3.2 → 0.3.3

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 (51) hide show
  1. package/build/src/elements/environment/EnvironmentEditor.d.ts.map +1 -1
  2. package/build/src/elements/environment/EnvironmentEditor.js +8 -6
  3. package/build/src/elements/environment/EnvironmentEditor.js.map +1 -1
  4. package/build/src/elements/har/HarViewer.d.ts.map +1 -1
  5. package/build/src/elements/har/HarViewer.js +13 -15
  6. package/build/src/elements/har/HarViewer.js.map +1 -1
  7. package/build/src/elements/http/RequestEditor.d.ts +2 -1
  8. package/build/src/elements/http/RequestEditor.d.ts.map +1 -1
  9. package/build/src/elements/http/RequestEditor.js +17 -12
  10. package/build/src/elements/http/RequestEditor.js.map +1 -1
  11. package/build/src/elements/http/RequestLog.d.ts.map +1 -1
  12. package/build/src/elements/http/RequestLog.js +34 -8
  13. package/build/src/elements/http/RequestLog.js.map +1 -1
  14. package/build/src/md/button/internals/button.styles.js +4 -4
  15. package/build/src/md/button/internals/button.styles.js.map +1 -1
  16. package/build/src/md/motion/animation.d.ts +5 -3
  17. package/build/src/md/motion/animation.d.ts.map +1 -1
  18. package/build/src/md/motion/animation.js +4 -2
  19. package/build/src/md/motion/animation.js.map +1 -1
  20. package/build/src/md/ripple/internals/ripple.styles.d.ts.map +1 -1
  21. package/build/src/md/ripple/internals/ripple.styles.js +20 -8
  22. package/build/src/md/ripple/internals/ripple.styles.js.map +1 -1
  23. package/build/src/md/tabs/internals/Tab.d.ts +25 -9
  24. package/build/src/md/tabs/internals/Tab.d.ts.map +1 -1
  25. package/build/src/md/tabs/internals/Tab.js +122 -53
  26. package/build/src/md/tabs/internals/Tab.js.map +1 -1
  27. package/build/src/md/tabs/internals/Tab.styles.d.ts.map +1 -1
  28. package/build/src/md/tabs/internals/Tab.styles.js +69 -64
  29. package/build/src/md/tabs/internals/Tab.styles.js.map +1 -1
  30. package/build/src/md/tabs/internals/Tabs.d.ts +52 -54
  31. package/build/src/md/tabs/internals/Tabs.d.ts.map +1 -1
  32. package/build/src/md/tabs/internals/Tabs.js +270 -330
  33. package/build/src/md/tabs/internals/Tabs.js.map +1 -1
  34. package/build/src/md/tabs/internals/Tabs.styles.d.ts.map +1 -1
  35. package/build/src/md/tabs/internals/Tabs.styles.js +13 -17
  36. package/build/src/md/tabs/internals/Tabs.styles.js.map +1 -1
  37. package/demo/md/tabs/tabs.html +19 -0
  38. package/demo/md/tabs/tabs.ts +133 -83
  39. package/package.json +1 -1
  40. package/src/elements/environment/EnvironmentEditor.ts +8 -6
  41. package/src/elements/har/HarViewer.ts +13 -15
  42. package/src/elements/http/RequestEditor.ts +18 -13
  43. package/src/elements/http/RequestLog.ts +34 -8
  44. package/src/md/button/internals/button.styles.ts +4 -4
  45. package/src/md/motion/animation.ts +4 -2
  46. package/src/md/ripple/internals/ripple.styles.ts +20 -8
  47. package/src/md/tabs/internals/Tab.styles.ts +69 -64
  48. package/src/md/tabs/internals/Tab.ts +126 -43
  49. package/src/md/tabs/internals/Tabs.styles.ts +13 -17
  50. package/src/md/tabs/internals/Tabs.ts +259 -305
  51. package/test/elements/har/HarViewerElement.test.ts +1 -55
@@ -1,9 +1,10 @@
1
1
  import { __esDecorate, __runInitializers } from "tslib";
2
- import { html, LitElement, nothing } from 'lit';
2
+ import { html, LitElement } from 'lit';
3
3
  import { property, query, queryAssignedElements, state } from 'lit/decorators.js';
4
4
  import { classMap } from 'lit/directives/class-map.js';
5
5
  import { styleMap } from 'lit/directives/style-map.js';
6
6
  import { Easing } from '../../motion/animation.js';
7
+ import UiTab from './Tab.js';
7
8
  import '../../icon-button/ui-icon-button.js';
8
9
  import '../../divider/ui-divider.js';
9
10
  import '../../icons/ui-icon.js';
@@ -18,27 +19,21 @@ export function calcPercent(w, w0) {
18
19
  }
19
20
  let UiTabs = (() => {
20
21
  let _classSuper = LitElement;
21
- let _assignedElements_decorators;
22
- let _assignedElements_initializers = [];
23
- let _assignedElements_extraInitializers = [];
22
+ let _tabs_decorators;
23
+ let _tabs_initializers = [];
24
+ let _tabs_extraInitializers = [];
25
+ let _tabsScrollerElement_decorators;
26
+ let _tabsScrollerElement_initializers = [];
27
+ let _tabsScrollerElement_extraInitializers = [];
28
+ let _slotElement_decorators;
29
+ let _slotElement_initializers = [];
30
+ let _slotElement_extraInitializers = [];
24
31
  let _pointer_decorators;
25
32
  let _pointer_initializers = [];
26
33
  let _pointer_extraInitializers = [];
27
- let _content_decorators;
28
- let _content_initializers = [];
29
- let _content_extraInitializers = [];
30
34
  let _priority_decorators;
31
35
  let _priority_initializers = [];
32
36
  let _priority_extraInitializers = [];
33
- let _scrollable_decorators;
34
- let _scrollable_initializers = [];
35
- let _scrollable_extraInitializers = [];
36
- let _selected_decorators;
37
- let _selected_initializers = [];
38
- let _selected_extraInitializers = [];
39
- let _selectedAttribute_decorators;
40
- let _selectedAttribute_initializers = [];
41
- let _selectedAttribute_extraInitializers = [];
42
37
  let _pointerStyles_decorators;
43
38
  let _pointerStyles_initializers = [];
44
39
  let _pointerStyles_extraInitializers = [];
@@ -48,87 +43,112 @@ let UiTabs = (() => {
48
43
  let _isVisible_decorators;
49
44
  let _isVisible_initializers = [];
50
45
  let _isVisible_extraInitializers = [];
46
+ let _autoActivate_decorators;
47
+ let _autoActivate_initializers = [];
48
+ let _autoActivate_extraInitializers = [];
51
49
  return class UiTabs extends _classSuper {
52
50
  static {
53
51
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
54
- _assignedElements_decorators = [queryAssignedElements({ flatten: true })];
52
+ _tabs_decorators = [queryAssignedElements({ flatten: true, selector: 'ui-tab' })];
53
+ _tabsScrollerElement_decorators = [query('.tabs')];
54
+ _slotElement_decorators = [query('slot')];
55
55
  _pointer_decorators = [query('.pointer')];
56
- _content_decorators = [query('.content')];
57
56
  _priority_decorators = [property({ type: String, reflect: true })];
58
- _scrollable_decorators = [property({ type: Boolean })];
59
- _selected_decorators = [property({ type: String })];
60
- _selectedAttribute_decorators = [property({ type: String })];
61
57
  _pointerStyles_decorators = [state()];
62
58
  _indicated_decorators = [state()];
63
59
  _isVisible_decorators = [state()];
64
- __esDecorate(this, null, _assignedElements_decorators, { kind: "accessor", name: "assignedElements", static: false, private: false, access: { has: obj => "assignedElements" in obj, get: obj => obj.assignedElements, set: (obj, value) => { obj.assignedElements = value; } }, metadata: _metadata }, _assignedElements_initializers, _assignedElements_extraInitializers);
60
+ _autoActivate_decorators = [property({ type: Boolean })];
61
+ __esDecorate(this, null, _tabs_decorators, { kind: "accessor", name: "tabs", static: false, private: false, access: { has: obj => "tabs" in obj, get: obj => obj.tabs, set: (obj, value) => { obj.tabs = value; } }, metadata: _metadata }, _tabs_initializers, _tabs_extraInitializers);
62
+ __esDecorate(this, null, _tabsScrollerElement_decorators, { kind: "accessor", name: "tabsScrollerElement", static: false, private: false, access: { has: obj => "tabsScrollerElement" in obj, get: obj => obj.tabsScrollerElement, set: (obj, value) => { obj.tabsScrollerElement = value; } }, metadata: _metadata }, _tabsScrollerElement_initializers, _tabsScrollerElement_extraInitializers);
63
+ __esDecorate(this, null, _slotElement_decorators, { kind: "accessor", name: "slotElement", static: false, private: false, access: { has: obj => "slotElement" in obj, get: obj => obj.slotElement, set: (obj, value) => { obj.slotElement = value; } }, metadata: _metadata }, _slotElement_initializers, _slotElement_extraInitializers);
65
64
  __esDecorate(this, null, _pointer_decorators, { kind: "accessor", name: "pointer", static: false, private: false, access: { has: obj => "pointer" in obj, get: obj => obj.pointer, set: (obj, value) => { obj.pointer = value; } }, metadata: _metadata }, _pointer_initializers, _pointer_extraInitializers);
66
- __esDecorate(this, null, _content_decorators, { kind: "accessor", name: "content", static: false, private: false, access: { has: obj => "content" in obj, get: obj => obj.content, set: (obj, value) => { obj.content = value; } }, metadata: _metadata }, _content_initializers, _content_extraInitializers);
67
65
  __esDecorate(this, null, _priority_decorators, { kind: "accessor", name: "priority", static: false, private: false, access: { has: obj => "priority" in obj, get: obj => obj.priority, set: (obj, value) => { obj.priority = value; } }, metadata: _metadata }, _priority_initializers, _priority_extraInitializers);
68
- __esDecorate(this, null, _scrollable_decorators, { kind: "accessor", name: "scrollable", static: false, private: false, access: { has: obj => "scrollable" in obj, get: obj => obj.scrollable, set: (obj, value) => { obj.scrollable = value; } }, metadata: _metadata }, _scrollable_initializers, _scrollable_extraInitializers);
69
- __esDecorate(this, null, _selected_decorators, { kind: "accessor", name: "selected", static: false, private: false, access: { has: obj => "selected" in obj, get: obj => obj.selected, set: (obj, value) => { obj.selected = value; } }, metadata: _metadata }, _selected_initializers, _selected_extraInitializers);
70
- __esDecorate(this, null, _selectedAttribute_decorators, { kind: "accessor", name: "selectedAttribute", static: false, private: false, access: { has: obj => "selectedAttribute" in obj, get: obj => obj.selectedAttribute, set: (obj, value) => { obj.selectedAttribute = value; } }, metadata: _metadata }, _selectedAttribute_initializers, _selectedAttribute_extraInitializers);
71
66
  __esDecorate(this, null, _pointerStyles_decorators, { kind: "accessor", name: "pointerStyles", static: false, private: false, access: { has: obj => "pointerStyles" in obj, get: obj => obj.pointerStyles, set: (obj, value) => { obj.pointerStyles = value; } }, metadata: _metadata }, _pointerStyles_initializers, _pointerStyles_extraInitializers);
72
67
  __esDecorate(this, null, _indicated_decorators, { kind: "accessor", name: "indicated", static: false, private: false, access: { has: obj => "indicated" in obj, get: obj => obj.indicated, set: (obj, value) => { obj.indicated = value; } }, metadata: _metadata }, _indicated_initializers, _indicated_extraInitializers);
73
68
  __esDecorate(this, null, _isVisible_decorators, { kind: "accessor", name: "isVisible", static: false, private: false, access: { has: obj => "isVisible" in obj, get: obj => obj.isVisible, set: (obj, value) => { obj.isVisible = value; } }, metadata: _metadata }, _isVisible_initializers, _isVisible_extraInitializers);
69
+ __esDecorate(this, null, _autoActivate_decorators, { kind: "accessor", name: "autoActivate", static: false, private: false, access: { has: obj => "autoActivate" in obj, get: obj => obj.autoActivate, set: (obj, value) => { obj.autoActivate = value; } }, metadata: _metadata }, _autoActivate_initializers, _autoActivate_extraInitializers);
74
70
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
75
71
  }
76
- items = [];
77
72
  activeItem = null;
78
73
  previousItem = null;
79
- #assignedElements_accessor_storage = __runInitializers(this, _assignedElements_initializers, void 0);
80
- get assignedElements() { return this.#assignedElements_accessor_storage; }
81
- set assignedElements(value) { this.#assignedElements_accessor_storage = value; }
82
- #pointer_accessor_storage = (__runInitializers(this, _assignedElements_extraInitializers), __runInitializers(this, _pointer_initializers, void 0));
83
- get pointer() { return this.#pointer_accessor_storage; }
84
- set pointer(value) { this.#pointer_accessor_storage = value; }
85
- #content_accessor_storage = (__runInitializers(this, _pointer_extraInitializers), __runInitializers(this, _content_initializers, void 0));
86
- get content() { return this.#content_accessor_storage; }
87
- set content(value) { this.#content_accessor_storage = value; }
88
- #priority_accessor_storage = (__runInitializers(this, _content_extraInitializers), __runInitializers(this, _priority_initializers, 'primary'
89
74
  /**
90
- * If true, tabs are scrollable and the tab width is based on the label width.
91
- * @attribute
75
+ * The currently selected tab, `null` only when there are no tab children.
92
76
  */
93
- ));
94
- /**
95
- * The priority of the tabs.
96
- *
97
- * @default primary
98
- * @attribute
99
- */
100
- get priority() { return this.#priority_accessor_storage; }
101
- set priority(value) { this.#priority_accessor_storage = value; }
102
- #scrollable_accessor_storage = (__runInitializers(this, _priority_extraInitializers), __runInitializers(this, _scrollable_initializers, void 0));
77
+ get activeTab() {
78
+ return this.tabs.find((tab) => tab.selected) ?? null;
79
+ }
80
+ set activeTab(tab) {
81
+ // Ignore setting activeTab to null. As long as there are children, one tab
82
+ // must be selected.
83
+ if (tab) {
84
+ this.activateTab(tab);
85
+ }
86
+ }
103
87
  /**
104
- * If true, tabs are scrollable and the tab width is based on the label width.
105
- * @attribute
88
+ * The index of the currently selected tab.
106
89
  */
107
- get scrollable() { return this.#scrollable_accessor_storage; }
108
- set scrollable(value) { this.#scrollable_accessor_storage = value; }
109
- #selected_accessor_storage = (__runInitializers(this, _scrollable_extraInitializers), __runInitializers(this, _selected_initializers, void 0));
90
+ get activeTabIndex() {
91
+ return this.tabs.findIndex((tab) => tab.selected);
92
+ }
110
93
  /**
111
- * The value of the selected tab.
112
- * This is matched with the `aria-controls` of the tab.
113
- * @attribute
94
+ * Sets the active tab by index.
114
95
  */
115
- get selected() { return this.#selected_accessor_storage; }
116
- set selected(value) { this.#selected_accessor_storage = value; }
117
- #selectedAttribute_accessor_storage = (__runInitializers(this, _selected_extraInitializers), __runInitializers(this, _selectedAttribute_initializers, `aria-controls`));
96
+ set activeTabIndex(index) {
97
+ const activateTabAtIndex = () => {
98
+ const tab = this.tabs[index];
99
+ // Ignore out-of-bound indices.
100
+ if (tab) {
101
+ this.activateTab(tab);
102
+ }
103
+ };
104
+ if (!this.slotElement) {
105
+ // This is needed to support setting the activeTabIndex via a lit property
106
+ // binding.
107
+ //
108
+ // ```ts
109
+ // html`
110
+ // <md-tabs .activeTabIndex=${1}>
111
+ // <md-tab>First</md-tab>
112
+ // <md-tab>Second</md-tab>
113
+ // </md-tabs>
114
+ // `;
115
+ // ```
116
+ //
117
+ // It's needed since lit's rendering lifecycle is asynchronous, and the
118
+ // `<slot>` element hasn't rendered, so `tabs` is empty.
119
+ this.updateComplete.then(activateTabAtIndex);
120
+ return;
121
+ }
122
+ activateTabAtIndex();
123
+ }
124
+ get focusedTab() {
125
+ return this.tabs.find((tab) => tab.matches(':focus-within'));
126
+ }
127
+ #tabs_accessor_storage = __runInitializers(this, _tabs_initializers, void 0);
128
+ get tabs() { return this.#tabs_accessor_storage; }
129
+ set tabs(value) { this.#tabs_accessor_storage = value; }
130
+ #tabsScrollerElement_accessor_storage = (__runInitializers(this, _tabs_extraInitializers), __runInitializers(this, _tabsScrollerElement_initializers, void 0));
131
+ get tabsScrollerElement() { return this.#tabsScrollerElement_accessor_storage; }
132
+ set tabsScrollerElement(value) { this.#tabsScrollerElement_accessor_storage = value; }
133
+ #slotElement_accessor_storage = (__runInitializers(this, _tabsScrollerElement_extraInitializers), __runInitializers(this, _slotElement_initializers, void 0));
134
+ get slotElement() { return this.#slotElement_accessor_storage; }
135
+ set slotElement(value) { this.#slotElement_accessor_storage = value; }
136
+ #pointer_accessor_storage = (__runInitializers(this, _slotElement_extraInitializers), __runInitializers(this, _pointer_initializers, void 0));
137
+ get pointer() { return this.#pointer_accessor_storage; }
138
+ set pointer(value) { this.#pointer_accessor_storage = value; }
139
+ #priority_accessor_storage = (__runInitializers(this, _pointer_extraInitializers), __runInitializers(this, _priority_initializers, 'primary'));
118
140
  /**
119
- * The attribute on the `ui-tab` that indicates which value for `selected`
120
- * corresponds to which tab.
141
+ * The priority of the tabs.
121
142
  *
122
- * @default aria-controls
143
+ * @default primary
123
144
  * @attribute
124
145
  */
125
- get selectedAttribute() { return this.#selectedAttribute_accessor_storage; }
126
- set selectedAttribute(value) { this.#selectedAttribute_accessor_storage = value; }
127
- #pointerStyles_accessor_storage = (__runInitializers(this, _selectedAttribute_extraInitializers), __runInitializers(this, _pointerStyles_initializers, void 0));
146
+ get priority() { return this.#priority_accessor_storage; }
147
+ set priority(value) { this.#priority_accessor_storage = value; }
148
+ #pointerStyles_accessor_storage = (__runInitializers(this, _priority_extraInitializers), __runInitializers(this, _pointerStyles_initializers, void 0));
128
149
  get pointerStyles() { return this.#pointerStyles_accessor_storage; }
129
150
  set pointerStyles(value) { this.#pointerStyles_accessor_storage = value; }
130
- contentScroll = __runInitializers(this, _pointerStyles_extraInitializers);
131
- #indicated_accessor_storage = __runInitializers(this, _indicated_initializers, false);
151
+ #indicated_accessor_storage = (__runInitializers(this, _pointerStyles_extraInitializers), __runInitializers(this, _indicated_initializers, false));
132
152
  get indicated() { return this.#indicated_accessor_storage; }
133
153
  set indicated(value) { this.#indicated_accessor_storage = value; }
134
154
  observer = __runInitializers(this, _indicated_extraInitializers);
@@ -139,12 +159,23 @@ let UiTabs = (() => {
139
159
  */
140
160
  get isVisible() { return this.#isVisible_accessor_storage; }
141
161
  set isVisible(value) { this.#isVisible_accessor_storage = value; }
162
+ #autoActivate_accessor_storage = (__runInitializers(this, _isVisible_extraInitializers), __runInitializers(this, _autoActivate_initializers, false));
163
+ /**
164
+ * Whether or not to automatically select a tab when it is focused.
165
+ */
166
+ get autoActivate() { return this.#autoActivate_accessor_storage; }
167
+ set autoActivate(value) { this.#autoActivate_accessor_storage = value; }
168
+ internals = (__runInitializers(this, _autoActivate_extraInitializers), this.attachInternals());
142
169
  constructor() {
143
170
  super();
171
+ this.internals.role = 'tablist';
144
172
  this.observer = new IntersectionObserver(this.intersectionCallback.bind(this), {
145
173
  threshold: 1.0,
146
174
  rootMargin: '0px',
147
175
  });
176
+ this.addEventListener('keydown', this.handleKeydown.bind(this));
177
+ this.addEventListener('keyup', this.handleKeyup.bind(this));
178
+ this.addEventListener('focusout', this.handleFocusout.bind(this));
148
179
  }
149
180
  willUpdate(cp) {
150
181
  if (cp.has('isVisible')) {
@@ -154,10 +185,10 @@ let UiTabs = (() => {
154
185
  }
155
186
  connectedCallback() {
156
187
  super.connectedCallback();
188
+ this.observer.observe(this);
157
189
  if (!this.hasAttribute('role')) {
158
190
  this.setAttribute('role', 'tablist');
159
191
  }
160
- this.observer.observe(this);
161
192
  }
162
193
  disconnectedCallback() {
163
194
  super.disconnectedCallback();
@@ -167,97 +198,28 @@ let UiTabs = (() => {
167
198
  const [entry] = entries;
168
199
  this.isVisible = entry.isIntersecting;
169
200
  }
170
- async updateItems() {
171
- const elements = this.assignedElements || [];
172
- const items = elements.filter(this.isTabItem, this);
173
- this.items = items;
174
- if (this.activeItem && !items.includes(this.activeItem)) {
175
- this.activeItem = null;
176
- }
177
- await this.updateComplete;
178
- this.ensureSelection();
179
- }
180
201
  /**
181
- * @return Whether the given element is a list item element.
202
+ * Scrolls the toolbar, if overflowing, to the active tab, or the provided
203
+ * tab.
204
+ *
205
+ * @param tabToScrollTo The tab that should be scrolled to. Defaults to the
206
+ * active tab.
207
+ * @return A Promise that resolves after the tab has been scrolled to.
182
208
  */
183
- isTabItem(element) {
184
- if (element.nodeType !== Node.ELEMENT_NODE) {
185
- return false;
186
- }
187
- return element.localName === 'ui-tab';
188
- }
189
- isSelectable(element) {
190
- if (element.disabled) {
191
- return false;
192
- }
193
- if (element.hidden && element.hasAttribute('hidden')) {
194
- return false;
195
- }
196
- return true;
197
- }
198
- ensureSelection() {
199
- const { selected, selectedAttribute, activeItem, items } = this;
200
- if (!selectedAttribute) {
201
- return;
202
- }
203
- const item = items.find((i) => i.getAttribute(selectedAttribute) === selected);
204
- if (item && item === activeItem) {
205
- return;
206
- }
207
- this.makeSelection(item);
208
- }
209
- makeSelection(tab, focus = false) {
210
- const { activeItem } = this;
211
- if (activeItem === tab) {
212
- tab.highlight();
213
- return;
214
- }
215
- this.previousItem = activeItem;
216
- if (activeItem) {
217
- this.deselectItem(activeItem);
218
- }
219
- if (tab) {
220
- this.selectItem(tab, focus);
221
- this.positionPointer(tab, activeItem);
222
- if (this.activeItem) {
223
- // we set this here so we won't notify selection when initializing.
224
- this.notifySelect(tab);
225
- }
226
- }
227
- }
228
- notifySelect(item) {
229
- const index = this.items.indexOf(item);
230
- if (index === -1) {
209
+ async scrollToTab(tabToScrollTo) {
210
+ await this.updateComplete;
211
+ const { tabs } = this;
212
+ tabToScrollTo ??= this.activeTab;
213
+ if (!tabToScrollTo || !tabs.includes(tabToScrollTo) || !this.tabsScrollerElement) {
231
214
  return;
232
215
  }
233
- const attrValue = item.getAttribute(this.selectedAttribute);
234
- this.dispatchEvent(new CustomEvent('select', {
235
- detail: {
236
- item,
237
- index,
238
- select: attrValue,
239
- },
240
- }));
241
- }
242
- selectItem(tab, focus = false) {
243
- this.activeItem = tab;
244
- if (this.hasAttribute('tabindex')) {
245
- this.removeAttribute('tabindex');
246
- }
247
- tab.setAttribute('aria-selected', 'true');
248
- tab.setAttribute('tabindex', '0');
249
- tab.selected = true;
250
- tab.priority = this.priority;
251
- tab.scrollIntoView({ inline: 'nearest', behavior: 'auto', block: 'nearest' });
252
- if (focus) {
253
- tab.focus();
254
- }
255
- }
256
- deselectItem(tab) {
257
- tab.setAttribute('aria-selected', 'false');
258
- tab.setAttribute('tabindex', '-1');
259
- tab.selected = false;
260
- tab.indicated = false;
216
+ // wait for tabs to render.
217
+ await Promise.all(tabs.map((tab) => tab.updateComplete));
218
+ tabToScrollTo.scrollIntoView({
219
+ block: 'nearest',
220
+ inline: 'nearest',
221
+ behavior: !this.focusedTab ? 'instant' : 'auto',
222
+ });
261
223
  }
262
224
  handleVisibility() {
263
225
  const { previousItem, activeItem } = this;
@@ -267,11 +229,12 @@ let UiTabs = (() => {
267
229
  this.positionPointer(activeItem, previousItem);
268
230
  }
269
231
  async positionPointer(tab, old) {
270
- const { items, pointer, isVisible } = this;
232
+ const { pointer, isVisible } = this;
271
233
  if (!isVisible) {
234
+ tab.indicated = true;
272
235
  return;
273
236
  }
274
- const index = items.indexOf(tab);
237
+ const index = this.tabs.indexOf(tab);
275
238
  if (index < 0 || !pointer) {
276
239
  this.pointerStyles = undefined;
277
240
  return;
@@ -282,27 +245,29 @@ let UiTabs = (() => {
282
245
  return;
283
246
  }
284
247
  const isPrimary = this.priority === 'primary';
285
- const final = isPrimary ? this.getPrimaryLeft(tab) : this.getSecondaryLeft(tab);
286
- if (this.pointerStyles && this.pointerStyles.left === `${final}px`) {
248
+ const final = this.getTabSizing(tab);
249
+ if (this.pointerStyles && this.pointerStyles.left === `${final.left}px`) {
250
+ tab.indicated = true;
287
251
  return;
288
252
  }
289
253
  // first position this indicator in the place of the old one.
290
254
  // update the view and then run the animation.
291
255
  this.indicated = true;
292
- const left = isPrimary ? this.getPrimaryLeft(old) : this.getSecondaryLeft(old);
293
- this.pointerStyles = { left };
256
+ const starting = this.getTabSizing(old);
257
+ this.pointerStyles = { left: `${starting.left}px`, width: `${starting.width}px` };
294
258
  await this.updateComplete;
295
- const frames = isPrimary ? this.getPrimaryKeyframes(left, final) : this.getSecondaryKeyframes(left, final);
259
+ const frames = isPrimary ? this.getPrimaryKeyframes(starting, final) : this.getSecondaryKeyframes(starting, final);
296
260
  if (this.moveAnimation) {
297
261
  this.moveAnimation.cancel();
298
262
  }
299
263
  const moveAnimation = pointer.animate(frames, {
300
- duration: 360,
264
+ duration: 200,
301
265
  iterations: 1,
302
- easing: Easing.DECELERATION,
266
+ easing: Easing.EMPHASIZED_DECELERATE,
303
267
  });
304
268
  const finalStyles = {
305
- left: `${final}px`,
269
+ left: `${final.left}px`,
270
+ width: `${final.width}px`,
306
271
  };
307
272
  moveAnimation.addEventListener('finish', () => {
308
273
  this.pointerStyles = finalStyles;
@@ -315,186 +280,180 @@ let UiTabs = (() => {
315
280
  });
316
281
  this.moveAnimation = moveAnimation;
317
282
  }
318
- moveAnimation = __runInitializers(this, _isVisible_extraInitializers);
319
- getPrimaryLeft(tab) {
320
- const contentBox = this.content.getBoundingClientRect();
321
- const tabRect = tab.getBoundingClientRect();
322
- const leftFromParent = tabRect.x - contentBox.x;
323
- const offset = this.scrollable ? 48 : 0; // left button
324
- const left = leftFromParent + tabRect.width / 2 - 20 + offset;
325
- return `${left}px`;
326
- }
327
- getSecondaryLeft(tab) {
328
- const contentBox = this.content.getBoundingClientRect();
329
- const tabRect = tab.getBoundingClientRect();
330
- const offset = this.scrollable ? 48 : 0; // left button
331
- const leftFromParent = tabRect.x - contentBox.x + offset;
332
- return `${leftFromParent}px`;
283
+ moveAnimation;
284
+ getTabSizing(tab) {
285
+ const contentBox = this.tabsScrollerElement.getBoundingClientRect();
286
+ const sizing = tab.getIndicatorSizing();
287
+ sizing.left = sizing.left - contentBox.x;
288
+ return sizing;
333
289
  }
334
290
  getPrimaryKeyframes(start, final) {
335
291
  return [
336
292
  {
337
- left: start,
338
- width: `40px`,
339
- },
340
- {
341
- width: `80px`,
293
+ left: `${start.left}px`,
294
+ width: `${start.width}px`,
342
295
  },
343
296
  {
344
- left: final,
345
- width: `40px`,
297
+ left: `${final.left}px`,
298
+ width: `${final.width}px`,
346
299
  },
347
300
  ];
348
301
  }
349
302
  getSecondaryKeyframes(start, final) {
350
303
  return [
351
304
  {
352
- left: start,
305
+ left: `${start.left}px`,
306
+ width: `${start.width}px`,
353
307
  },
354
308
  {
355
- left: final,
309
+ left: `${final.left}px`,
310
+ width: `${final.width}px`,
356
311
  },
357
312
  ];
358
313
  }
359
- contentClickHandler(e) {
360
- this.activateFromEvent(e);
361
- }
362
- contentKeyDownHandler(e) {
363
- if (['Enter', 'Space'].includes(e.code)) {
364
- this.activateFromEvent(e);
314
+ async handleTabClick(e) {
315
+ const tab = e.composedPath().find((el) => isTab(el));
316
+ // Allow event to bubble
317
+ await new Promise((resolve) => setTimeout(resolve, 0));
318
+ if (e.defaultPrevented || !tab || tab.selected) {
319
+ return;
365
320
  }
321
+ this.activateTab(tab);
366
322
  }
367
- activateFromEvent(e) {
368
- const { items } = this;
369
- const path = e.composedPath();
370
- let item;
371
- while (!item) {
372
- const next = path.shift();
373
- if (!next) {
374
- break;
375
- }
376
- if (items.includes(next)) {
377
- item = next;
378
- }
323
+ activateTab(activeTab) {
324
+ const { tabs } = this;
325
+ const previousTab = this.activeTab;
326
+ if (!tabs.includes(activeTab) || previousTab === activeTab) {
327
+ // Ignore setting activeTab to a tab element that is not a child.
328
+ return;
379
329
  }
380
- if (!item) {
330
+ for (const tab of tabs) {
331
+ tab.selected = tab === activeTab;
332
+ }
333
+ if (previousTab) {
334
+ // Don't dispatch a change event if activating a tab when no previous tabs
335
+ // were selected, such as when md-tabs auto-selects the first tab.
336
+ const detail = {
337
+ item: activeTab,
338
+ index: this.tabs.indexOf(activeTab),
339
+ };
340
+ const defaultPrevented = !this.dispatchEvent(new CustomEvent('change', { detail, bubbles: false, cancelable: true }));
341
+ if (defaultPrevented) {
342
+ for (const tab of tabs) {
343
+ tab.selected = tab === previousTab;
344
+ }
345
+ return;
346
+ }
347
+ previousTab.indicated = false;
348
+ }
349
+ activeTab.indicated = false;
350
+ this.positionPointer(activeTab, previousTab);
351
+ this.updateFocusableTab(activeTab);
352
+ this.scrollToTab(activeTab);
353
+ }
354
+ updateFocusableTab(focusableTab) {
355
+ for (const tab of this.tabs) {
356
+ tab.tabIndex = tab === focusableTab ? 0 : -1;
357
+ }
358
+ }
359
+ async handleKeydown(event) {
360
+ // Allow event to bubble.
361
+ await new Promise((resolve) => setTimeout(resolve, 0));
362
+ const isLeft = event.key === 'ArrowLeft';
363
+ const isRight = event.key === 'ArrowRight';
364
+ const isHome = event.key === 'Home';
365
+ const isEnd = event.key === 'End';
366
+ // Ignore non-navigation keys
367
+ if (event.defaultPrevented || (!isLeft && !isRight && !isHome && !isEnd)) {
381
368
  return;
382
369
  }
383
- this.makeSelection(item, true);
384
- }
385
- handleScrollLeft() {
386
- const { contentScroll = 0, content } = this;
387
- if (contentScroll === 0) {
370
+ const { tabs } = this;
371
+ // Don't try to select another tab if there aren't any.
372
+ if (tabs.length < 2) {
388
373
  return;
389
374
  }
390
- let left = contentScroll - 80;
391
- if (left < 0) {
392
- left = 0;
375
+ // Prevent default interactions, such as scrolling.
376
+ event.preventDefault();
377
+ let indexToFocus;
378
+ if (isHome || isEnd) {
379
+ indexToFocus = isHome ? 0 : tabs.length - 1;
380
+ }
381
+ else {
382
+ // Check if moving forwards or backwards
383
+ const isRtl = getComputedStyle(this).direction === 'rtl';
384
+ const forwards = isRtl ? isLeft : isRight;
385
+ const { focusedTab } = this;
386
+ if (!focusedTab) {
387
+ // If there is not already a tab focused, select the first or last tab
388
+ // based on the direction we're traveling.
389
+ indexToFocus = forwards ? 0 : tabs.length - 1;
390
+ }
391
+ else {
392
+ const focusedIndex = this.tabs.indexOf(focusedTab);
393
+ indexToFocus = forwards ? focusedIndex + 1 : focusedIndex - 1;
394
+ if (indexToFocus >= tabs.length) {
395
+ // Return to start if moving past the last item.
396
+ indexToFocus = 0;
397
+ }
398
+ else if (indexToFocus < 0) {
399
+ // Go to end if moving before the first item.
400
+ indexToFocus = tabs.length - 1;
401
+ }
402
+ }
403
+ }
404
+ const tabToFocus = tabs[indexToFocus];
405
+ tabToFocus.focus();
406
+ if (this.autoActivate) {
407
+ this.activateTab(tabToFocus);
393
408
  }
394
- this.contentScroll = left;
395
- content.scrollTo({
396
- behavior: 'smooth',
397
- left,
398
- });
399
- }
400
- handleScrollRight() {
401
- const { contentScroll = 0, content } = this;
402
- let left = contentScroll + 80;
403
- if (left + content.clientWidth > content.scrollWidth) {
404
- left = content.scrollWidth - content.clientWidth;
409
+ else {
410
+ this.updateFocusableTab(tabToFocus);
405
411
  }
406
- this.contentScroll = left;
407
- content.scrollTo({
408
- behavior: 'smooth',
409
- left,
410
- });
411
412
  }
412
- handleScroll(e) {
413
- const div = e.target;
414
- this.contentScroll = div.scrollLeft;
413
+ // scroll to item on keyup.
414
+ handleKeyup() {
415
+ this.scrollToTab(this.focusedTab ?? this.activeTab);
415
416
  }
416
- handleKeyDown(e) {
417
- if (e.code === 'ArrowRight') {
418
- e.preventDefault();
419
- const tab = this.activeItem ? this.getNextItem(this.activeItem) : this.items[0];
420
- this.makeSelection(tab, true);
417
+ handleFocusout() {
418
+ // restore focus to selected item when blurring the tab bar.
419
+ if (this.matches(':focus-within')) {
420
+ return;
421
421
  }
422
- else if (e.code === 'ArrowLeft') {
423
- e.preventDefault();
424
- const tab = this.activeItem ? this.getPreviousItem(this.activeItem) : this.items[0];
425
- this.makeSelection(tab, true);
422
+ const { activeTab } = this;
423
+ if (activeTab) {
424
+ this.updateFocusableTab(activeTab);
426
425
  }
427
426
  }
428
- getPreviousItem(item) {
429
- const { items } = this;
430
- const curIndex = items.indexOf(item);
431
- if (curIndex < 0) {
432
- return item;
433
- }
434
- let i = curIndex;
435
- let result;
436
- do {
437
- i--;
438
- if (i === curIndex) {
439
- // looped back from the end, no active element to find.
440
- return item;
441
- }
442
- const tmp = items[i];
443
- if (!tmp) {
444
- i = items.length;
445
- continue;
446
- }
447
- if (this.isSelectable(tmp)) {
448
- result = tmp;
427
+ handleSlotChange() {
428
+ for (const tab of this.tabs) {
429
+ tab.priority = this.priority;
430
+ if (tab.selected) {
431
+ tab.indicated = true;
449
432
  }
450
- } while (!result);
451
- return result || item;
452
- }
453
- getNextItem(item) {
454
- const { items } = this;
455
- const curIndex = items.indexOf(item);
456
- if (curIndex < 0) {
457
- return item;
458
433
  }
459
- let i = curIndex;
460
- let next;
461
- do {
462
- i++;
463
- if (i === curIndex) {
464
- // looped back from the start, no active element to find.
465
- return item;
466
- }
467
- const tmp = items[i];
468
- if (!tmp) {
469
- i = -1;
470
- continue;
471
- }
472
- if (this.isSelectable(tmp)) {
473
- next = tmp;
474
- }
475
- } while (!next);
476
- return next || item;
434
+ const firstTab = this.tabs[0];
435
+ if (!this.activeTab && firstTab) {
436
+ // If the active tab was removed, auto-select the first one. There should
437
+ // always be a selected tab while the bar has children.
438
+ this.activateTab(firstTab);
439
+ }
440
+ // When children shift, ensure the active tab is visible. For example, if
441
+ // many children are added before the active tab, it'd be pushed off screen.
442
+ // This ensures it stays visible.
443
+ this.scrollToTab(this.activeTab);
444
+ if (this.activeTab) {
445
+ this.updateFocusableTab(this.activeTab);
446
+ }
477
447
  }
478
448
  render() {
479
- const classes = {
480
- surface: true,
481
- scrollable: !!this.scrollable,
482
- };
483
449
  return html `
484
- <div class="${classMap(classes)}">
485
- ${this.renderLeftScrollControl()}
486
- <div class="content" @scroll="${this.handleScroll}" @keydown="${this.handleKeyDown}">${this.renderSlot()}</div>
487
- ${this.rightScrollControl()} ${this.renderIndicator()}
488
- <div class="divider"></div>
489
- </div>
450
+ <div class="tabs">${this.renderSlot()}</div>
451
+ ${this.renderIndicator()}
452
+ <ui-divider class="divider"></ui-divider>
490
453
  `;
491
454
  }
492
455
  renderSlot() {
493
- return html `<slot
494
- @slotchange="${this.updateItems}"
495
- @click="${this.contentClickHandler}"
496
- @keydown="${this.contentKeyDownHandler}"
497
- ></slot>`;
456
+ return html `<slot @slotchange="${this.handleSlotChange}" @click="${this.handleTabClick}"></slot>`;
498
457
  }
499
458
  renderIndicator() {
500
459
  const classes = {
@@ -509,34 +468,15 @@ let UiTabs = (() => {
509
468
  </div>
510
469
  `;
511
470
  }
512
- renderLeftScrollControl() {
513
- if (!this.scrollable) {
514
- return nothing;
515
- }
516
- return html ` <div class="scroll-control left">
517
- <ui-icon-button aria-label="Scroll tabs left" title="Scroll tabs left" @click="${this.handleScrollLeft}">
518
- <ui-icon icon="chevronLeft"></ui-icon>
519
- </ui-icon-button>
520
- </div>`;
521
- }
522
- rightScrollControl() {
523
- if (!this.scrollable) {
524
- return nothing;
525
- }
526
- return html `<div class="scroll-control right">
527
- <ui-icon-button aria-label="Scroll tabs right" title="Scroll tabs right" @click="${this.handleScrollRight}">
528
- <ui-icon icon="chevronRight"></ui-icon>
529
- </ui-icon-button>
530
- </div>`;
531
- }
532
471
  };
533
472
  })();
534
473
  /**
535
474
  * A container for tabs.
536
475
  *
537
- * @fires select - A non bubbling event when selection change through user interaction.
538
- * The `event.detail` object contains the `item` and `index` properties.
539
- * It also has the `select` property with the value of the `selectedAttribute` on the tab.
476
+ * @fires change - A non bubbling event when selection change through user interaction.
540
477
  */
541
478
  export default UiTabs;
479
+ function isTab(element) {
480
+ return element instanceof UiTab;
481
+ }
542
482
  //# sourceMappingURL=Tabs.js.map