@aquera/nile-elements 0.0.20 → 0.0.21

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 (121) hide show
  1. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/index.d.ts +3 -0
  2. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/index.js +3 -0
  3. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/index.js.map +1 -1
  4. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-calendar/nile-calendar.css.js +2 -5
  5. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-calendar/nile-calendar.css.js.map +1 -1
  6. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-calendar/nile-calendar.d.ts +1 -0
  7. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-calendar/nile-calendar.js +8 -16
  8. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-calendar/nile-calendar.js.map +1 -1
  9. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/index.d.ts +1 -0
  10. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/index.js +2 -0
  11. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/index.js.map +1 -0
  12. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/nile-tab.css.d.ts +12 -0
  13. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/nile-tab.css.js +80 -0
  14. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/nile-tab.css.js.map +1 -0
  15. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/nile-tab.d.ts +55 -0
  16. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/nile-tab.js +129 -0
  17. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab/nile-tab.js.map +1 -0
  18. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/index.d.ts +1 -0
  19. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/index.js +2 -0
  20. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/index.js.map +1 -0
  21. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/nile-tab-group.css.d.ts +12 -0
  22. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/nile-tab-group.css.js +245 -0
  23. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/nile-tab-group.css.js.map +1 -0
  24. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/nile-tab-group.d.ts +82 -0
  25. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/nile-tab-group.js +392 -0
  26. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-group/nile-tab-group.js.map +1 -0
  27. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/index.d.ts +1 -0
  28. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/index.js +2 -0
  29. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/index.js.map +1 -0
  30. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/nile-tab-panel.css.d.ts +12 -0
  31. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/nile-tab-panel.css.js +32 -0
  32. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/nile-tab-panel.css.js.map +1 -0
  33. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/nile-tab-panel.d.ts +38 -0
  34. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/nile-tab-panel.js +71 -0
  35. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/src/nile-tab-panel/nile-tab-panel.js.map +1 -0
  36. package/.rollup.cache/opt/atlassian/pipelines/agent/build/packages/nile-elements/dist/tsconfig.tsbuildinfo +1 -1
  37. package/dist/index.cjs.js +1 -1
  38. package/dist/index.esm.js +1 -1
  39. package/dist/index.iife.js +460 -86
  40. package/dist/nile-calendar/nile-calendar.cjs.js +1 -1
  41. package/dist/nile-calendar/nile-calendar.cjs.js.map +1 -1
  42. package/dist/nile-calendar/nile-calendar.css.cjs.js +1 -1
  43. package/dist/nile-calendar/nile-calendar.css.cjs.js.map +1 -1
  44. package/dist/nile-calendar/nile-calendar.css.esm.js +2 -5
  45. package/dist/nile-calendar/nile-calendar.esm.js +8 -16
  46. package/dist/nile-tab/index.cjs.js +2 -0
  47. package/dist/nile-tab/index.cjs.js.map +1 -0
  48. package/dist/nile-tab/index.esm.js +1 -0
  49. package/dist/nile-tab/nile-tab.cjs.js +2 -0
  50. package/dist/nile-tab/nile-tab.cjs.js.map +1 -0
  51. package/dist/nile-tab/nile-tab.css.cjs.js +2 -0
  52. package/dist/nile-tab/nile-tab.css.cjs.js.map +1 -0
  53. package/dist/nile-tab/nile-tab.css.esm.js +68 -0
  54. package/dist/nile-tab/nile-tab.esm.js +21 -0
  55. package/dist/nile-tab-group/index.cjs.js +2 -0
  56. package/dist/nile-tab-group/index.cjs.js.map +1 -0
  57. package/dist/nile-tab-group/index.esm.js +1 -0
  58. package/dist/nile-tab-group/nile-tab-group.cjs.js +2 -0
  59. package/dist/nile-tab-group/nile-tab-group.cjs.js.map +1 -0
  60. package/dist/nile-tab-group/nile-tab-group.css.cjs.js +2 -0
  61. package/dist/nile-tab-group/nile-tab-group.css.cjs.js.map +1 -0
  62. package/dist/nile-tab-group/nile-tab-group.css.esm.js +233 -0
  63. package/dist/nile-tab-group/nile-tab-group.esm.js +43 -0
  64. package/dist/nile-tab-panel/index.cjs.js +2 -0
  65. package/dist/nile-tab-panel/index.cjs.js.map +1 -0
  66. package/dist/nile-tab-panel/index.esm.js +1 -0
  67. package/dist/nile-tab-panel/nile-tab-panel.cjs.js +2 -0
  68. package/dist/nile-tab-panel/nile-tab-panel.cjs.js.map +1 -0
  69. package/dist/nile-tab-panel/nile-tab-panel.css.cjs.js +2 -0
  70. package/dist/nile-tab-panel/nile-tab-panel.css.cjs.js.map +1 -0
  71. package/dist/nile-tab-panel/nile-tab-panel.css.esm.js +20 -0
  72. package/dist/nile-tab-panel/nile-tab-panel.esm.js +6 -0
  73. package/dist/src/index.d.ts +3 -0
  74. package/dist/src/index.js +3 -0
  75. package/dist/src/index.js.map +1 -1
  76. package/dist/src/nile-calendar/nile-calendar.css.js +2 -5
  77. package/dist/src/nile-calendar/nile-calendar.css.js.map +1 -1
  78. package/dist/src/nile-calendar/nile-calendar.d.ts +1 -0
  79. package/dist/src/nile-calendar/nile-calendar.js +8 -16
  80. package/dist/src/nile-calendar/nile-calendar.js.map +1 -1
  81. package/dist/src/nile-tab/index.d.ts +1 -0
  82. package/dist/src/nile-tab/index.js +2 -0
  83. package/dist/src/nile-tab/index.js.map +1 -0
  84. package/dist/src/nile-tab/nile-tab.css.d.ts +12 -0
  85. package/dist/src/nile-tab/nile-tab.css.js +80 -0
  86. package/dist/src/nile-tab/nile-tab.css.js.map +1 -0
  87. package/dist/src/nile-tab/nile-tab.d.ts +55 -0
  88. package/dist/src/nile-tab/nile-tab.js +129 -0
  89. package/dist/src/nile-tab/nile-tab.js.map +1 -0
  90. package/dist/src/nile-tab-group/index.d.ts +1 -0
  91. package/dist/src/nile-tab-group/index.js +2 -0
  92. package/dist/src/nile-tab-group/index.js.map +1 -0
  93. package/dist/src/nile-tab-group/nile-tab-group.css.d.ts +12 -0
  94. package/dist/src/nile-tab-group/nile-tab-group.css.js +245 -0
  95. package/dist/src/nile-tab-group/nile-tab-group.css.js.map +1 -0
  96. package/dist/src/nile-tab-group/nile-tab-group.d.ts +82 -0
  97. package/dist/src/nile-tab-group/nile-tab-group.js +392 -0
  98. package/dist/src/nile-tab-group/nile-tab-group.js.map +1 -0
  99. package/dist/src/nile-tab-panel/index.d.ts +1 -0
  100. package/dist/src/nile-tab-panel/index.js +2 -0
  101. package/dist/src/nile-tab-panel/index.js.map +1 -0
  102. package/dist/src/nile-tab-panel/nile-tab-panel.css.d.ts +12 -0
  103. package/dist/src/nile-tab-panel/nile-tab-panel.css.js +32 -0
  104. package/dist/src/nile-tab-panel/nile-tab-panel.css.js.map +1 -0
  105. package/dist/src/nile-tab-panel/nile-tab-panel.d.ts +38 -0
  106. package/dist/src/nile-tab-panel/nile-tab-panel.js +71 -0
  107. package/dist/src/nile-tab-panel/nile-tab-panel.js.map +1 -0
  108. package/dist/tsconfig.tsbuildinfo +1 -1
  109. package/package.json +1 -1
  110. package/src/index.ts +3 -0
  111. package/src/nile-calendar/nile-calendar.css.ts +2 -5
  112. package/src/nile-calendar/nile-calendar.ts +9 -16
  113. package/src/nile-tab/index.ts +1 -0
  114. package/src/nile-tab/nile-tab.css.ts +82 -0
  115. package/src/nile-tab/nile-tab.ts +129 -0
  116. package/src/nile-tab-group/index.ts +1 -0
  117. package/src/nile-tab-group/nile-tab-group.css.ts +247 -0
  118. package/src/nile-tab-group/nile-tab-group.ts +441 -0
  119. package/src/nile-tab-panel/index.ts +1 -0
  120. package/src/nile-tab-panel/nile-tab-panel.css.ts +34 -0
  121. package/src/nile-tab-panel/nile-tab-panel.ts +74 -0
