mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 21:41:57 +00:00
66 lines
1.6 KiB
TypeScript
66 lines
1.6 KiB
TypeScript
'use client';
|
|
|
|
import { ReactNode } from 'react';
|
|
|
|
import { cn } from '@/lib/helper';
|
|
|
|
type FieldMessageTone = 'error' | 'info' | 'success';
|
|
|
|
export interface FieldMessageProps {
|
|
message?: ReactNode;
|
|
tone?: FieldMessageTone;
|
|
isVisible?: boolean;
|
|
persistent?: boolean;
|
|
className?: string;
|
|
ariaLive?: 'off' | 'polite' | 'assertive';
|
|
}
|
|
|
|
const toneClassName: Record<FieldMessageTone, string> = {
|
|
error: 'text-error',
|
|
info: 'text-base-content/60',
|
|
success: 'text-success',
|
|
};
|
|
|
|
/**
|
|
* Shared helper to render bottom field feedback without causing layout shift.
|
|
* Keeps a minimal slot height, but expands when the content wraps onto multiple lines.
|
|
*/
|
|
export const FieldMessage = ({
|
|
message,
|
|
tone = 'info',
|
|
isVisible,
|
|
persistent = true,
|
|
className,
|
|
ariaLive,
|
|
}: FieldMessageProps) => {
|
|
const hasMessage = Boolean(message);
|
|
const visible = isVisible ?? hasMessage;
|
|
const liveRegion = ariaLive ?? (tone === 'error' ? 'assertive' : 'polite');
|
|
|
|
return (
|
|
<div
|
|
aria-live={liveRegion}
|
|
aria-hidden={!visible && !hasMessage}
|
|
className={cn(
|
|
'relative w-full text-sm leading-5 transition-[opacity,transform] duration-150 ease-out',
|
|
persistent && 'min-h-[1.25rem]',
|
|
className
|
|
)}
|
|
>
|
|
<span
|
|
className={cn(
|
|
'block whitespace-pre-line',
|
|
toneClassName[tone],
|
|
visible
|
|
? 'opacity-100 translate-y-0'
|
|
: 'opacity-0 -translate-y-1 pointer-events-none'
|
|
)}
|
|
>
|
|
{visible || persistent ? (message ?? '\u00A0') : message}
|
|
</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default FieldMessage;
|