mirror of
https://github.com/documenso/documenso.git
synced 2025-11-17 18:21:32 +10:00
Add ability to enable or disable allowed signature types: - Drawn - Typed - Uploaded **Tabbed style signature dialog**  **Document settings**  **Team preferences**  ## Changes Made - 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 ## Testing Performed 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>
|
|
);
|
|
}
|