mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 00:32:43 +10:00
Add ability to enable or disable allowed signature types: - Drawn - Typed - Uploaded **Tabbed style signature dialog**  **Document settings**  **Team preferences**  - Add multiselect to select allowed signatures in document and templates settings tab - Add multiselect to select allowed signatures in teams preferences - Removed "Enable typed signatures" from document/template edit page - Refactored signature pad to use tabs instead of an all in one signature pad Added E2E tests to check settings are applied correctly for documents and templates
148 lines
3.6 KiB
TypeScript
148 lines
3.6 KiB
TypeScript
import * as React from 'react';
|
|
|
|
import { motion } from 'framer-motion';
|
|
|
|
import { cn } from '../../lib/utils';
|
|
|
|
interface TabsProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
defaultValue?: string;
|
|
value?: string;
|
|
onValueChange?: (value: string) => void;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
interface TabsContextValue {
|
|
value: string;
|
|
onValueChange: (value: string) => void;
|
|
}
|
|
|
|
const TabsContext = React.createContext<TabsContextValue | undefined>(undefined);
|
|
|
|
function useTabs() {
|
|
const context = React.useContext(TabsContext);
|
|
if (!context) {
|
|
throw new Error('useTabs must be used within a Tabs provider');
|
|
}
|
|
return context;
|
|
}
|
|
|
|
export function Tabs({
|
|
defaultValue,
|
|
value,
|
|
onValueChange,
|
|
children,
|
|
className,
|
|
...props
|
|
}: TabsProps) {
|
|
const [tabValue, setTabValue] = React.useState(defaultValue || '');
|
|
|
|
const handleValueChange = React.useCallback(
|
|
(newValue: string) => {
|
|
setTabValue(newValue);
|
|
onValueChange?.(newValue);
|
|
},
|
|
[onValueChange],
|
|
);
|
|
|
|
const contextValue = React.useMemo(
|
|
() => ({
|
|
value: value !== undefined ? value : tabValue,
|
|
onValueChange: handleValueChange,
|
|
}),
|
|
[value, tabValue, handleValueChange],
|
|
);
|
|
|
|
return (
|
|
<TabsContext.Provider value={contextValue}>
|
|
<div className={cn('w-full', className)} {...props}>
|
|
{children}
|
|
</div>
|
|
</TabsContext.Provider>
|
|
);
|
|
}
|
|
|
|
interface TabsListProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export function TabsList({ children, className, ...props }: TabsListProps) {
|
|
return (
|
|
<div
|
|
className={cn('border-border flex flex-wrap border-b', className)}
|
|
role="tabslist"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface TabsTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
value: string;
|
|
icon?: React.ReactNode;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export function TabsTrigger({ value, icon, children, className, ...props }: TabsTriggerProps) {
|
|
const { value: selectedValue, onValueChange } = useTabs();
|
|
const isSelected = selectedValue === value;
|
|
|
|
return (
|
|
<button
|
|
role="tab"
|
|
type="button"
|
|
aria-selected={isSelected}
|
|
data-state={isSelected ? 'active' : 'inactive'}
|
|
onClick={() => onValueChange(value)}
|
|
className={cn(
|
|
'relative flex items-center px-4 py-3 text-sm font-medium transition-all',
|
|
'focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
isSelected ? 'text-foreground' : 'text-muted-foreground hover:text-foreground',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{icon && <span className="flex items-center">{icon}</span>}
|
|
{children}
|
|
{isSelected && (
|
|
<motion.div
|
|
layoutId="activeTabIndicator"
|
|
className="bg-foreground/40 absolute bottom-0 left-0 h-0.5 w-full rounded-full"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{
|
|
type: 'spring',
|
|
stiffness: 500,
|
|
damping: 50,
|
|
}}
|
|
/>
|
|
)}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
interface TabsContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
value: string;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export function TabsContent({ value, children, className, ...props }: TabsContentProps) {
|
|
const { value: selectedValue } = useTabs();
|
|
const isSelected = selectedValue === value;
|
|
|
|
if (!isSelected) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
role="tabpanel"
|
|
data-state={isSelected ? 'active' : 'inactive'}
|
|
className={cn('mt-4', className)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|