-
-
Notifications
You must be signed in to change notification settings - Fork 315
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Mahmoud-zino <mahmoud.alhalaby@gmail.cim> Co-authored-by: endigo9740 <gundamx9740@gmail.com>
- Loading branch information
1 parent
007f848
commit 201d370
Showing
26 changed files
with
667 additions
and
153 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
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
33 changes: 33 additions & 0 deletions
33
packages/skeleton-svelte/src/lib/components/Tab/Tabs.svelte
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,33 @@ | ||
<script lang="ts"> | ||
import type { TabsProps } from './types.js'; | ||
let { | ||
id, | ||
// Root | ||
base = 'w-full', | ||
spaceY = 'space-y-4', | ||
classes = '', | ||
// Tab List | ||
listBase = 'flex', | ||
listJustify = 'justify-start', | ||
listGap = 'gap-2', | ||
listBorder = 'border-b-[1px] border-surface-200-800', | ||
listClasses = '', | ||
// Snippets | ||
list, | ||
panels | ||
}: TabsProps = $props(); | ||
</script> | ||
|
||
<!-- @component A Tab parent component. --> | ||
|
||
<div {id} class="{base} {spaceY} {classes}" data-testid="tabs"> | ||
{#if list} | ||
<div class="{listBase} {listGap} {listJustify} {listBorder} {listClasses}" role="tablist"> | ||
{@render list()} | ||
</div> | ||
{/if} | ||
{#if panels} | ||
{@render panels()} | ||
{/if} | ||
</div> |
138 changes: 138 additions & 0 deletions
138
packages/skeleton-svelte/src/lib/components/Tab/TabsControl.svelte
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,138 @@ | ||
<script lang="ts"> | ||
import type { TabsControlProps } from './types.js'; | ||
let { | ||
id, | ||
name, | ||
group, | ||
title, | ||
// A11y | ||
label = '', | ||
controls = '', | ||
// Root | ||
base = 'group', | ||
active = 'text-surface-950-50 border-surface-950-50', | ||
inactive = 'text-surface-600-400 border-transparent', | ||
flex = 'flex justify-center items-center', | ||
background = '', | ||
border = 'border-b-[1px]', | ||
text = 'type-scale-3', | ||
padding = 'pb-2', | ||
rounded = '', | ||
gap = 'gap-1', | ||
cursor = 'cursor-pointer', | ||
classes = '', | ||
// Content | ||
contentBase = 'w-full', | ||
contentFlex = 'flex justify-center items-center', | ||
contentGap = 'gap-2', | ||
contentBg = 'group-hover:preset-tonal-primary', | ||
contentPadding = 'p-2 px-4', | ||
contentRounded = 'rounded', | ||
contentClasses = '', | ||
// Events | ||
onclick = () => {}, | ||
onkeypress = () => {}, | ||
onkeydown = () => {}, | ||
onkeyup = () => {}, | ||
onchange = () => {}, | ||
// Snippets | ||
children | ||
}: TabsControlProps = $props(); | ||
const selected = $derived(group === name); | ||
const rxActive = $derived(selected ? active : inactive); | ||
let elemInput: HTMLInputElement; | ||
function onKeyDownHandler(event: KeyboardEvent) { | ||
// Fire Event Handler | ||
onkeydown(event); | ||
// If select key events | ||
if (!['ArrowRight', 'ArrowLeft', 'Home', 'End'].includes(event.code)) return; | ||
// Prevent default behavior | ||
event.preventDefault(); | ||
// Find the closest tab/tablelist | ||
const currTab = elemInput.closest('[role="tab"]'); | ||
if (!currTab) return; | ||
const tabList = elemInput.closest('[role="tablist"]'); | ||
if (!tabList) return; | ||
// Get RTL mode | ||
const isRTL = getComputedStyle(tabList).direction === 'rtl'; | ||
// Get list of tab elements | ||
const tabs = Array.from(tabList.querySelectorAll('[role="tab"]')); | ||
// Get a reference to the current tab | ||
const currIndex = tabs.indexOf(currTab); | ||
// Determine the index of the next tab | ||
let nextIndex = -1; | ||
switch (event.code) { | ||
case 'ArrowRight': | ||
if (isRTL) { | ||
nextIndex = currIndex - 1 < 0 ? tabs.length - 1 : currIndex - 1; | ||
break; | ||
} | ||
nextIndex = currIndex + 1 >= tabs.length ? 0 : currIndex + 1; | ||
break; | ||
case 'ArrowLeft': | ||
if (isRTL) { | ||
nextIndex = currIndex + 1 >= tabs.length ? 0 : currIndex + 1; | ||
break; | ||
} | ||
nextIndex = currIndex - 1 < 0 ? tabs.length - 1 : currIndex - 1; | ||
break; | ||
case 'Home': | ||
nextIndex = 0; | ||
break; | ||
case 'End': | ||
nextIndex = tabs.length - 1; | ||
break; | ||
} | ||
if (nextIndex < 0) return; | ||
// Set Active Tab | ||
const nextTab = tabs![nextIndex!]; | ||
const nextTabInput = nextTab?.querySelector('input'); | ||
if (nextTabInput) { | ||
nextTabInput.click(); | ||
(nextTab as HTMLDivElement).focus(); | ||
} | ||
} | ||
</script> | ||
|
||
<!-- @component A Tab Control component. --> | ||
|
||
<label | ||
{id} | ||
class="{base} {rxActive} {flex} {background} {border} {text} {padding} {rounded} {gap} {cursor} {classes}" | ||
aria-label={label} | ||
{title} | ||
> | ||
<!-- NOTE: do not add additional classes to this <div> --> | ||
<div | ||
class="size-full" | ||
role="tab" | ||
aria-controls={controls} | ||
aria-selected={selected} | ||
data-testid="tabs-control" | ||
tabindex={selected ? 0 : -1} | ||
onkeydown={onKeyDownHandler} | ||
{onkeypress} | ||
{onkeyup} | ||
> | ||
<!-- Keep these classes on wrapping element --> | ||
<div class="h-0 w-0 flex-none overflow-hidden"> | ||
<input bind:group bind:this={elemInput} type="radio" {name} value={name} onchange={() => onchange(group)} {onclick} tabindex="-1" /> | ||
</div> | ||
<!-- Content --> | ||
{#if children} | ||
<div class="{contentBase} {contentFlex} {contentGap} {contentBg} {contentPadding} {contentRounded} {contentClasses}"> | ||
{@render children()} | ||
</div> | ||
{/if} | ||
</div> | ||
</label> |
23 changes: 23 additions & 0 deletions
23
packages/skeleton-svelte/src/lib/components/Tab/TabsPanel.svelte
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,23 @@ | ||
<script lang="ts"> | ||
import type { TabsPanelProps } from './types.js'; | ||
let { | ||
id, | ||
value, | ||
group, | ||
// A11y | ||
labelledBy, | ||
// Root | ||
classes = '', | ||
// Snippets | ||
children | ||
}: TabsPanelProps = $props(); | ||
</script> | ||
|
||
<!-- @component A Tab Panel component. --> | ||
|
||
{#if value === group && children} | ||
<div {id} role="tabpanel" tabindex="0" aria-labelledby={labelledBy} class={classes}> | ||
{@render children()} | ||
</div> | ||
{/if} |
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,5 @@ | ||
import Tabs from './Tabs.svelte'; | ||
import Control from './TabsControl.svelte'; | ||
import Panel from './TabsPanel.svelte'; | ||
|
||
export default Object.assign(Tabs, { Control, Panel }); |
138 changes: 138 additions & 0 deletions
138
packages/skeleton-svelte/src/lib/components/Tab/types.ts
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,138 @@ | ||
import type { Snippet } from 'svelte'; | ||
|
||
// Tabs --- | ||
|
||
export interface TabsProps { | ||
/** Provide a unique ID. */ | ||
id?: string; | ||
|
||
// Root --- | ||
/** Sets base styles. */ | ||
base?: string; | ||
/** Set vertical spacing between list and panels. */ | ||
spaceY?: string; | ||
/** Provide arbitrary CSS classes. */ | ||
classes?: string; | ||
|
||
// Tab list --- | ||
/** Sets the list snippet element's base styles. */ | ||
listBase?: string; | ||
/** Sets the list snippet element's justification styles. */ | ||
listJustify?: string; | ||
/** Sets the list snippet element's gap spacing. */ | ||
listGap?: string; | ||
/** Sets the list snippet element's border styles. */ | ||
listBorder?: string; | ||
/** Provide arbitrary CSS classes to the list snippet. */ | ||
listClasses?: string; | ||
|
||
// Tab panel --- | ||
/** Provide arbitrary CSS classes to the tab panel snippet. */ | ||
panelClasses?: string; | ||
|
||
// Snippets --- | ||
/** The tab list slot. */ | ||
list?: Snippet; | ||
/** The tab panel slot. */ | ||
panels?: Snippet; | ||
} | ||
|
||
// TabControl --- | ||
|
||
export interface TabsControlProps { | ||
/** Provide a unique ID. */ | ||
id?: string; | ||
/** Provide the tab control name. */ | ||
name: string; | ||
/** Provide the tab control radio group. */ | ||
group: string; | ||
/** Provide a hoverable title attribute. */ | ||
title?: string; | ||
|
||
// A11y --- | ||
/** Sets the A11y label. */ | ||
label?: string; | ||
/** Sets ARIA controls value to define which panel this tab controls. */ | ||
controls?: string; | ||
|
||
// Root --- | ||
/** Sets base styles. */ | ||
base?: string; | ||
/** Sets the active control styles. */ | ||
active?: string; | ||
/** Sets the inactive control styles. */ | ||
inactive?: string; | ||
/** Sets flex styles. */ | ||
flex?: string; | ||
/** Sets background styles. */ | ||
background?: string; | ||
/** Sets border styles. */ | ||
border?: string; | ||
/** Sets text size styles. */ | ||
text?: string; | ||
/** Sets padding styles. */ | ||
padding?: string; | ||
/** Sets rounded styles. */ | ||
rounded?: string; | ||
/** Sets vertical gap styles. */ | ||
gap?: string; | ||
/** Sets cursor styles. */ | ||
cursor?: string; | ||
/** Provide arbitrary CSS classes. */ | ||
classes?: string; | ||
|
||
// Tab --- | ||
/** Sets tab content base styles. */ | ||
contentBase?: string; | ||
/** Sets tab content flex styles. */ | ||
contentFlex?: string; | ||
/** Sets the tab content gap styles. */ | ||
contentGap?: string; | ||
/** Sets the tab content background styles. */ | ||
contentBg?: string; | ||
/** Sets the tab content padding styles. */ | ||
contentPadding?: string; | ||
/** Sets the tab content rounded styles. */ | ||
contentRounded?: string; | ||
/** Provide arbitrary CSS classes for the tab content. */ | ||
contentClasses?: string; | ||
|
||
// Events --- | ||
/** Triggers on Tab Control click. */ | ||
onclick?: (event: MouseEvent) => void; | ||
/** Triggers on Tab Control key press. */ | ||
onkeypress?: (event: KeyboardEvent) => void; | ||
/** Triggers on Tab Control key down. */ | ||
onkeydown?: (event: KeyboardEvent) => void; | ||
/** Triggers on Tab Control key up. */ | ||
onkeyup?: (event: KeyboardEvent) => void; | ||
/** Triggers on Tab Control group change. */ | ||
onchange?: (group: string) => void; | ||
|
||
// Snippets --- | ||
/** The default child slot. */ | ||
children?: Snippet; | ||
} | ||
|
||
// TabPanel --- | ||
|
||
export interface TabsPanelProps { | ||
/** Provide a unique ID. */ | ||
id?: string; | ||
/** Provide the tab panel value. */ | ||
value: string; | ||
/** Provide the tab control radio group. */ | ||
group: string; | ||
|
||
// A11y --- | ||
/** Sets the A11y labelledby. */ | ||
labelledBy?: string; | ||
|
||
// Root --- | ||
/** Provide arbitrary CSS classes. */ | ||
classes?: string; | ||
|
||
// Snippets --- | ||
/** The default child slot. */ | ||
children?: Snippet; | ||
} |
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
Oops, something went wrong.