mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 10:21:05 +00:00
- Inline class assignments to avoid script-level clutter - Ignore a11y-missing-attribute instead of redundant href
113 lines
2.9 KiB
Svelte
113 lines
2.9 KiB
Svelte
<script>
|
|
let className = undefined;
|
|
export { className as class };
|
|
export let selected = 0;
|
|
export let iconDescription = 'Show menu options';
|
|
export let role = 'navigation';
|
|
export let type = 'default';
|
|
export let triggerHref = '#';
|
|
export let style = undefined;
|
|
|
|
import { createEventDispatcher, setContext } from 'svelte';
|
|
import { writable, derived } from 'svelte/store';
|
|
import ChevronDownGlyph from 'carbon-icons-svelte/lib/ChevronDownGlyph';
|
|
import { cx } from '../../lib';
|
|
|
|
const dispatch = createEventDispatcher();
|
|
|
|
let dropdownHidden = true;
|
|
let tabs = writable([]);
|
|
let tabsById = derived(tabs, _ => _.reduce((a, c) => ({ ...a, [c.id]: c }), {}));
|
|
let currentIndex = selected;
|
|
let selectedTab = writable(undefined);
|
|
let content = writable([]);
|
|
let selectedContent = writable(undefined);
|
|
|
|
setContext('Tabs', {
|
|
selectedTab,
|
|
selectedContent,
|
|
add: data => {
|
|
tabs.update(_ => [..._, { ...data, index: _.length }]);
|
|
},
|
|
addContent: data => {
|
|
content.update(_ => [..._, { ...data, index: _.length }]);
|
|
},
|
|
update: id => {
|
|
currentIndex = $tabsById[id].index;
|
|
},
|
|
change: direction => {
|
|
let index = currentIndex + direction;
|
|
|
|
if (index < 0) {
|
|
index = $tabs.length - 1;
|
|
} else if (index >= $tabs.length) {
|
|
index = 0;
|
|
}
|
|
|
|
let disabled = $tabs[index].disabled;
|
|
|
|
while (disabled) {
|
|
index = index + direction;
|
|
|
|
if (index < 0) {
|
|
index = $tabs.length - 1;
|
|
} else if (index >= $tabs.length) {
|
|
index = 0;
|
|
}
|
|
|
|
disabled = $tabs[index].disabled;
|
|
}
|
|
|
|
currentIndex = index;
|
|
}
|
|
});
|
|
|
|
$: currentTab = $tabs[currentIndex] || undefined;
|
|
$: currentContent = $content[currentIndex] || undefined;
|
|
$: {
|
|
selected = currentIndex;
|
|
dispatch('change', currentIndex);
|
|
|
|
if (currentTab) {
|
|
selectedTab.set(currentTab.id);
|
|
}
|
|
|
|
if (currentContent) {
|
|
selectedContent.set(currentContent.id);
|
|
}
|
|
}
|
|
$: if ($selectedTab) {
|
|
dropdownHidden = true;
|
|
}
|
|
</script>
|
|
|
|
<div class={cx('--tabs', type === 'container' && '--tabs--container', className)} {style} {role}>
|
|
<div
|
|
role="listbox"
|
|
tabindex="0"
|
|
class={cx('--tabs-trigger')}
|
|
aria-label={$$props['aria-label'] || 'listbox'}
|
|
on:click={() => {
|
|
dropdownHidden = !dropdownHidden;
|
|
}}
|
|
on:keypress
|
|
on:keypress={() => {
|
|
dropdownHidden = !dropdownHidden;
|
|
}}>
|
|
<a
|
|
tabindex="-1"
|
|
class={cx('--tabs-trigger-text')}
|
|
href={triggerHref}
|
|
on:click
|
|
on:click={() => {
|
|
dropdownHidden = !dropdownHidden;
|
|
}}>
|
|
{#if currentTab}{currentTab.label}{/if}
|
|
</a>
|
|
<ChevronDownGlyph aria-hidden="true" title={iconDescription} />
|
|
</div>
|
|
<ul role="tablist" class={cx('--tabs__nav', dropdownHidden && '--tabs__nav--hidden')}>
|
|
<slot />
|
|
</ul>
|
|
</div>
|
|
<slot name="content" />
|