-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ld-tabs): tabs tablist tab tabpanellist tabpanel
- Loading branch information
1 parent
292c295
commit acc5964
Showing
12 changed files
with
988 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
.ld-tab { | ||
--ld-tab-padding-x: 0.875rem; | ||
--ld-tab-padding-y: 0.75rem; | ||
--ld-tab-min-height: 2.5rem; | ||
--ld-tab-color: var(--ld-col-rblck-default); | ||
--ld-tab-bg-color: var(--ld-col-bg-lg); | ||
--ld-tab-focus-color: var(--ld-col-rblck1); | ||
--ld-tab-focus-bg-color: var(--ld-col-rblck4); | ||
--ld-tab-hover-bg-color: var(--ld-col-rblck1); | ||
--ld-tab-active-bg-color: var(--ld-col-rblck2); | ||
--ld-tab-gap: 0.625rem; | ||
--ld-tab-icon-size: 1.25rem; | ||
position: relative; | ||
font: var(--ld-typo-body-m); | ||
font-weight: 700; | ||
border: 0; | ||
text-decoration: none; | ||
user-select: none; | ||
touch-action: manipulation; | ||
display: grid; | ||
grid-template-columns: minmax(0rem, var(--ld-sp-16)) max-content minmax( | ||
0rem, | ||
var(--ld-sp-16) | ||
); | ||
align-items: center; | ||
text-align: center; | ||
justify-content: center; | ||
line-height: 1; | ||
scroll-snap-align: start; | ||
white-space: nowrap; | ||
-webkit-touch-callout: none; | ||
padding: var(--ld-tab-padding-y) var(--ld-tab-padding-x); | ||
min-height: var(--ld-tab-min-height); | ||
color: var(--ld-tab-color); | ||
background-color: var(--ld-tab-bg-color); | ||
|
||
&[aria-disabled='true'] { | ||
--ld-tab-focus-bg-color: var(--ld-col-rblck3); | ||
--ld-tab-focus-color: var(--ld-col-wht); | ||
|
||
.ld-tab__content { | ||
opacity: 0.25; | ||
} | ||
} | ||
|
||
/* Selection indicator */ | ||
&::after { | ||
content: ''; | ||
position: absolute; | ||
inset: auto 0 0 0; | ||
height: var(--ld-sp-2); | ||
z-index: 1; | ||
} | ||
|
||
&[aria-selected='true'] { | ||
color: var(--ld-tab-color-selected); | ||
|
||
&::after { | ||
background-color: var(--ld-tab-color-selected); | ||
} | ||
} | ||
|
||
&:focus:focus-visible { | ||
outline: none; | ||
background-color: var(--ld-tab-focus-bg-color); | ||
|
||
&:not(:active), | ||
&[aria-disabled='true'] { | ||
color: var(--ld-tab-focus-color); | ||
} | ||
|
||
&[aria-selected='true'] { | ||
&:not(:active) { | ||
color: var(--ld-tab-selected-focus-color); | ||
} | ||
|
||
&::after { | ||
background-color: var(--ld-tab-selected-focus-color); | ||
} | ||
} | ||
|
||
&:not([aria-selected='true'])::after { | ||
background-color: inherit; | ||
} | ||
} | ||
|
||
&:where(:not([aria-disabled='true'])) { | ||
cursor: pointer; | ||
|
||
@media (hover: hover) { | ||
&:hover { | ||
background-color: var(--ld-tab-hover-bg-color); | ||
|
||
&:not([aria-selected='true'])::after { | ||
background-color: inherit; | ||
} | ||
} | ||
} | ||
&:active:focus, | ||
&:active:focus:focus-visible { | ||
background-color: var(--ld-tab-active-bg-color); | ||
|
||
&:not([aria-selected='true'])::after { | ||
background-color: inherit; | ||
} | ||
} | ||
} | ||
|
||
.ld-tabs--sm & { | ||
--ld-tab-padding-x: 0.625rem; | ||
--ld-tab-padding-y: 0.5rem; | ||
--ld-tab-min-height: 2rem; | ||
--ld-tab-gap: 0.375rem; | ||
--ld-tab-icon-size: 1rem; | ||
font: var(--ld-typo-body-s); | ||
} | ||
|
||
.ld-tabs--lg & { | ||
--ld-tab-padding-x: 1.3125rem; | ||
--ld-tab-padding-y: 0.75rem; | ||
--ld-tab-min-height: 3.125rem; | ||
--ld-tab-gap: 0.875rem; | ||
--ld-tab-icon-size: 1.5rem; | ||
font: var(--ld-typo-body-l); | ||
} | ||
|
||
.ld-tabs--sm &, | ||
.ld-tabs--lg & { | ||
line-height: 1; | ||
font-weight: 700; | ||
} | ||
|
||
:where(.ld-tabs--ghost) & { | ||
--ld-tab-bg-color: transparent; | ||
} | ||
|
||
.ld-tabs--brand-color & { | ||
--ld-tab-selected-focus-color: var(--ld-col-wht); | ||
|
||
&[aria-selected='true'] { | ||
color: var(--ld-col-wht); | ||
|
||
&:focus, | ||
&:focus:focus-visible { | ||
&::after { | ||
background-color: var(--ld-tab-color-selected); | ||
} | ||
} | ||
} | ||
|
||
&[aria-disabled='true'] { | ||
opacity: 0.5; | ||
|
||
.ld-tab__content { | ||
opacity: 1; | ||
} | ||
} | ||
|
||
&:not([aria-disabled='true']) { | ||
--ld-tab-color: var(--ld-col-wht); | ||
--ld-tab-focus-color: var(--ld-col-wht); | ||
} | ||
|
||
.ld-theme-ocean &, | ||
[class*='ld-theme'] .ld-theme-ocean & { | ||
--ld-tab-color-selected: var(--ld-thm-ocean-accent); | ||
--ld-tab-bg-color: var(--ld-thm-ocean-bg-primary); | ||
--ld-tab-focus-bg-color: var(--ld-col-rb-focus); | ||
--ld-tab-focus-color: var(--ld-col-rb6); | ||
--ld-tab-hover-bg-color: var(--ld-col-rb-hover); | ||
--ld-tab-active-bg-color: var(--ld-col-rb-active); | ||
|
||
&[aria-disabled='true'] { | ||
--ld-tab-focus-color: var(--ld-col-rb5); | ||
--ld-tab-focus-bg-color: var(--ld-col-rb6); | ||
--ld-tab-color: var(--ld-col-rb3); | ||
} | ||
} | ||
|
||
.ld-theme-tea &, | ||
[class*='ld-theme'] .ld-theme-tea & { | ||
--ld-tab-color-selected: var(--ld-thm-tea-accent); | ||
--ld-tab-bg-color: var(--ld-thm-tea-bg-primary); | ||
--ld-tab-focus-bg-color: var(--ld-col-rg-focus); | ||
--ld-tab-focus-color: var(--ld-col-rg6); | ||
--ld-tab-hover-bg-color: var(--ld-col-rg-hover); | ||
--ld-tab-active-bg-color: var(--ld-col-rg-active); | ||
|
||
&[aria-disabled='true'] { | ||
--ld-tab-focus-color: var(--ld-col-rg5); | ||
--ld-tab-focus-bg-color: var(--ld-col-rg6); | ||
--ld-tab-color: var(--ld-col-rg3); | ||
} | ||
} | ||
|
||
.ld-theme-bubblegum &, | ||
[class*='ld-theme'] .ld-theme-bubblegum & { | ||
--ld-tab-color-selected: var(--ld-thm-bubblegum-accent); | ||
--ld-tab-bg-color: var(--ld-thm-bubblegum-bg-primary); | ||
--ld-tab-focus-bg-color: var(--ld-col-rp-focus); | ||
--ld-tab-focus-color: var(--ld-col-rp6); | ||
--ld-tab-hover-bg-color: var(--ld-col-rp-hover); | ||
--ld-tab-active-bg-color: var(--ld-col-rp-active); | ||
|
||
&[aria-disabled='true'] { | ||
--ld-tab-focus-color: var(--ld-col-rp5); | ||
--ld-tab-focus-bg-color: var(--ld-col-rp6); | ||
--ld-tab-color: var(--ld-col-rp3); | ||
} | ||
} | ||
|
||
.ld-theme-shake &, | ||
[class*='ld-theme'] .ld-theme-shake & { | ||
--ld-tab-color-selected: var(--ld-thm-shake-accent); | ||
--ld-tab-bg-color: var(--ld-thm-shake-bg-primary); | ||
--ld-tab-focus-bg-color: var(--ld-col-rp-focus); | ||
--ld-tab-focus-color: var(--ld-col-rp6); | ||
--ld-tab-hover-bg-color: var(--ld-col-rp-hover); | ||
--ld-tab-active-bg-color: var(--ld-col-rp-active); | ||
|
||
&[aria-disabled='true'] { | ||
--ld-tab-focus-color: var(--ld-col-rp5); | ||
--ld-tab-focus-bg-color: var(--ld-col-rp6); | ||
--ld-tab-color: var(--ld-col-rp3); | ||
} | ||
} | ||
|
||
.ld-theme-solvent &, | ||
[class*='ld-theme'] .ld-theme-solvent & { | ||
--ld-tab-color-selected: var(--ld-thm-solvent-accent); | ||
--ld-tab-bg-color: var(--ld-thm-solvent-bg-primary); | ||
--ld-tab-focus-bg-color: var(--ld-col-rp-focus); | ||
--ld-tab-focus-color: var(--ld-col-rp6); | ||
--ld-tab-hover-bg-color: var(--ld-col-rp-hover); | ||
--ld-tab-active-bg-color: var(--ld-col-rp-active); | ||
|
||
&[aria-disabled='true'] { | ||
--ld-tab-focus-color: var(--ld-col-rp5); | ||
--ld-tab-focus-bg-color: var(--ld-col-rp6); | ||
--ld-tab-color: var(--ld-col-rp3); | ||
} | ||
} | ||
} | ||
} | ||
|
||
.ld-tab__spacer { | ||
display: inline-flex; | ||
flex-shrink: 1; | ||
width: 1rem; | ||
} | ||
|
||
.ld-tab__content { | ||
display: grid; | ||
grid-auto-flow: column; | ||
gap: var(--ld-tab-gap); | ||
align-items: center; | ||
|
||
ld-icon, | ||
.ld-icon { | ||
svg { | ||
width: var(--ld-tab-icon-size); | ||
height: var(--ld-tab-icon-size); | ||
} | ||
} | ||
} | ||
|
||
/* .ld-theme-ocean -> default */ | ||
.ld-tab, | ||
:where(.ld-theme-ocean) .ld-tab, | ||
:where([class*='ld-theme'] .ld-theme-ocean) .ld-tab { | ||
--ld-tab-color-selected: var(--ld-thm-ocean-bg-primary); | ||
--ld-tab-selected-focus-color: var(--ld-col-rb2); | ||
} | ||
|
||
:where(.ld-theme-tea), | ||
:where([class*='ld-theme'] .ld-theme-tea) { | ||
.ld-tab { | ||
--ld-tab-color-selected: var(--ld-thm-tea-bg-primary); | ||
--ld-tab-selected-focus-color: var(--ld-col-rg2); | ||
} | ||
} | ||
|
||
:where(.ld-theme-bubblegum), | ||
:where([class*='ld-theme'] .ld-theme-bubblegum) { | ||
.ld-tab { | ||
--ld-tab-color-selected: var(--ld-thm-bubblegum-bg-primary); | ||
--ld-tab-selected-focus-color: var(--ld-col-rp2); | ||
} | ||
} | ||
|
||
:where(.ld-theme-shake), | ||
:where([class*='ld-theme'] .ld-theme-shake) { | ||
.ld-tab { | ||
--ld-tab-color-selected: var(--ld-thm-shake-bg-primary); | ||
--ld-tab-selected-focus-color: var(--ld-col-rp2); | ||
} | ||
} | ||
|
||
:where(.ld-theme-solvent), | ||
:where([class*='ld-theme'] .ld-theme-solvent) { | ||
.ld-tab { | ||
--ld-tab-color-selected: var(--ld-thm-solvent-bg-primary); | ||
--ld-tab-selected-focus-color: var(--ld-col-rp2); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import '../../components' // type definitions for type checks and intelliSense | ||
import { | ||
Component, | ||
Element, | ||
Event, | ||
EventEmitter, | ||
h, | ||
Prop, | ||
Watch, | ||
} from '@stencil/core' | ||
|
||
/** | ||
* @virtualProp ref - reference to component | ||
* @virtualProp {string | number} key - for tracking the node's identity when working with lists | ||
*/ | ||
@Component({ | ||
tag: 'ld-tab', | ||
styleUrl: 'ld-tab.css', | ||
shadow: false, | ||
}) | ||
export class LdTab { | ||
@Element() el: HTMLElement | ||
|
||
/** | ||
* If present, this boolean attribute indicates that the tab is selected. | ||
*/ | ||
@Prop({ mutable: true, reflect: true }) selected?: boolean | ||
|
||
/** | ||
* Disables the tab. | ||
*/ | ||
@Prop() disabled?: boolean | ||
|
||
/** | ||
* Emitted with the id of the selected tab. | ||
*/ | ||
@Event() tabChange: EventEmitter<string> | ||
|
||
private handleTabClick(ev) { | ||
ev.preventDefault() | ||
|
||
if (this.disabled) return | ||
|
||
if (ev.currentTarget.getAttribute('aria-selected')) return | ||
|
||
this.selected = true | ||
} | ||
|
||
@Watch('selected') | ||
emitEvent(newSelected: boolean, oldSelected: boolean) { | ||
if (!newSelected || newSelected === oldSelected) return | ||
|
||
const index = Array.prototype.indexOf.call( | ||
this.el.closest('.ld-tablist__scroll-container').children, | ||
this.el | ||
) | ||
|
||
this.tabChange.emit(index) | ||
} | ||
|
||
render() { | ||
return ( | ||
<button | ||
class="ld-tab" | ||
role="tab" | ||
aria-selected={this.selected ? 'true' : undefined} | ||
aria-disabled={this.disabled ? 'true' : undefined} | ||
onClick={this.handleTabClick.bind(this)} | ||
tabindex={this.selected ? undefined : '-1'} | ||
> | ||
<span class="ld-tab__spacer"></span> | ||
<span class="ld-tab__content"> | ||
<slot></slot> | ||
</span> | ||
<span class="ld-tab__spacer"></span> | ||
</button> | ||
) | ||
} | ||
} |
Oops, something went wrong.