mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- Remove nested git repository from bugulma/frontend/.git - Add all frontend files to main repository tracking - Convert from separate frontend/backend repos to unified monorepo - Preserve all frontend code and development history as tracked files - Eliminate nested repository complexity for simpler development workflow This creates a proper monorepo structure with frontend and backend coexisting in the same repository for easier development and deployment.
144 lines
3.5 KiB
TypeScript
144 lines
3.5 KiB
TypeScript
import React from 'react';
|
|
|
|
interface TabComponentProps {
|
|
activeValue: string;
|
|
onValueChange: (value: string) => void;
|
|
}
|
|
|
|
export const Tabs = ({
|
|
children,
|
|
value,
|
|
onValueChange,
|
|
className = '',
|
|
}: {
|
|
children?: React.ReactNode;
|
|
value: string;
|
|
onValueChange: (value: string) => void;
|
|
className?: string;
|
|
}) => {
|
|
return (
|
|
<div className={`tabs-container ${className}`}>
|
|
{React.Children.map(children, (child) => {
|
|
if (React.isValidElement(child)) {
|
|
return React.cloneElement(child as React.ReactElement<TabComponentProps>, {
|
|
activeValue: value,
|
|
onValueChange,
|
|
});
|
|
}
|
|
return child;
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const TabsList = ({
|
|
children,
|
|
className = '',
|
|
activeValue,
|
|
onValueChange,
|
|
}: {
|
|
children?: React.ReactNode;
|
|
className?: string;
|
|
activeValue?: string;
|
|
onValueChange?: (value: string) => void;
|
|
}) => {
|
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
const tabs = Array.from(e.currentTarget.querySelectorAll('[role="tab"]')) as HTMLElement[];
|
|
const currentIndex = tabs.findIndex((tab) => tab === e.target);
|
|
let newIndex = currentIndex;
|
|
|
|
if (e.key === 'ArrowLeft') {
|
|
newIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
|
|
} else if (e.key === 'ArrowRight') {
|
|
newIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
|
|
} else if (e.key === 'Home') {
|
|
newIndex = 0;
|
|
} else if (e.key === 'End') {
|
|
newIndex = tabs.length - 1;
|
|
}
|
|
|
|
if (newIndex !== currentIndex) {
|
|
e.preventDefault();
|
|
tabs[newIndex]?.focus();
|
|
tabs[newIndex]?.click();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
role="tablist"
|
|
aria-label="Content tabs"
|
|
className={`flex border-b ${className}`}
|
|
onKeyDown={handleKeyDown}
|
|
>
|
|
{React.Children.map(children, (child) => {
|
|
if (React.isValidElement(child)) {
|
|
const childProps = child.props as { value: string };
|
|
return React.cloneElement(child, {
|
|
isActive: childProps.value === activeValue,
|
|
onClick: () => onValueChange?.(childProps.value),
|
|
});
|
|
}
|
|
return child;
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const TabsTrigger = ({
|
|
children,
|
|
value,
|
|
isActive,
|
|
onClick,
|
|
className = '',
|
|
}: {
|
|
value: string;
|
|
children?: React.ReactNode;
|
|
isActive?: boolean;
|
|
onClick?: () => void;
|
|
className?: string;
|
|
}) => {
|
|
return (
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={isActive}
|
|
aria-controls={`tabpanel-${value}`}
|
|
id={`tab-${value}`}
|
|
tabIndex={isActive ? 0 : -1}
|
|
onClick={onClick}
|
|
className={`px-4 py-2 text-sm font-medium -mb-px focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 ${isActive ? 'border-b-2 border-primary text-primary' : 'text-muted-foreground hover:text-foreground'} ${className}`}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
};
|
|
TabsTrigger.displayName = 'TabsTrigger';
|
|
|
|
export const TabsContent = ({
|
|
children,
|
|
value,
|
|
className = '',
|
|
activeValue,
|
|
}: {
|
|
value: string;
|
|
children?: React.ReactNode;
|
|
className?: string;
|
|
activeValue?: string;
|
|
}) => {
|
|
const isHidden = value !== activeValue;
|
|
return (
|
|
<div
|
|
role="tabpanel"
|
|
id={`tabpanel-${value}`}
|
|
aria-labelledby={`tab-${value}`}
|
|
tabIndex={isHidden ? -1 : 0}
|
|
hidden={isHidden}
|
|
className={`py-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 ${className}`}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
};
|
|
TabsContent.displayName = 'TabsContent';
|