@@ -0,0 +1,441 @@
1
+ /**
2
+ * Copyright Aquera Inc 2023
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {LitElement, html, property, CSSResultArray, TemplateResult} from 'lit-element';
9
+ import { customElement } from 'lit/decorators.js';
10
+ import {styles} from './nile-tab-group.css';
11
+
12
+ import '../nile-icon-button/nile-icon-button';
13
+ import { classMap } from 'lit/directives/class-map.js';
14
+ import { query, state } from 'lit/decorators.js';
15
+ import { scrollIntoView } from '../internal/scroll';
16
+ import { watch } from '../internal/watch';
17
+ import NileElement from '../internal/nile-element';
18
+ import type { CSSResultGroup } from 'lit';
19
+ import type NileTab from '../nile-tab/nile-tab';
20
+ import type NileTabPanel from '../nile-tab-panel/nile-tab-panel';
21
+
22
+ /**
23
+ * Nile icon component.
24
+ *
25
+ * @tag nile-tab-group
26
+ *
27
+ * @slot - Used for grouping tab panels in the tab group. Must be `<nile-tab-panel>` elements.
28
+ * @slot nav - Used for grouping tabs in the tab group. Must be `<nile-tab>` elements.
29
+ *
30
+ * @event {{ name: String }} nile-tab-show - Emitted when a tab is shown.
31
+ * @event {{ name: String }} nile-tab-hide - Emitted when a tab is hidden.
32
+ *
33
+ * @csspart base - The component's base wrapper.
34
+ * @csspart nav - The tab group's navigation container where tabs are slotted in.
35
+ * @csspart tabs - The container that wraps the tabs.
36
+ * @csspart active-tab-indicator - The line that highlights the currently selected tab.
37
+ * @csspart body - The tab group's body where tab panels are slotted in.
38
+ * @csspart scroll-button - The previous/next scroll buttons that show when tabs are scrollable, an `<nile-icon-button>`.
39
+ * @csspart scroll-button--start - The starting scroll button.
40
+ * @csspart scroll-button--end - The ending scroll button.
41
+ * @csspart scroll-button__base - The scroll button's exported `base` part.
42
+ *
43
+ * @cssproperty --indicator-color - The color of the active tab indicator.
44
+ * @cssproperty --track-color - The color of the indicator's track (the line that separates tabs from panels).
45
+ * @cssproperty --track-width - The width of the indicator's track (the line that separates tabs from panels).
46
+ *
47
+ */
48
+ @customElement('nile-tab-group')
49
+ export class NileTabGroup extends NileElement {
50
+
51
+ static styles: CSSResultGroup = styles;
52
+
53
+ private activeTab?: NileTab;
54
+ private mutationObserver: MutationObserver;
55
+ private resizeObserver: ResizeObserver;
56
+ private tabs: NileTab[] = [];
57
+ private panels: NileTabPanel[] = [];
58
+
59
+ @query('.tab-group') tabGroup: HTMLElement;
60
+ @query('.tab-group__body') body: HTMLSlotElement;
61
+ @query('.tab-group__nav') nav: HTMLElement;
62
+ @query('.tab-group__indicator') indicator: HTMLElement;
63
+
64
+ @state() private hasScrollControls = false;
65
+
66
+ /** The placement of the tabs. */
67
+ @property() placement: 'top' | 'bottom' | 'start' | 'end' = 'top';
68
+
69
+ /**
70
+ * When set to auto, navigating tabs with the arrow keys will instantly show the corresponding tab panel. When set to
71
+ * manual, the tab will receive focus but will not show until the user presses spacebar or enter.
72
+ */
73
+ @property() activation: 'auto' | 'manual' = 'auto';
74
+
75
+ /** Disables the scroll arrows that appear when tabs overflow. */
76
+ @property({ attribute: 'no-scroll-controls', type: Boolean }) noScrollControls = false;
77
+
78
+ connectedCallback() {
79
+ const whenAllDefined = Promise.allSettled([
80
+ customElements.whenDefined('nile-tab'),
81
+ customElements.whenDefined('nile-tab-panel')
82
+ ]);
83
+
84
+ super.connectedCallback();
85
+
86
+ this.resizeObserver = new ResizeObserver(() => {
87
+ this.repositionIndicator();
88
+ this.updateScrollControls();
89
+ });
90
+
91
+ this.mutationObserver = new MutationObserver(mutations => {
92
+ // Update aria labels when the DOM changes
93
+ if (mutations.some(m => !['aria-labelledby', 'aria-controls'].includes(m.attributeName!))) {
94
+ setTimeout(() => this.setAriaLabels());
95
+ }
96
+
97
+ // Sync tabs when disabled states change
98
+ if (mutations.some(m => m.attributeName === 'disabled')) {
99
+ this.syncTabsAndPanels();
100
+ }
101
+ });
102
+
103
+ // After the first update...
104
+ this.updateComplete.then(() => {
105
+ this.syncTabsAndPanels();
106
+ this.mutationObserver.observe(this, { attributes: true, childList: true, subtree: true });
107
+ this.resizeObserver.observe(this.nav);
108
+
109
+ // Wait for tabs and tab panels to be registered
110
+ whenAllDefined.then(() => {
111
+ // Set initial tab state when the tabs become visible
112
+ const intersectionObserver = new IntersectionObserver((entries, observer) => {
113
+ if (entries[0].intersectionRatio > 0) {
114
+ this.setAriaLabels();
115
+ this.setActiveTab(this.getActiveTab() ?? this.tabs[0], { emitEvents: false });
116
+ observer.unobserve(entries[0].target);
117
+ }
118
+ });
119
+ intersectionObserver.observe(this.tabGroup);
120
+ });
121
+ });
122
+ }
123
+
124
+ disconnectedCallback() {
125
+ this.mutationObserver.disconnect();
126
+ this.resizeObserver.unobserve(this.nav);
127
+ }
128
+
129
+ private getAllTabs(options: { includeDisabled: boolean } = { includeDisabled: true }) {
130
+ const slot = this.shadowRoot!.querySelector<HTMLSlotElement>('slot[name="nav"]')!;
131
+
132
+ return [...(slot.assignedElements() as NileTab[])].filter(el => {
133
+ return options.includeDisabled
134
+ ? el.tagName.toLowerCase() === 'nile-tab'
135
+ : el.tagName.toLowerCase() === 'nile-tab' && !el.disabled;
136
+ });
137
+ }
138
+
139
+ private getAllPanels() {
140
+ return [...this.body.assignedElements()].filter(el => el.tagName.toLowerCase() === 'nile-tab-panel') as [NileTabPanel];
141
+ }
142
+
143
+ private getActiveTab() {
144
+ return this.tabs.find(el => el.active);
145
+ }
146
+
147
+ private handleClick(event: MouseEvent) {
148
+ const target = event.target as HTMLElement;
149
+ const tab = target.closest('nile-tab');
150
+ const tabGroup = tab?.closest('nile-tab-group');
151
+
152
+ // Ensure the target tab is in this tab group
153
+ if (tabGroup !== this) {
154
+ return;
155
+ }
156
+
157
+ if (tab !== null) {
158
+ this.setActiveTab(tab, { scrollBehavior: 'smooth' });
159
+ }
160
+ }
161
+
162
+ private handleKeyDown(event: KeyboardEvent) {
163
+ const target = event.target as HTMLElement;
164
+ const tab = target.closest('nile-tab');
165
+ const tabGroup = tab?.closest('nile-tab-group');
166
+
167
+ // Ensure the target tab is in this tab group
168
+ if (tabGroup !== this) {
169
+ return;
170
+ }
171
+
172
+ // Activate a tab
173
+ if (['Enter', ' '].includes(event.key)) {
174
+ if (tab !== null) {
175
+ this.setActiveTab(tab, { scrollBehavior: 'smooth' });
176
+ event.preventDefault();
177
+ }
178
+ }
179
+
180
+ // Move focus left or right
181
+ if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) {
182
+ const activeEl = this.tabs.find(t => t.matches(':focus'));
183
+ const isRtl = false;
184
+
185
+ if (activeEl?.tagName.toLowerCase() === 'nile-tab') {
186
+ let index = this.tabs.indexOf(activeEl);
187
+
188
+ if (event.key === 'Home') {
189
+ index = 0;
190
+ } else if (event.key === 'End') {
191
+ index = this.tabs.length - 1;
192
+ } else if (
193
+ (['top', 'bottom'].includes(this.placement) && event.key === (isRtl ? 'ArrowRight' : 'ArrowLeft')) ||
194
+ (['start', 'end'].includes(this.placement) && event.key === 'ArrowUp')
195
+ ) {
196
+ index--;
197
+ } else if (
198
+ (['top', 'bottom'].includes(this.placement) && event.key === (isRtl ? 'ArrowLeft' : 'ArrowRight')) ||
199
+ (['start', 'end'].includes(this.placement) && event.key === 'ArrowDown')
200
+ ) {
201
+ index++;
202
+ }
203
+
204
+ if (index < 0) {
205
+ index = this.tabs.length - 1;
206
+ }
207
+
208
+ if (index > this.tabs.length - 1) {
209
+ index = 0;
210
+ }
211
+
212
+ this.tabs[index].focus({ preventScroll: true });
213
+
214
+ if (this.activation === 'auto') {
215
+ this.setActiveTab(this.tabs[index], { scrollBehavior: 'smooth' });
216
+ }
217
+
218
+ if (['top', 'bottom'].includes(this.placement)) {
219
+ scrollIntoView(this.tabs[index], this.nav, 'horizontal');
220
+ }
221
+
222
+ event.preventDefault();
223
+ }
224
+ }
225
+ }
226
+
227
+ private handleScrollToStart() {
228
+ this.nav.scroll({
229
+ left:
230
+ true
231
+ ? this.nav.scrollLeft + this.nav.clientWidth
232
+ : this.nav.scrollLeft - this.nav.clientWidth,
233
+ behavior: 'smooth'
234
+ });
235
+ }
236
+
237
+ private handleScrollToEnd() {
238
+ this.nav.scroll({
239
+ left:
240
+ true
241
+ ? this.nav.scrollLeft - this.nav.clientWidth
242
+ : this.nav.scrollLeft + this.nav.clientWidth,
243
+ behavior: 'smooth'
244
+ });
245
+ }
246
+
247
+ private setActiveTab(tab: NileTab, options?: { emitEvents?: boolean; scrollBehavior?: 'auto' | 'smooth' }) {
248
+
249
+ options = {
250
+ emitEvents: true,
251
+ scrollBehavior: 'auto',
252
+ ...options
253
+ };
254
+
255
+ if (tab !== this.activeTab && !tab.disabled) {
256
+
257
+ const previousTab = this.activeTab;
258
+ this.activeTab = tab;
259
+
260
+
261
+ // Sync active tab and panel
262
+ this.tabs.map(el => (el.active = el === this.activeTab));
263
+ this.panels.map(el => (el.active = el.name === this.activeTab?.panel));
264
+ this.syncIndicator();
265
+
266
+ if (['top', 'bottom'].includes(this.placement)) {
267
+ scrollIntoView(this.activeTab, this.nav, 'horizontal', options.scrollBehavior);
268
+ }
269
+
270
+ // Emit events
271
+ if (options.emitEvents) {
272
+ if (previousTab) {
273
+ this.emit('nile-tab-hide', { value: previousTab.panel });
274
+ }
275
+
276
+ this.emit('nile-tab-show', { value: this.activeTab.panel });
277
+ }
278
+ }
279
+ }
280
+
281
+ private setAriaLabels() {
282
+ // Link each tab with its corresponding panel
283
+ this.tabs.forEach(tab => {
284
+ const panel = this.panels.find(el => el.name === tab.panel);
285
+ if (panel) {
286
+ tab.setAttribute('aria-controls', panel.getAttribute('id')!);
287
+ panel.setAttribute('aria-labelledby', tab.getAttribute('id')!);
288
+ }
289
+ });
290
+ }
291
+
292
+ private repositionIndicator() {
293
+ const currentTab = this.getActiveTab();
294
+
295
+ if (!currentTab) {
296
+ return;
297
+ }
298
+
299
+ const width = currentTab.clientWidth;
300
+ const height = currentTab.clientHeight;
301
+ const isRtl = false;
302
+
303
+ // We can't used offsetLeft/offsetTop here due to a shadow parent issue where neither can getBoundingClientRect
304
+ // because it provides invalid values for animating elements: https://bugs.chromium.org/p/chromium/issues/detail?id=920069
305
+ const allTabs = this.getAllTabs();
306
+ const precedingTabs = allTabs.slice(0, allTabs.indexOf(currentTab));
307
+ const offset = precedingTabs.reduce(
308
+ (previous, current) => ({
309
+ left: previous.left + current.clientWidth + 24,
310
+ top: previous.top + current.clientHeight
311
+ }),
312
+ { left: 0, top: 0 }
313
+ );
314
+
315
+ switch (this.placement) {
316
+ case 'top':
317
+ case 'bottom':
318
+ this.indicator.style.width = `${width}px`;
319
+ this.indicator.style.height = 'auto';
320
+ this.indicator.style.translate = `${offset.left}px`;
321
+ break;
322
+
323
+ case 'start':
324
+ case 'end':
325
+ this.indicator.style.width = 'auto';
326
+ this.indicator.style.height = `${height}px`;
327
+ this.indicator.style.translate = `0 ${offset.top}px`;
328
+ break;
329
+ }
330
+ }
331
+
332
+ // This stores tabs and panels so we can refer to a cache instead of calling querySelectorAll() multiple times.
333
+ private syncTabsAndPanels() {
334
+ this.tabs = this.getAllTabs({ includeDisabled: false });
335
+ this.panels = this.getAllPanels();
336
+ this.syncIndicator();
337
+
338
+ // After updating, show or hide scroll controls as needed
339
+ this.updateComplete.then(() => this.updateScrollControls());
340
+ }
341
+
342
+ @watch('noScrollControls', { waitUntilFirstUpdate: true })
343
+ updateScrollControls() {
344
+ if (this.noScrollControls) {
345
+ this.hasScrollControls = false;
346
+ } else {
347
+ this.hasScrollControls =
348
+ ['top', 'bottom'].includes(this.placement) && this.nav.scrollWidth > this.nav.clientWidth;
349
+ this.hasScrollControls = false;
350
+ }
351
+ }
352
+
353
+ @watch('placement', { waitUntilFirstUpdate: true })
354
+ syncIndicator() {
355
+ const tab = this.getActiveTab();
356
+
357
+ if (tab) {
358
+ this.indicator.style.display = 'block';
359
+ this.repositionIndicator();
360
+ } else {
361
+ this.indicator.style.display = 'none';
362
+ }
363
+ }
364
+
365
+ /** Shows the specified tab panel. */
366
+ show(panel: string) {
367
+ const tab = this.tabs.find(el => el.panel === panel);
368
+
369
+ if (tab) {
370
+ this.setActiveTab(tab, { scrollBehavior: 'smooth' });
371
+ }
372
+ }
373
+
374
+ render() {
375
+ const isRtl = true;
376
+
377
+ return html`
378
+ <div
379
+ part="base"
380
+ class=${classMap({
381
+ 'tab-group': true,
382
+ 'tab-group--top': this.placement === 'top',
383
+ 'tab-group--bottom': this.placement === 'bottom',
384
+ 'tab-group--start': this.placement === 'start',
385
+ 'tab-group--end': this.placement === 'end',
386
+ 'tab-group--rtl': true,
387
+ 'tab-group--has-scroll-controls': this.hasScrollControls
388
+ })}
389
+ @click=${this.handleClick}
390
+ @keydown=${this.handleKeyDown}
391
+ >
392
+ <div class="tab-group__nav-container" part="nav">
393
+ ${this.hasScrollControls
394
+ ? html`
395
+ <nile-icon-button
396
+ part="scroll-button scroll-button--start"
397
+ exportparts="base:scroll-button__base"
398
+ class="tab-group__scroll-button tab-group__scroll-button--start"
399
+ name='arrowright'
400
+ library="system"
401
+ label="scrollToStart"
402
+ @click=${this.handleScrollToStart}
403
+ ></nile-icon-button>
404
+ `
405
+ : ''}
406
+
407
+ <div class="tab-group__nav">
408
+ <div part="tabs" class="tab-group__tabs" role="tablist">
409
+ <div part="active-tab-indicator" class="tab-group__indicator"></div>
410
+ <slot name="nav" @slotchange=${this.syncTabsAndPanels}></slot>
411
+ </div>
412
+ </div>
413
+
414
+ ${this.hasScrollControls
415
+ ? html`
416
+ <nile-icon-button
417
+ part="scroll-button scroll-button--end"
418
+ exportparts="base:scroll-button__base"
419
+ class="tab-group__scroll-button tab-group__scroll-button--end"
420
+ name='arrowleft'
421
+ library="system"
422
+ label="scrollToEnd"
423
+ @click=${this.handleScrollToEnd}
424
+ ></nile-icon-button>
425
+ `
426
+ : ''}
427
+ </div>
428
+
429
+ <slot part="body" class="tab-group__body" @slotchange=${this.syncTabsAndPanels}></slot>
430
+ </div>
431
+ `;
432
+ }
433
+ }
434
+
435
+ export default NileTabGroup;
436
+
437
+ declare global {
438
+ interface HTMLElementTagNameMap {
439
+ 'nile-tab-group': NileTabGroup;
440
+ }
441
+ }
@@ -0,0 +1 @@
1
+ export { NileTabPanel } from './nile-tab-panel';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Copyright Aquera Inc 2023
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {css} from 'lit-element';
9
+
10
+ /**
11
+ * TabPanel CSS
12
+ */
13
+ export const styles = css`
14
+ [hidden] {
15
+ display: none !important;
16
+ }
17
+
18
+ :host {
19
+ --padding: 0;
20
+
21
+ display: none;
22
+ }
23
+
24
+ :host([active]) {
25
+ display: block;
26
+ }
27
+
28
+ .tab-panel {
29
+ display: block;
30
+ padding: var(--padding);
31
+ }
32
+ `;
33
+
34
+ export default [styles];
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Copyright Aquera Inc 2023
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {LitElement, html, property, CSSResultArray, TemplateResult} from 'lit-element';
9
+ import { customElement } from 'lit/decorators.js';
10
+ import {styles} from './nile-tab-panel.css';
11
+
12
+ import { classMap } from 'lit/directives/class-map.js';
13
+ import { watch } from '../internal/watch';
14
+ import NileElement from '../internal/nile-element';
15
+ import type { CSSResultGroup } from 'lit';
16
+
17
+ let id = 0;
18
+
19
+ /**
20
+ * Nile icon component.
21
+ *
22
+ * @tag nile-tab-panel
23
+ *
24
+ * @slot - The tab panel's content.
25
+ *
26
+ * @csspart base - The component's base wrapper.
27
+ *
28
+ * @cssproperty --padding - The tab panel's padding.
29
+ */
30
+ @customElement('nile-tab-panel')
31
+ export class NileTabPanel extends NileElement {
32
+
33
+ static styles: CSSResultGroup = styles;
34
+
35
+ private readonly attrId = ++id;
36
+ private readonly componentId = `nile-tab-panel-${this.attrId}`;
37
+
38
+ /** The tab panel's name. */
39
+ @property({ reflect: true }) name = '';
40
+
41
+ /** When true, the tab panel will be shown. */
42
+ @property({ type: Boolean, reflect: true }) active = false;
43
+
44
+ connectedCallback() {
45
+ super.connectedCallback();
46
+ this.id = this.id.length > 0 ? this.id : this.componentId;
47
+ this.setAttribute('role', 'tabpanel');
48
+ }
49
+
50
+ @watch('active')
51
+ handleActiveChange() {
52
+ this.setAttribute('aria-hidden', this.active ? 'false' : 'true');
53
+ }
54
+
55
+ render() {
56
+ return html`
57
+ <slot
58
+ part="base"
59
+ class=${classMap({
60
+ 'tab-panel': true,
61
+ 'tab-panel--active': this.active
62
+ })}
63
+ ></slot>
64
+ `;
65
+ }
66
+ }
67
+
68
+ export default NileTabPanel;
69
+
70
+ declare global {
71
+ interface HTMLElementTagNameMap {
72
+ 'nile-tab-panel': NileTabPanel;
73
+ }
74
+ }