Skip to content

Commit cf38182

Browse files
Cathy SillerRyan A. Johnson
authored andcommitted
feat(tabset): add tabset functionality
1 parent 36d1b96 commit cf38182

File tree

11 files changed

+288
-12
lines changed

11 files changed

+288
-12
lines changed

source/_less/reset.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ html {
2020
*::after {
2121
box-sizing: inherit;
2222
}
23+
24+
a {
25+
background-color: transparent;
26+
}

source/components/reveal/HxReveal.less

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@
2020
text-align: left;
2121
width: 100%;
2222

23-
&:empty {
24-
display: none;
25-
}
26-
2723
&:hover {
2824
cursor: pointer;
2925
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
window.addEventListener('WebComponentsReady', function () {
2+
const tagName = 'hx-tabpanel';
3+
const template = document.createElement('template');
4+
5+
template.innerHTML = `
6+
<style>${require('./HxTabpanel.less')}</style>
7+
${require('../reveal/HxReveal.html')}
8+
`;
9+
10+
class HxTabpanel extends HTMLElement {
11+
static get is () {
12+
return tagName;
13+
}
14+
15+
constructor () {
16+
super();
17+
this.attachShadow({ mode: 'open' });
18+
if (window.ShadyCSS) {
19+
ShadyCSS.prepareTemplate(template, tagName);
20+
ShadyCSS.styleElement(this);
21+
}
22+
this.shadowRoot.appendChild(template.content.cloneNode(true));
23+
}
24+
25+
connectedCallback () {
26+
if (!this.hasAttribute('role')) {
27+
this.setAttribute('role', 'tabpanel');
28+
}
29+
}
30+
31+
set open (value) {
32+
if (Boolean(value)) {
33+
this.setAttribute('open', '');
34+
} else {
35+
this.removeAttribute('open');
36+
}
37+
}
38+
39+
get open () {
40+
return this.hasAttribute('open');
41+
}
42+
}
43+
customElements.define(HxTabpanel.is, HxTabpanel)
44+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@import (reference) 'vars';
2+
@import '../reveal/HxReveal.less';
3+
4+
:host {
5+
&([open]) {
6+
border-color: @gray-300 transparent;
7+
border-style: solid;
8+
border-width: 1px;
9+
padding: 1.5rem 1.25rem;
10+
}
11+
}
12+
13+
#toggle {
14+
display: none;
15+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<div id ="head">
2+
<slot name="tabs"></slot>
3+
</div>
4+
<div id ="body">
5+
<slot></slot>
6+
</div>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
window.addEventListener('WebComponentsReady', function () {
2+
const tagName = 'hx-tabset';
3+
const template = document.createElement('template');
4+
5+
template.innerHTML = `
6+
<style>${require('./HxTabset.less')}</style>
7+
${require('./HxTabset.html')}
8+
`;
9+
10+
class HxTabset extends HTMLElement {
11+
static get is () {
12+
return tagName;
13+
}
14+
15+
static get observedAttributes () {
16+
return ['selected'];
17+
}
18+
19+
constructor () {
20+
super();
21+
this.attachShadow({mode: 'open'});
22+
if (window.ShadyCSS) {
23+
ShadyCSS.prepareTemplate(template, tagName);
24+
ShadyCSS.styleElement(this);
25+
}
26+
this.shadowRoot.appendChild(template.content.cloneNode(true));
27+
this.$head = this.shadowRoot.querySelector('#head');
28+
this._onHeadClick = this._onHeadClick.bind(this);
29+
}
30+
31+
connectedCallback () {
32+
this.$head.addEventListener('click', this._onHeadClick);
33+
if (!this.hasAttribute('role')) {
34+
this.setAttribute('role', 'tablist');
35+
}
36+
37+
if (!this.hasAttribute('selected')) {
38+
this._selectPanelByIndex(0);
39+
}
40+
}
41+
42+
disconnectedCallback () {
43+
this.$head.removeEventListener('click', this._onHeadClick);
44+
}
45+
46+
attributeChangedCallback (attr, oldValue, newValue) {
47+
this._selectPanelById(newValue);
48+
}
49+
50+
get selected () {
51+
return this.getAttribute('selected');
52+
}
53+
54+
set selected (id) {
55+
if (this.querySelector(`hx-tabpanel#${id}`)) {
56+
this.setAttribute('selected', id);
57+
} else {
58+
throw new Error(`Tab with id "${id}" not found`);
59+
}
60+
}
61+
62+
get tabs () {
63+
return Array.from(this.querySelectorAll(`.hxTab`));
64+
}
65+
66+
get panels () {
67+
return Array.from(this.querySelectorAll('hx-tabpanel'));
68+
}
69+
70+
_selectPanelById (id) {
71+
this._selectPanel(this.querySelector(`hx-tabpanel#${id}`));
72+
}
73+
74+
_selectPanelByIndex (idx) {
75+
if (idx < 0 || idx >= this.panels.length) {
76+
throw new Error(`Panel index out of bounds`);
77+
} else {
78+
this._selectPanel(this.panels[idx]);
79+
}
80+
}
81+
82+
_selectPanel (panel) {
83+
if (panel) {
84+
this._reset();
85+
panel.open = true;
86+
var selectedIndex = this.panels.indexOf(panel);
87+
this.tabs[selectedIndex].classList.add('current');
88+
}
89+
}
90+
91+
_reset () {
92+
this.panels.forEach(panel => panel.open = false);
93+
this.tabs.forEach(tab => tab.classList.remove('current'));
94+
}
95+
96+
_onHeadClick (event) {
97+
event.preventDefault();
98+
this._selectPanelByIndex(this.tabs.indexOf(event.target));
99+
}
100+
}
101+
customElements.define(HxTabset.is, HxTabset)
102+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
:host {
2+
background-color: inherit;
3+
display: block;
4+
}
5+
6+
slot {
7+
// Because Safari
8+
background-color: inherit;
9+
}
10+
11+
#head {
12+
background-color: inherit;
13+
}
Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,68 @@
11
---
22
title: Tabset
33
assets:
4-
- bootstrap.helix.css
4+
- helix-ui.css
5+
- helix-ui.js
56
---
67
{% extends 'component.njk' %}
78
{% block content %}
89
<!-- explanation goes here -->
910

11+
<section>
12+
<h2 class="hxSectionTitle" id="tabset">Tabset</h2>
1013
<div class="demo">
11-
<ul class="nav nav-tabs" role="tablist">
12-
<li role="presentation" class="active"><a href="#">Home</a></li>
13-
<li role="presentation"><a href="#">Profile</a></li>
14-
<li role="presentation"><a href="#">Messages</a></li>
15-
<li role="presentation" class="disabled"><a href="#">Not Available</a></li>
16-
</ul>
14+
<hx-tabset>
15+
<div slot="tabs">
16+
<a href="#" role="tab" class="hxTab">Cupcake Ipsum</a>
17+
<a href="#" role="tab" class="hxTab">Biscuit Marshmallow</a>
18+
<a href="#" role="tab" class="hxTab">Caramels Marzipan</a>
19+
</div>
20+
<hx-tabpanel id="cupcake">
21+
<p>
22+
Cupcake ipsum dolor sit amet pie. Tiramisu icing cake. Macaroon halvah apple pie. Carrot cake cheesecake chocolate bar toffee biscuit candy gummi bears. Apple pie chocolate cake dessert liquorice biscuit cupcake. Jelly sugar plum chocolate bar chocolate. Wafer fruitcake carrot cake jelly jujubes cookie danish. Sesame snaps gingerbread gingerbread pastry gummies jelly beans icing. Sesame snaps wafer lollipop. Cookie gummies icing marshmallow powder soufflé topping tart.
23+
</p>
24+
</hx-tabpanel>
25+
<hx-tabpanel id="biscuit">
26+
<p>
27+
Biscuit marshmallow chocolate cake tiramisu cupcake. Powder liquorice jelly cheesecake chocolate bar. Chocolate cake icing pudding. Tart ice cream cupcake. Cake chocolate bar carrot cake pie marshmallow sugar plum dessert. Pudding lollipop sugar plum topping sugar plum chocolate bar jelly-o pastry donut. Candy canes icing apple pie pie.
28+
</p>
29+
</hx-tabpanel>
30+
<hx-tabpanel id="caramel">
31+
<p>
32+
Caramels marzipan gummi bears. Cake chocolate bar cake jelly-o tart topping. Jelly sweet jelly soufflé jelly toffee fruitcake. Toffee tart pastry liquorice bonbon sugar plum sweet. Jelly-o tiramisu cotton candy candy canes jelly beans dragée macaroon dessert toffee. Sesame snaps danish jujubes gummies marshmallow sesame snaps chocolate cake bonbon dessert. Candy canes cupcake sugar plum jelly beans bear claw icing candy canes bear claw. Marshmallow gingerbread biscuit jelly-o.
33+
</p>
34+
</hx-tabpanel>
35+
</hx-tabset>
1736
</div>
37+
{% code 'html' %}
38+
<hx-tabset>
39+
<div slot="tabs">
40+
<a href="#" role="tab" class="hxTab">Cupcake Ipsum</a>
41+
<a href="#" role="tab" class="hxTab">Biscuit Marshmallow</a>
42+
<a href="#" role="tab" class="hxTab">Caramel Marzipan</a>
43+
</div>
44+
<hx-tabpanel id="cupcake">...</hx-tabpanel>
45+
<hx-tabpanel id="biscuit">...</hx-tabpanel>
46+
<hx-tabpanel id="caramel">...</hx-tabpanel>
47+
</hx-tabset>
48+
{% endcode %}
49+
</section>
1850

19-
<!-- TODO: need content in tab body -->
51+
<section>
52+
<h2 class="hxSectionTitle" id="attributes">Attributes</h2>
53+
<dl>
54+
<dt>selected</dt>
55+
<dd>Selects the tab and panel</dd>
56+
</dl>
57+
</section>
58+
59+
<section>
60+
<h2 class="hxSectionTitle" id="properties">Properties</h2>
61+
<dl>
62+
<dt>panels <small>(read-only) Array&lt;Element&gt;</small></dt>
63+
<dt>selected</dt>
64+
<dd>Determines if tab and panel is selected</dd>
65+
<dt>tabs <small>(read-only) Array&lt;Element&gt;</small></dt>
66+
</dl>
67+
</section>
2068
{% endblock %}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
hx-tabset {
2+
display: block;
3+
4+
[slot="tabs"] {
5+
background-color: inherit;
6+
}
7+
8+
.hxTab {
9+
background-color: transparent;
10+
border-radius: 2px 2px 0 0;
11+
border: 1px solid transparent;
12+
bottom: -1px;
13+
box-sizing: border-box;
14+
color: @gray-700;
15+
display: inline-block;
16+
font-size: 0.875rem;
17+
font-weight: 500;
18+
line-height: 1;
19+
margin-left: 8px;
20+
padding: 0.75rem 0.75rem 0.5rem;
21+
position: relative;
22+
23+
&:hover {
24+
color: @cyan-500;
25+
}
26+
27+
&:active {
28+
color: @cyan-700;
29+
}
30+
31+
&.current {
32+
background-color: inherit;
33+
border-color: @gray-300 @gray-300 transparent;
34+
color: @cyan-900;
35+
}
36+
}
37+
38+
hx-tabpanel {
39+
min-height: 172px; //min height
40+
}
41+
42+
hx-tabpanel:not([open]) {
43+
display: none;
44+
}
45+
}

source/helix-ui.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@
99
import './components/checkbox/HxCheckbox';
1010
import './components/icon/HxIcon';
1111
import './components/reveal/HxReveal';
12+
import './components/tabset/HxTabpanel';
13+
import './components/tabset/HxTabset';

0 commit comments

Comments
 (0)