import cx from 'clsx'; import { isNotNil } from 'es-toolkit'; import { Component, createEffect, createMemo, createSignal, Index, JSX, mergeProps, onMount, Show, } from 'solid-js'; interface Option { label: string | JSX.Element; value: string | number | symbol | boolean; } interface SegmentsProps { name?: string; options: Option[]; value?: Option['value']; defaultValue?: Option['value']; direction?: 'horizontal' | 'vertical'; disabled?: boolean; onChange?: (value: Option['value'] | undefined) => void; } const Segments: Component = (props) => { const mProps = mergeProps( { options: [], disabled: false, direction: 'horizontal', }, props, ); const optionRefs: HTMLDivElement[] = []; const originalValue = createMemo(() => mProps.value); const [selected, setSelected] = createSignal(undefined); const [indicatorTop, setIndicatorTop] = createSignal(0); const [indicatorLeft, setIndicatorLeft] = createSignal(0); const [indicatorHeight, setIndicatorHeight] = createSignal(0); const [indicatorWidth, setIndicatorWidth] = createSignal(0); onMount(() => { if (isNotNil(mProps.defaultValue)) { setSelected(mProps.defaultValue); } }); createEffect(() => { if (isNotNil(originalValue()) && originalValue() !== selected()) { setSelected(originalValue()); } }); createEffect(() => { const selectedIndex = mProps.options?.findIndex((option) => option.value === selected()); if (isNotNil(selectedIndex) && optionRefs.length > 0 && optionRefs.length > selectedIndex) { const optionElement = optionRefs[selectedIndex]; if (isNotNil(optionElement)) { setIndicatorTop(optionElement.offsetTop); setIndicatorLeft(optionElement.offsetLeft); setIndicatorHeight(optionElement.offsetHeight); setIndicatorWidth(optionElement.offsetWidth); } } }); const handleSelect = (value: Option['value']) => { if (mProps.disabled) { return; } setSelected(value); mProps.onChange?.(value); }; return (
{(option, index) => (
(optionRefs[index] = el)} aria-disabled={mProps.disabled} class={cx( 'z-[5] cursor-pointer rounded-sm px-2 py-1', selected() === option().value ? 'not-aria-disabled:text-on-primary-surface aria-disabled:text-primary-disabled hover:not-aria-disabled:bg-primary-surface-hover/45' : 'not-aria-disabled:text-on-surface aria-disabled:text-surface-disabled hover:not-aria-disabled:bg-surface/35', )} onClick={() => handleSelect(option().value)}> {option().label}
)}
); }; export default Segments;