@refinitiv-ui/elements 5.12.0-dev.0 → 5.12.0

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.
@@ -5,14 +5,18 @@ import { property } from '@refinitiv-ui/core/lib/decorators/property.js';
5
5
  import { query } from '@refinitiv-ui/core/lib/decorators/query.js';
6
6
  import { VERSION } from '../version.js';
7
7
  import { tweenAnimate } from './helpers/animate.js';
8
+ import { Tab } from '../tab/index.js';
8
9
  import '../button/index.js';
9
10
  const BAR_TRAVEL_DISTANCE = 150; // scroll distance
10
11
  /**
11
12
  * Container for tabs
13
+ *
14
+ * @fires value-changed - Fired when the `value` changes.
12
15
  */
13
16
  let TabBar = class TabBar extends ResponsiveElement {
14
17
  constructor() {
15
18
  super(...arguments);
19
+ this.defaultRole = 'tablist';
16
20
  /**
17
21
  * Specify tab's horizontal alignment
18
22
  */
@@ -25,6 +29,11 @@ let TabBar = class TabBar extends ResponsiveElement {
25
29
  * Use to switch from horizontal to vertical layout.
26
30
  */
27
31
  this.vertical = false;
32
+ /**
33
+ * Internal value of tab bar.
34
+ * Controlled by public setter and getter
35
+ */
36
+ this._value = '';
28
37
  }
29
38
  /**
30
39
  * Element version number
@@ -52,6 +61,23 @@ let TabBar = class TabBar extends ResponsiveElement {
52
61
  }
53
62
  `;
54
63
  }
64
+ /**
65
+ * Value of tab-bar, derived from value of an active tab.
66
+ * @param value Element value
67
+ * @default -
68
+ */
69
+ set value(value) {
70
+ value = typeof value === 'string' ? value : String(value);
71
+ const oldValue = this._value;
72
+ if (value !== oldValue && this.isValidValue(value)) {
73
+ this._value = value;
74
+ this.activateTab(value);
75
+ this.requestUpdate('value', oldValue);
76
+ }
77
+ }
78
+ get value() {
79
+ return this._value;
80
+ }
55
81
  /**
56
82
  * Called after the element’s DOM has been updated the first time.
57
83
  * register scroll event on content element to toggle scroll button
@@ -68,6 +94,8 @@ let TabBar = class TabBar extends ResponsiveElement {
68
94
  this.toggleScrollButton(this.content.clientWidth);
69
95
  }, 66); // equal 15 fps for compatibility
70
96
  });
97
+ this.addEventListener('tap', this.onTap);
98
+ this.addEventListener('keydown', this.onKeyDown);
71
99
  }
72
100
  /**
73
101
  * Called when the element’s DOM has been updated and rendered
@@ -75,16 +103,9 @@ let TabBar = class TabBar extends ResponsiveElement {
75
103
  * @returns {void}
76
104
  */
77
105
  updated(changedProperties) {
78
- /* istanbul ignore else */
79
106
  if (changedProperties.has('level')) {
80
107
  this.setLevel();
81
108
  }
82
- if (changedProperties.has('vertical')) {
83
- // if tab bar changed from horizontal to vertical
84
- if (this.vertical) {
85
- this.hideScrollButtons();
86
- }
87
- }
88
109
  super.updated(changedProperties);
89
110
  }
90
111
  /**
@@ -100,54 +121,132 @@ let TabBar = class TabBar extends ResponsiveElement {
100
121
  }
101
122
  }
102
123
  /**
103
- * Hide all scroll buttons
104
- * @returns {void}
124
+ * Return true if incoming value matches one of the existing tabs
125
+ * @param value Value to check
126
+ * @returns true if incoming value matches one of the existing tabs
105
127
  */
106
- hideScrollButtons() {
107
- this.leftBtn.style.setProperty('display', 'none');
108
- this.rightBtn.style.setProperty('display', 'none');
128
+ isValidValue(value) {
129
+ const tabList = this.getFocusableTabs();
130
+ return tabList.some(tab => this.getTabValue(tab) === value);
109
131
  }
110
132
  /**
111
- * Hide/Show scroll button when element is overflow.
112
- * @param elementWidth width of element
133
+ * When the slot changes, set the level, toggle the scroll button, and set the value
113
134
  * @returns {void}
114
135
  */
115
- toggleScrollButton(elementWidth) {
116
- const { scrollLeft, scrollWidth } = this.content;
117
- if (this.vertical) {
136
+ onSlotChange() {
137
+ const tabList = this.getFocusableTabs();
138
+ if (tabList.length < 1) {
118
139
  return;
119
140
  }
120
- // handle left button
121
- if (scrollLeft > 0) {
122
- this.leftBtn.style.setProperty('display', 'flex');
141
+ this.setLevel();
142
+ // get tab value from active tab
143
+ const activeTab = tabList.find(tab => tab.active) || tabList[0];
144
+ if (activeTab) {
145
+ this.value = this.getTabValue(activeTab);
146
+ }
147
+ this.manageTabIndex();
148
+ }
149
+ /**
150
+ * Mark tab as active
151
+ * @param value value of tab to select
152
+ * @returns {void}
153
+ */
154
+ activateTab(value) {
155
+ if (!value) {
156
+ return;
123
157
  }
124
- else {
125
- this.leftBtn.style.setProperty('display', 'none');
158
+ let hasActiveTab = false;
159
+ const tabList = this.getTabElements(); // get all tab elements include disabled tab
160
+ tabList.forEach(tab => {
161
+ const tabValue = this.getTabValue(tab);
162
+ // only mark tab as active once
163
+ if (tabValue === value && !hasActiveTab && !tab.disabled) {
164
+ tab.active = true;
165
+ hasActiveTab = true;
166
+ }
167
+ else {
168
+ tab.active = false;
169
+ }
170
+ });
171
+ }
172
+ /**
173
+ * Set tab value and fires `tab-changed` event
174
+ * @param event - Event
175
+ * @returns {void}
176
+ */
177
+ onTap(event) {
178
+ if (event.defaultPrevented) {
179
+ return;
126
180
  }
127
- // handle right button
128
- if (Math.floor(scrollWidth - scrollLeft) > Math.round(elementWidth)) {
129
- this.rightBtn.style.setProperty('display', 'flex');
181
+ const element = event.target;
182
+ if (element instanceof Tab) {
183
+ const tabValue = this.getTabValue(element);
184
+ if (tabValue !== this.value) {
185
+ this.value = this.getTabValue(element);
186
+ this.notifyPropertyChange('value', tabValue);
187
+ }
130
188
  }
131
- else {
132
- this.rightBtn.style.setProperty('display', 'none');
189
+ }
190
+ /**
191
+ * Get the value of a tab
192
+ * @param tab - The tab element.
193
+ * @returns The value of the tab.
194
+ */
195
+ getTabValue(tab) {
196
+ return tab.value || (tab.hasAttribute('value') ? '' : this.getTabLabel(tab));
197
+ }
198
+ /**
199
+ * Return the tab's label, or its textContent, or an empty string
200
+ * @param tab - The tab element.
201
+ * @returns The tab label.
202
+ */
203
+ getTabLabel(tab) {
204
+ return tab.label || tab.textContent || '';
205
+ }
206
+ /**
207
+ * Get Tab elements from slot
208
+ * @returns the array of Tab
209
+ */
210
+ getTabElements() {
211
+ const tabs = [];
212
+ for (const child of this.children) {
213
+ if (child instanceof Tab) {
214
+ tabs.push(child);
215
+ }
133
216
  }
217
+ return tabs;
218
+ }
219
+ /**
220
+ * Get focusable tab elements
221
+ * @returns the array of focusable tab
222
+ */
223
+ getFocusableTabs() {
224
+ return this.getTabElements().filter(tab => !tab.disabled);
134
225
  }
135
226
  /**
136
227
  * Set tab level attribute accordingly
137
228
  * @returns {void}
138
229
  */
139
230
  setLevel() {
140
- const tabList = this.querySelectorAll('ef-tab');
141
- tabList.forEach((tab) => {
231
+ const tabList = this.getTabElements(); // get all tab elements include disabled tab
232
+ tabList === null || tabList === void 0 ? void 0 : tabList.forEach((tab) => {
142
233
  tab.level = this.level;
143
234
  });
144
235
  }
145
236
  /**
146
- * Detects when slot changes
237
+ * Hide/Show scroll button when element is overflow.
238
+ * @param elementWidth width of element
147
239
  * @returns {void}
148
240
  */
149
- onSlotChange() {
150
- this.setLevel();
241
+ toggleScrollButton(elementWidth) {
242
+ if (this.vertical) {
243
+ return;
244
+ }
245
+ const { scrollLeft, scrollWidth } = this.content;
246
+ const leftBtnStyle = scrollLeft > 0 ? 'flex' : 'none';
247
+ const rightBtnStyle = scrollWidth - scrollLeft - elementWidth > 1 ? 'flex' : 'none';
248
+ this.leftBtn.style.setProperty('display', leftBtnStyle);
249
+ this.rightBtn.style.setProperty('display', rightBtnStyle);
151
250
  }
152
251
  /**
153
252
  * Update scroll position when clicked on left button
@@ -177,6 +276,114 @@ let TabBar = class TabBar extends ResponsiveElement {
177
276
  }
178
277
  tweenAnimate({ target: this.content, startPosition: scrollLeft, endPosition });
179
278
  }
279
+ /**
280
+ * Focus and set active to tab
281
+ * @param tab - The element that was clicked.
282
+ * @return {void}
283
+ */
284
+ focusAndSetActiveTab(tab) {
285
+ tab.focus();
286
+ tab.scrollIntoView({ block: 'nearest' });
287
+ this.value = this.getTabValue(tab);
288
+ }
289
+ /**
290
+ * Navigate to first focusable tab of the tab bar
291
+ * @returns {void}
292
+ */
293
+ first() {
294
+ const tabList = this.getFocusableTabs();
295
+ if (tabList.length <= 0) {
296
+ return;
297
+ }
298
+ this.focusAndSetActiveTab(tabList[0]);
299
+ this.rovingTabIndex(tabList[0], tabList);
300
+ }
301
+ /**
302
+ * Navigate to last focusable tab of the tab bar
303
+ * @returns {void}
304
+ */
305
+ last() {
306
+ const tabList = this.getFocusableTabs();
307
+ if (tabList.length <= 0) {
308
+ return;
309
+ }
310
+ const lastTab = tabList[tabList.length - 1];
311
+ this.focusAndSetActiveTab(lastTab);
312
+ this.rovingTabIndex(lastTab, tabList);
313
+ }
314
+ /**
315
+ * Navigate to next or previous focusable tab
316
+ * @param direction up/next; down/previous
317
+ * @returns {void}
318
+ */
319
+ navigateToSibling(direction) {
320
+ const tabList = this.getFocusableTabs();
321
+ if (tabList.length <= 0) {
322
+ return;
323
+ }
324
+ const focusedTabIndex = tabList.findIndex(tab => tab === document.activeElement);
325
+ const nextTab = direction === 'next'
326
+ ? tabList[focusedTabIndex + 1] || tabList[0]
327
+ : tabList[focusedTabIndex - 1] || tabList[tabList.length - 1];
328
+ this.focusAndSetActiveTab(nextTab);
329
+ this.rovingTabIndex(nextTab, tabList);
330
+ }
331
+ /**
332
+ * Handles key down event
333
+ * @param event Key down event object
334
+ * @returns {void}
335
+ */
336
+ onKeyDown(event) {
337
+ if (event.defaultPrevented) {
338
+ return;
339
+ }
340
+ switch (event.key) {
341
+ case 'Right':
342
+ case 'Down':
343
+ case 'ArrowRight':
344
+ case 'ArrowDown':
345
+ this.navigateToSibling('next');
346
+ break;
347
+ case 'Left':
348
+ case 'Up':
349
+ case 'ArrowLeft':
350
+ case 'ArrowUp':
351
+ this.navigateToSibling('previous');
352
+ break;
353
+ case 'Home':
354
+ this.first();
355
+ break;
356
+ case 'End':
357
+ this.last();
358
+ break;
359
+ default:
360
+ return;
361
+ }
362
+ event.preventDefault();
363
+ }
364
+ /**
365
+ * Sets the tabindex to -1 for all tabs except the active tab.
366
+ * @param target the tab to be focused
367
+ * @param tabList Array of tabs that contains target
368
+ * @returns {void}
369
+ */
370
+ rovingTabIndex(target, tabList) {
371
+ tabList.forEach((tab) => {
372
+ tab.tabIndex = -1;
373
+ });
374
+ target.tabIndex = 0;
375
+ }
376
+ /**
377
+ * Set tabIndex to all tabs
378
+ * @returns {void}
379
+ */
380
+ manageTabIndex() {
381
+ const tabList = this.getFocusableTabs();
382
+ if (tabList && tabList.length > 0) {
383
+ const focusedTabIndex = tabList.findIndex(tab => tab.active);
384
+ this.rovingTabIndex(tabList[focusedTabIndex], tabList);
385
+ }
386
+ }
180
387
  /**
181
388
  * A `TemplateResult` that will be used
182
389
  * to render the updated internal template.
@@ -184,11 +391,11 @@ let TabBar = class TabBar extends ResponsiveElement {
184
391
  */
185
392
  render() {
186
393
  return html `
187
- <ef-button icon="left" part="left-btn" @tap=${this.handleScrollLeft}></ef-button>
188
- <div part="content">
189
- <slot @slotchange=${this.onSlotChange}></slot>
190
- </div>
191
- <ef-button icon="right" part="right-btn" @tap=${this.handleScrollRight}></ef-button>
394
+ ${!this.vertical ? html `<ef-button tabIndex="-1" icon="left" part="left-btn" @tap=${this.handleScrollLeft}></ef-button>` : null}
395
+ <div part="content">
396
+ <slot @slotchange=${this.onSlotChange}></slot>
397
+ </div>
398
+ ${!this.vertical ? html `<ef-button tabIndex="-1" icon="right" part="right-btn" @tap=${this.handleScrollRight}></ef-button>` : null}
192
399
  `;
193
400
  }
194
401
  };
@@ -201,6 +408,9 @@ __decorate([
201
408
  __decorate([
202
409
  property({ type: Boolean, reflect: true })
203
410
  ], TabBar.prototype, "vertical", void 0);
411
+ __decorate([
412
+ property({ type: String, attribute: false })
413
+ ], TabBar.prototype, "value", null);
204
414
  __decorate([
205
415
  query('[part="content"')
206
416
  ], TabBar.prototype, "content", void 0);
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '5.12.0-dev.0';
1
+ export const VERSION = '5.12.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@refinitiv-ui/elements",
3
- "version": "5.12.0-dev.0",
3
+ "version": "5.12.0",
4
4
  "description": "Element Framework Elements",
5
5
  "author": "Refinitiv",
6
6
  "license": "Apache-2.0",
@@ -590,13 +590,17 @@
590
590
  "lint": "node cli lint",
591
591
  "lint:fix": "node cli lint --fix",
592
592
  "prepublishOnly": "node scripts/release",
593
- "api-analyzer": "node ../../scripts/release/api-analyzer.js",
594
- "version": "node ../../scripts/version"
593
+ "api-analyzer": "node ../../scripts/release/api-analyzer.js"
595
594
  },
596
595
  "dependencies": {
597
596
  "@refinitiv-ui/browser-sparkline": "1.1.7",
598
- "@refinitiv-ui/halo-theme": "^5.4.4-dev.0",
599
- "@refinitiv-ui/solar-theme": "^5.6.3-dev.0",
597
+ "@refinitiv-ui/core": "^5.4.1",
598
+ "@refinitiv-ui/halo-theme": "^5.5.0",
599
+ "@refinitiv-ui/i18n": "^5.2.6",
600
+ "@refinitiv-ui/phrasebook": "^5.4.1",
601
+ "@refinitiv-ui/solar-theme": "^5.7.0",
602
+ "@refinitiv-ui/translate": "^5.3.1",
603
+ "@refinitiv-ui/utils": "^5.1.2",
600
604
  "@types/chart.js": "^2.9.31",
601
605
  "chart.js": "~2.9.4",
602
606
  "d3-interpolate": "^3.0.1",
@@ -605,24 +609,12 @@
605
609
  "tslib": "^2.3.1"
606
610
  },
607
611
  "devDependencies": {
608
- "@refinitiv-ui/core": "^5.4.1-dev.0",
609
- "@refinitiv-ui/demo-block": "^5.1.8-dev.0",
610
- "@refinitiv-ui/i18n": "^5.2.7-dev.0",
611
- "@refinitiv-ui/phrasebook": "^5.4.2-dev.0",
612
- "@refinitiv-ui/test-helpers": "^5.1.3-dev.0",
613
- "@refinitiv-ui/translate": "^5.3.1-dev.0",
614
- "@refinitiv-ui/utils": "^5.1.2-dev.0",
612
+ "@refinitiv-ui/demo-block": "^5.1.8",
613
+ "@refinitiv-ui/test-helpers": "^5.1.2",
615
614
  "@types/d3-interpolate": "^3.0.1"
616
615
  },
617
- "peerDependencies": {
618
- "@refinitiv-ui/core": "^5.4.1-dev.0",
619
- "@refinitiv-ui/i18n": "^5.2.7-dev.0",
620
- "@refinitiv-ui/phrasebook": "^5.4.2-dev.0",
621
- "@refinitiv-ui/translate": "^5.3.1-dev.0",
622
- "@refinitiv-ui/utils": "^5.1.2-dev.0"
623
- },
624
616
  "publishConfig": {
625
617
  "access": "public"
626
618
  },
627
- "gitHead": "593b957791e180fd1167f4045027ccc011143290"
619
+ "gitHead": "5ee75ad2727753f4fc973377a008d6228de3aab9"
628
620
  }