-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtabs.tsx
More file actions
103 lines (89 loc) · 2.29 KB
/
tabs.tsx
File metadata and controls
103 lines (89 loc) · 2.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import {
Component,
type ComponentInterface,
Element,
Event,
type EventEmitter,
Host,
h,
Listen,
Prop,
State,
Watch,
} from "@stencil/core";
/**
* The tabs component is used to create a tabbed interface. It manages the state
* of which tab is active and displays an indicator under the active tab. Use
* `ScoutTabsTab` components to define the individual tabs.
*
* Currently there is no support for navigational tabs. Navigation has to be
* handled programatically for now.
*/
@Component({
tag: "scout-tabs",
styleUrl: "tabs.css",
shadow: {
delegatesFocus: true,
},
})
export class ScoutTabs implements ComponentInterface {
@Element() el: HTMLElement;
@Prop()
public value: number = 0;
@State()
private widths: number[] = [];
@State()
private lefts: number[] = [];
@Event()
public scoutChange: EventEmitter<{ value: number }>;
render() {
return (
<Host>
<slot />
{this.getIndicator()}
</Host>
);
}
componentDidLoad() {
this.updateChildrenClasses();
this.calculateIndicatorSizes();
}
getIndicator() {
const width = this.widths[this.value] || 0;
const left = this.lefts[this.value] || 0;
const indicatorStyle = {
width: `${width}px`,
transform: `translateX(${left}px)`,
};
return <div aria-hidden="true" class="indicator" style={indicatorStyle} />;
}
@Listen("click", { capture: true })
handleClick(event: MouseEvent) {
const target = event.target as HTMLElement;
const tabs = Array.from(this.el.children);
const clickedIndex = tabs.indexOf(target);
if (clickedIndex !== -1 && clickedIndex !== this.value) {
this.scoutChange.emit({ value: clickedIndex });
}
}
@Watch("value")
updateChildrenClasses() {
Array.from(this.el.children).forEach((child, index) => {
const tab = child as HTMLElement;
if (index === this.value) {
tab.setAttribute("data-active", "true");
} else {
tab.removeAttribute("data-active");
}
});
}
@Watch("value")
calculateIndicatorSizes() {
this.widths = Array.from(this.el.children).map(
(child) => (child as HTMLElement).offsetWidth,
);
this.lefts = this.widths.map((_, index) =>
this.widths.slice(0, index).reduce((acc, w) => acc + w, 0),
);
}
}