extended-dynamic-forms
Version:
Extended React JSON Schema Form (RJSF) v6 with custom components, widgets, templates, layouts, and form events
497 lines (495 loc) • 16.5 kB
TypeScript
import { WidgetProps } from '@rjsf/utils';
/**
* Presentation modes for the Choice Widget
*/
export declare enum PresentationMode {
/** Dropdown/Select presentation */
DROPDOWN = "dropdown",
/** Radio button presentation */
RADIO = "radio",
/** Checkbox presentation (multi-select) */
CHECKBOX = "checkbox"
}
/**
* Loading states for async data operations
*/
export interface LoadingState {
/** Whether the widget is currently loading */
isLoading: boolean;
/** Loading message to display to users */
message?: string;
/** Progress percentage (0-100) for long operations */
progress?: number;
}
/**
* Error states for data operations
*/
export interface ErrorState {
/** Whether an error has occurred */
hasError: boolean;
/** Error message to display to users */
message: string;
/** Error code for programmatic handling */
code?: string | number;
/** Stack trace for debugging (development only) */
stack?: string;
/** Whether the error is recoverable */
recoverable?: boolean;
/** Timestamp when the error occurred */
timestamp?: number;
}
/**
* Represents a single choice option
*/
export interface ChoiceOption {
/** Display text for the option */
label: string;
/** Value to be stored when selected */
value: string | number | boolean;
/** Whether this option is disabled */
disabled?: boolean;
/** Additional metadata for the option */
metadata?: {
/** Optional description or help text */
description?: string;
/** CSS class names for styling */
className?: string;
/** Icon identifier for display */
icon?: string;
/** Group or category identifier for organizing options */
group?: string;
/** Display order for sorting */
order?: number;
/** Additional data attributes */
data?: Record<string, unknown>;
/** Accessibility attributes */
a11y?: {
/** ARIA label for screen readers */
ariaLabel?: string;
/** ARIA description */
ariaDescription?: string;
/** Whether this option has a popup */
ariaHasPopup?: boolean;
};
/** Internal properties for system use */
_index?: number;
_active?: boolean;
_originalData?: any;
};
}
/**
* Configuration for static data sources
*/
export interface StaticDataSourceConfig {
/** Type identifier for static data source */
type: 'static';
/** Array of choice options */
options: ChoiceOption[];
}
/**
* HTTP method types for API requests
*/
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH';
/**
* Response mapper function to transform API data into ChoiceOption[]
*/
export type ResponseMapper = (data: unknown) => ChoiceOption[];
/**
* Built-in response mapper configurations
*/
export interface ResponseMapperConfig {
/** Type of built-in mapper */
type: 'simple' | 'nested' | 'custom';
/** Configuration for simple mapper */
simple?: {
/** Path to the array in response (e.g., 'data.items') */
arrayPath?: string;
/** Field name for label (default: 'label') */
labelField?: string;
/** Field name for value (default: 'value') */
valueField?: string;
/** Field name for disabled state (default: 'disabled') */
disabledField?: string;
};
/** Configuration for nested mapper */
nested?: {
/** Path to the array in response */
arrayPath: string;
/** Nested paths for extracting data */
paths: {
label: string;
value: string;
disabled?: string;
description?: string;
};
};
/** Custom mapper function */
custom?: ResponseMapper;
}
/**
* Cache configuration for API responses
*/
export interface CacheConfig {
/** Cache duration in milliseconds (default: 300000 = 5 minutes) */
ttl?: number;
/** Cache key for storage (auto-generated if not provided) */
key?: string;
/** Whether to use browser storage for persistence */
persistent?: boolean;
/** Cache storage type */
storageType?: 'memory' | 'sessionStorage' | 'localStorage';
}
/**
* Configuration for API data sources
*/
export interface ApiDataSourceConfig {
/** Type identifier for API data source */
type: 'api';
/** API endpoint URL */
url: string;
/** HTTP method (default: 'GET') */
method?: HttpMethod;
/** Request headers */
headers?: Record<string, string>;
/** Request body for POST/PUT/PATCH */
body?: unknown;
/** Query parameters */
params?: Record<string, string | number | boolean>;
/** Response mapper configuration */
responseMapper: ResponseMapperConfig;
/** Cache configuration */
cache?: CacheConfig;
/** Request timeout in milliseconds (default: 30000) */
timeout?: number;
/** Number of retry attempts on failure (default: 0) */
retries?: number;
/** Authentication configuration */
auth?: {
/** Authentication type */
type: 'bearer' | 'basic' | 'apiKey';
/** Token or credentials */
credentials: string | {
username: string;
password: string;
};
/** Header name for API key auth */
headerName?: string;
};
}
/**
* WebSocket message types for real-time communication
*/
export type WebSocketMessageType = 'options-update' | 'options-add' | 'options-remove' | 'options-patch' | 'search-request' | 'search-response' | 'connection-ready' | 'error' | 'heartbeat';
/**
* WebSocket message structure
*/
export interface WebSocketMessage<T = unknown> {
/** Message type identifier */
type: WebSocketMessageType;
/** Message payload */
data?: T;
/** Unique message ID for correlation */
messageId?: string;
/** Timestamp when message was created */
timestamp?: number;
/** Error details for error messages */
error?: {
code: string;
message: string;
details?: unknown;
};
}
/**
* Configuration for WebSocket data sources
*/
export interface WebSocketDataSourceConfig {
/** Type identifier for WebSocket data source */
type: 'websocket';
/** WebSocket server URL */
url: string;
/** WebSocket protocols */
protocols?: string | string[];
/** Authentication configuration */
auth?: {
/** Authentication type */
type: 'bearer' | 'query' | 'header';
/** Token or credentials */
credentials: string;
/** Parameter/header name for auth */
paramName?: string;
};
/** Connection configuration */
connection?: {
/** Connection timeout in milliseconds (default: 30000) */
connectTimeout?: number;
/** Auto-reconnect on connection loss (default: true) */
autoReconnect?: boolean;
/** Max reconnection attempts (default: 5) */
maxReconnectAttempts?: number;
/** Reconnection delay in milliseconds (default: 1000) */
reconnectDelay?: number;
/** Heartbeat interval in milliseconds (default: 30000) */
heartbeatInterval?: number;
/** Response timeout for requests in milliseconds (default: 10000) */
responseTimeout?: number;
};
/** Message handling configuration */
messageHandling?: {
/** Whether to request initial options on connect (default: true) */
requestInitialOptions?: boolean;
/** Initial message to send after connection */
initialMessage?: WebSocketMessage;
/** Whether to enable real-time search (default: false) */
enableRealtimeSearch?: boolean;
/** Search debounce delay in milliseconds (default: 300) */
searchDebounceMs?: number;
};
/** Response transformation */
responseMapper?: ResponseMapperConfig;
/** Cache configuration (for offline support) */
cache?: CacheConfig;
}
/**
* Union type for all data source configurations
*/
export type DataSourceConfig = StaticDataSourceConfig | ApiDataSourceConfig | WebSocketDataSourceConfig;
/**
* Validation configuration for selected values
*/
export interface ValidationConfig {
/** Custom validation function */
validator?: (value: unknown, options: ChoiceOption[]) => string | null;
/** Whether to validate on change (default: true) */
validateOnChange?: boolean;
/** Whether to validate on blur (default: true) */
validateOnBlur?: boolean;
/** Custom error messages */
messages?: {
required?: string;
invalid?: string;
custom?: string;
};
}
/**
* Search and filtering configuration
*/
export interface SearchConfig {
/** Whether search is enabled */
enabled: boolean;
/** Search placeholder text */
placeholder?: string;
/** Minimum characters before search activates */
minLength?: number;
/** Search debounce delay in milliseconds */
debounceMs?: number;
/** Whether search is case-sensitive */
caseSensitive?: boolean;
/** Fields to search in */
searchFields?: ('label' | 'value' | 'description')[];
/** Custom search function */
customFilter?: (option: ChoiceOption, searchTerm: string) => boolean;
}
/**
* Accessibility configuration
*/
export interface AccessibilityConfig {
/** ARIA label for the widget */
ariaLabel?: string;
/** ARIA description */
ariaDescription?: string;
/** Whether to announce changes to screen readers */
announceChanges?: boolean;
/** Custom ARIA attributes */
ariaAttributes?: Record<string, string>;
/** Keyboard navigation configuration */
keyboard?: {
/** Whether to enable arrow key navigation */
arrowKeys?: boolean;
/** Whether to enable type-ahead search */
typeAhead?: boolean;
/** Whether to enable home/end keys */
homeEnd?: boolean;
};
}
/**
* Main configuration interface for the Choice Widget
*/
export interface ChoiceWidgetConfig {
/** Presentation mode for the widget */
presentationMode?: PresentationMode;
/** Data source configuration */
dataSource?: DataSourceConfig;
/** Whether multiple selections are allowed */
multiple?: boolean;
/** Whether to allow clearing the selection */
allowClear?: boolean;
/** Placeholder text when no option is selected */
placeholder?: string;
/** Whether the widget is disabled */
disabled?: boolean;
/** Whether the widget is read-only */
readonly?: boolean;
/** Maximum number of selections (for multiple mode) */
maxSelections?: number;
/** Minimum number of selections (for multiple mode) */
minSelections?: number;
/** Search and filtering configuration */
search?: SearchConfig;
/** Validation configuration */
validation?: ValidationConfig;
/** Accessibility configuration */
accessibility?: AccessibilityConfig;
/** Custom CSS class names */
className?: string;
/** Custom inline styles */
style?: React.CSSProperties;
/** Size variant for Ant Design components */
size?: 'small' | 'middle' | 'large';
/** Whether to show option counts in multi-select */
showOptionCount?: boolean;
/** Custom loading component */
loadingComponent?: React.ComponentType<LoadingState>;
/** Custom error component */
errorComponent?: React.ComponentType<ErrorState>;
/** Custom empty state component */
emptyComponent?: React.ComponentType<{
message?: string;
}>;
}
/**
* Internal state interface for the Choice Widget
*/
export interface ChoiceWidgetState {
/** Available options */
options: ChoiceOption[];
/** Currently selected values */
selectedValues: (string | number | boolean)[];
/** Loading state */
loading: LoadingState;
/** Error state */
error: ErrorState;
/** Search term */
searchTerm: string;
/** Filtered options based on search */
filteredOptions: ChoiceOption[];
/** Whether the dropdown/menu is open */
isOpen: boolean;
/** Focused option index for keyboard navigation */
focusedOptionIndex: number;
}
/**
* Event handlers for the Choice Widget
*/
export interface ChoiceWidgetEventHandlers {
/** Called when selection changes */
onSelectionChange?: (values: (string | number | boolean)[], options: ChoiceOption[]) => void;
/** Called when search term changes */
onSearchChange?: (searchTerm: string) => void;
/** Called when widget receives focus */
onFocus?: (event: React.FocusEvent) => void;
/** Called when widget loses focus */
onBlur?: (event: React.FocusEvent) => void;
/** Called when dropdown opens */
onOpen?: () => void;
/** Called when dropdown closes */
onClose?: () => void;
/** Called when data loading starts */
onLoadingStart?: () => void;
/** Called when data loading completes */
onLoadingComplete?: (options: ChoiceOption[]) => void;
/** Called when an error occurs */
onError?: (error: ErrorState) => void;
/** Called when error is cleared */
onErrorClear?: () => void;
}
/**
* Props interface for the Choice Widget component
* Extends RJSF WidgetProps and adds Choice Widget specific properties
*/
export interface ChoiceWidgetProps extends WidgetProps {
/** Choice Widget configuration from ui:options */
options: WidgetProps['options'] & {
/** Choice Widget specific configuration */
choiceConfig?: ChoiceWidgetConfig;
/** Legacy support for enum options */
enumOptions?: {
label: string;
value: unknown;
}[];
};
/** Event handlers */
eventHandlers?: ChoiceWidgetEventHandlers;
/** Current widget state (for controlled usage) */
state?: Partial<ChoiceWidgetState>;
/** State change handler (for controlled usage) */
onStateChange?: (state: Partial<ChoiceWidgetState>) => void;
}
/**
* Data provider interface for abstracting data sources
*/
export interface ChoiceDataProvider {
/** Fetch options from the data source */
fetchOptions(): Promise<ChoiceOption[]>;
/** Whether this provider supports caching */
supportsCaching: boolean;
/** Whether this provider supports real-time updates */
supportsRealTime: boolean;
/** Cleanup resources */
cleanup(): void;
}
/**
* Cache interface for storing fetched data
*/
export interface ChoiceCache {
/** Get cached data */
get(key: string): ChoiceOption[] | null;
/** Set cached data */
set(key: string, data: ChoiceOption[], ttl?: number): void;
/** Clear specific cache entry */
clear(key: string): void;
/** Clear all cache entries */
clearAll(): void;
/** Check if cache entry exists and is valid */
has(key: string): boolean;
}
/**
* Strategy interface for different presentation modes
*/
export interface PresentationStrategy {
/** Render the widget */
render(props: ChoiceWidgetProps, state: ChoiceWidgetState): React.ReactElement;
/** Handle selection change */
handleSelectionChange(value: unknown, props: ChoiceWidgetProps, state: ChoiceWidgetState): (string | number | boolean)[];
/** Get default props for this presentation mode */
getDefaultProps(): Partial<ChoiceWidgetConfig>;
/** Validate props for this presentation mode */
validateProps(config: ChoiceWidgetConfig): string[] | null;
}
/**
* Type guard to check if a data source is static
*/
export declare function isStaticDataSource(dataSource: DataSourceConfig): dataSource is StaticDataSourceConfig;
/**
* Type guard to check if a data source is API-based
*/
export declare function isApiDataSource(dataSource: DataSourceConfig): dataSource is ApiDataSourceConfig;
/**
* Type guard to check if a data source is WebSocket-based
*/
export declare function isWebSocketDataSource(dataSource: DataSourceConfig): dataSource is WebSocketDataSourceConfig;
/**
* Type guard to check if widget supports multiple selections
*/
export declare function supportsMultipleSelections(mode: PresentationMode): boolean;
/**
* Default configuration values
*/
export declare const DEFAULT_CHOICE_CONFIG: Partial<ChoiceWidgetConfig>;
/**
* Default cache configuration
*/
export declare const DEFAULT_CACHE_CONFIG: CacheConfig;
/**
* Default response mapper for simple API responses
*/
export declare const DEFAULT_RESPONSE_MAPPER: ResponseMapperConfig;