96 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Svelte
		
	
	
	
	
	
| <script>import { getContext } from "svelte";
 | |
| export let group;
 | |
| export let name;
 | |
| export let value;
 | |
| export let title = "";
 | |
| export let controls = "";
 | |
| export let regionTab = "";
 | |
| export let active = getContext("active");
 | |
| export let hover = getContext("hover");
 | |
| export let flex = getContext("flex");
 | |
| export let padding = getContext("padding");
 | |
| export let rounded = getContext("rounded");
 | |
| export let spacing = getContext("spacing");
 | |
| const cBase = "text-center cursor-pointer transition-colors duration-100";
 | |
| const cInterface = "";
 | |
| let elemInput;
 | |
| function onKeyDown(event) {
 | |
|   if (["Enter", "Space"].includes(event.code)) {
 | |
|     event.preventDefault();
 | |
|     elemInput.click();
 | |
|   } else if (event.code === "ArrowRight") {
 | |
|     const tabList = elemInput.closest(".tab-list");
 | |
|     if (!tabList)
 | |
|       return;
 | |
|     const tabs = Array.from(tabList.querySelectorAll(".tab"));
 | |
|     const currTab = elemInput.closest(".tab");
 | |
|     if (!currTab)
 | |
|       return;
 | |
|     const currIndex = tabs.indexOf(currTab);
 | |
|     const nextIndex = currIndex + 1 >= tabs.length ? 0 : currIndex + 1;
 | |
|     const nextTab = tabs[nextIndex];
 | |
|     const nextTabInput = nextTab?.querySelector("input");
 | |
|     if (nextTab && nextTabInput) {
 | |
|       nextTabInput.click();
 | |
|       nextTab.focus();
 | |
|     }
 | |
|   } else if (event.code === "ArrowLeft") {
 | |
|     const tabList = elemInput.closest(".tab-list");
 | |
|     if (!tabList)
 | |
|       return;
 | |
|     const tabs = Array.from(tabList.querySelectorAll(".tab"));
 | |
|     const currTab = elemInput.closest(".tab");
 | |
|     if (!currTab)
 | |
|       return;
 | |
|     const currIndex = tabs.indexOf(currTab);
 | |
|     const nextIndex = currIndex - 1 < 0 ? tabs.length - 1 : currIndex - 1;
 | |
|     const nextTab = tabs[nextIndex];
 | |
|     const nextTabInput = nextTab?.querySelector("input");
 | |
|     if (nextTab && nextTabInput) {
 | |
|       nextTabInput.click();
 | |
|       nextTab.focus();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| $:
 | |
|   selected = value === group;
 | |
| $:
 | |
|   classesActive = selected ? active : hover;
 | |
| $:
 | |
|   classesBase = `${cBase} ${flex} ${padding} ${rounded} ${classesActive} ${$$props.class ?? ""}`;
 | |
| $:
 | |
|   classesInterface = `${cInterface} ${spacing}`;
 | |
| $:
 | |
|   classesTab = `${regionTab}`;
 | |
| function prunedRestProps() {
 | |
|   delete $$restProps.class;
 | |
|   return $$restProps;
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <label class={classesBase} {title}>
 | |
| 	<!-- A11y attributes are not allowed on <label> -->
 | |
| 	<div
 | |
| 		class="tab {classesTab}"
 | |
| 		data-testid="tab"
 | |
| 		role="tab"
 | |
| 		aria-controls={controls}
 | |
| 		aria-selected={selected}
 | |
| 		tabindex={selected ? 0 : -1}
 | |
| 		on:keydown={onKeyDown}
 | |
| 		on:keydown
 | |
| 		on:keyup
 | |
| 		on:keypress
 | |
| 	>
 | |
| 		<!-- NOTE: Don't use `hidden` as it prevents `required` from operating -->
 | |
| 		<div class="h-0 w-0 overflow-hidden">
 | |
| 			<input bind:this={elemInput} type="radio" bind:group {name} {value} {...prunedRestProps()} tabindex="-1" on:click on:change />
 | |
| 		</div>
 | |
| 		<!-- Interface -->
 | |
| 		<div class="tab-interface {classesInterface}">
 | |
| 			{#if $$slots.lead}<div class="tab-lead"><slot name="lead" /></div>{/if}
 | |
| 			<div class="tab-label"><slot /></div>
 | |
| 		</div>
 | |
| 	</div>
 | |
| </label>
 |