@octavian-tocan/react-dropdown 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,454 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * @file types.ts
6
+ * @brief TypeScript interfaces for the reusable dropdown system
7
+ */
8
+
9
+ /**
10
+ * @brief Supported dropdown placement options relative to the trigger
11
+ */
12
+ type DropdownPlacement = 'bottom' | 'top';
13
+ /**
14
+ * @brief Metadata used to render grouped sections inside the dropdown list
15
+ */
16
+ interface DropdownSectionMeta {
17
+ /** Stable identifier for the section used as React key */
18
+ key: string;
19
+ /** Human-readable label for the section header */
20
+ label: string;
21
+ /** Optional helper text describing the section */
22
+ description?: string;
23
+ /** Optional icon rendered next to the section label */
24
+ icon?: ReactNode;
25
+ }
26
+ /**
27
+ * @brief Base props for all dropdown components
28
+ */
29
+ interface BaseDropdownProps {
30
+ /** Optional CSS class name for custom styling */
31
+ className?: string;
32
+ /** Optional test identifier for automated testing */
33
+ 'data-testid'?: string;
34
+ /** Optional children elements */
35
+ children?: ReactNode;
36
+ }
37
+ /**
38
+ * @brief Props for DropdownRoot component
39
+ */
40
+ interface DropdownRootProps<T> extends BaseDropdownProps {
41
+ /** Array of items to display */
42
+ items: T[];
43
+ /** Currently selected item */
44
+ selectedItem?: T | null;
45
+ /** Callback invoked when selection changes */
46
+ onSelect: (item: T) => void;
47
+ /** Function to get unique key for each item */
48
+ getItemKey: (item: T) => string;
49
+ /** Function to get display text for each item */
50
+ getItemDisplay: (item: T) => string;
51
+ /** Optional function to filter items based on search query */
52
+ filterItems?: (items: T[], query: string) => T[];
53
+ /** Whether the dropdown is disabled */
54
+ disabled?: boolean;
55
+ /** Optional placeholder text for trigger */
56
+ placeholder?: string;
57
+ /** Optional placement configuration for the dropdown */
58
+ dropdownPlacement?: DropdownPlacement;
59
+ /** Optional function to retrieve description text shown beneath the label */
60
+ getItemDescription?: (item: T) => string | null | undefined;
61
+ /** Optional function to retrieve an icon rendered alongside the label */
62
+ getItemIcon?: (item: T) => ReactNode;
63
+ /** Optional function to group items under labeled section headers */
64
+ getItemSection?: (item: T) => DropdownSectionMeta | null | undefined;
65
+ /** Optional function to determine if item should show a separator before it */
66
+ getItemSeparator?: (item: T, index: number) => boolean;
67
+ /** Optional function to determine if item is disabled */
68
+ getItemDisabled?: (item: T) => boolean;
69
+ /** Optional function to get custom className for item */
70
+ getItemClassName?: (item: T, isSelected: boolean, isDisabled: boolean) => string;
71
+ /**
72
+ * Whether to close the dropdown automatically when an item is selected.
73
+ * Default: true. Set to false to keep dropdown open after selection (useful for multi-select scenarios).
74
+ */
75
+ closeOnSelect?: boolean;
76
+ /** Optional callback invoked when dropdown open state changes */
77
+ onOpenChange?: (isOpen: boolean) => void;
78
+ /** Ref to the trigger element for portal positioning */
79
+ triggerRef?: React.RefObject<HTMLElement | null>;
80
+ /** Whether to render dropdown in a portal (avoids overflow clipping) */
81
+ usePortal?: boolean;
82
+ }
83
+ /**
84
+ * @brief Props for DropdownTrigger component
85
+ */
86
+ interface DropdownTriggerProps extends BaseDropdownProps {
87
+ /** Display text to show in trigger */
88
+ displayValue: string;
89
+ /** Placeholder text when no value is selected */
90
+ placeholder?: string;
91
+ }
92
+ /**
93
+ * @brief Props for DropdownContent component (pure container)
94
+ */
95
+ interface DropdownContentProps$1 extends BaseDropdownProps {
96
+ }
97
+ /**
98
+ * @brief Props for DropdownSearch component
99
+ */
100
+ interface DropdownSearchProps extends BaseDropdownProps {
101
+ /** Current search query */
102
+ value: string;
103
+ /** Callback when search query changes */
104
+ onChange: (value: string) => void;
105
+ /** Ref for input element */
106
+ inputRef?: React.RefObject<HTMLInputElement>;
107
+ /** Placeholder text for search input */
108
+ placeholder?: string;
109
+ }
110
+ /**
111
+ * @brief Props for DropdownList component
112
+ */
113
+ interface DropdownListProps<T> extends BaseDropdownProps {
114
+ /** Items to display in list */
115
+ items: T[];
116
+ /** Optional callback when item is selected. If not provided, uses context's onSelect */
117
+ onSelect?: (item: T) => void;
118
+ /** Whether there are results to display */
119
+ hasResults: boolean;
120
+ /** Currently selected item */
121
+ selectedItem?: T | null;
122
+ /** Function to get unique key for each item */
123
+ getItemKey: (item: T) => string;
124
+ /** Function to get display text for each item */
125
+ getItemDisplay: (item: T) => string;
126
+ /** Optional custom render function for items */
127
+ renderItem?: (item: T, isSelected: boolean, onSelect: (item: T) => void) => ReactNode;
128
+ /** Optional function for displaying supporting description text */
129
+ getItemDescription?: (item: T) => string | null | undefined;
130
+ /** Optional function for rendering an icon element before the label */
131
+ getItemIcon?: (item: T) => ReactNode;
132
+ /** Optional function for grouping items into section headers */
133
+ getItemSection?: (item: T) => DropdownSectionMeta | null | undefined;
134
+ /** Optional function to determine if item should show a separator before it */
135
+ getItemSeparator?: (item: T, index: number) => boolean;
136
+ /** Optional function to determine if item is disabled */
137
+ getItemDisabled?: (item: T) => boolean;
138
+ /** Optional function to get custom className for item */
139
+ getItemClassName?: (item: T, isSelected: boolean, isDisabled: boolean) => string;
140
+ }
141
+ /**
142
+ * @brief Props for DropdownOption component
143
+ */
144
+ interface DropdownOptionProps<T> extends BaseDropdownProps {
145
+ /** Item to display */
146
+ item: T;
147
+ /** Callback when option is selected */
148
+ onSelect: (item: T) => void;
149
+ /** Whether this option is currently selected */
150
+ isSelected: boolean;
151
+ /** Display text for the option */
152
+ displayText: string;
153
+ /** Data key attribute for the option */
154
+ dataKey: string;
155
+ /** Optional supporting description shown under the primary label */
156
+ description?: string | null;
157
+ /** Optional icon element rendered before the label */
158
+ icon?: ReactNode;
159
+ /** Whether this option is disabled */
160
+ isDisabled?: boolean;
161
+ /** Optional custom className for the option */
162
+ className?: string;
163
+ }
164
+ /**
165
+ * @brief Dropdown context value interface
166
+ */
167
+ interface DropdownContextValue<T> {
168
+ /** Current open/closed state */
169
+ isOpen: boolean;
170
+ /** Function to set open state */
171
+ setIsOpen: (open: boolean) => void;
172
+ /** Currently selected item */
173
+ selectedItem: T | null;
174
+ /** Function to set selected item */
175
+ setSelectedItem: (item: T | null) => void;
176
+ /** Current search query */
177
+ searchQuery: string;
178
+ /** Function to set search query */
179
+ setSearchQuery: (query: string) => void;
180
+ /** Items to display */
181
+ items: T[];
182
+ /** Filtered items based on search query */
183
+ filteredItems: T[];
184
+ /** Function to get unique key for each item */
185
+ getItemKey: (item: T) => string;
186
+ /** Function to get display text for each item */
187
+ getItemDisplay: (item: T) => string;
188
+ /** Function to filter items based on search query */
189
+ filterItems: (items: T[], query: string) => T[];
190
+ /** Callback when item is selected */
191
+ onSelect: (item: T) => void;
192
+ /** Whether the dropdown is disabled */
193
+ disabled: boolean;
194
+ /** Whether to close dropdown on selection */
195
+ closeOnSelect: boolean;
196
+ /** Function to close dropdown */
197
+ closeDropdown: () => void;
198
+ /** Function to toggle dropdown open/closed state */
199
+ toggleDropdown: () => void;
200
+ /** Placement configuration shared with dropdown components */
201
+ dropdownPlacement?: DropdownPlacement;
202
+ /** Optional function to retrieve supporting descriptions */
203
+ getItemDescription?: (item: T) => string | null | undefined;
204
+ /** Optional function to retrieve icon elements */
205
+ getItemIcon?: (item: T) => ReactNode;
206
+ /** Optional function to group items by section metadata */
207
+ getItemSection?: (item: T) => DropdownSectionMeta | null | undefined;
208
+ /** Optional function to determine if item should show a separator before it */
209
+ getItemSeparator?: (item: T, index: number) => boolean;
210
+ /** Optional function to determine if item is disabled */
211
+ getItemDisabled?: (item: T) => boolean;
212
+ /** Optional function to get custom className for item */
213
+ getItemClassName?: (item: T, isSelected: boolean, isDisabled: boolean) => string;
214
+ /** Ref to the trigger element for portal positioning */
215
+ triggerRef?: React.RefObject<HTMLElement | null>;
216
+ /** Whether to render dropdown in a portal */
217
+ usePortal?: boolean;
218
+ }
219
+
220
+ /**
221
+ * @brief Main provider component that manages dropdown state
222
+ *
223
+ * This component provides all the state management and context for dropdown functionality.
224
+ * It handles open/close state, selection, search functionality, and provides
225
+ * all necessary callbacks to child components through context.
226
+ *
227
+ * @param props Dropdown configuration and callbacks
228
+ * @returns JSX element providing context to children
229
+ */
230
+ declare function DropdownRoot<T>({ children, items, selectedItem: initialSelectedItem, onSelect, getItemKey, getItemDisplay, filterItems, disabled, placeholder, className, dropdownPlacement, getItemDescription, getItemIcon, getItemSection, getItemSeparator, getItemDisabled, getItemClassName, closeOnSelect, onOpenChange, triggerRef, usePortal, 'data-testid': testId, }: DropdownRootProps<T>): react_jsx_runtime.JSX.Element;
231
+
232
+ /**
233
+ * @brief Trigger button that opens/closes the dropdown
234
+ *
235
+ * This component renders the button that users click to open the dropdown.
236
+ * It displays the current selection or placeholder text and includes
237
+ * a dropdown arrow indicator.
238
+ *
239
+ * @param props Trigger configuration and callbacks
240
+ * @returns JSX element for the trigger button
241
+ */
242
+ declare function DropdownTrigger({ displayValue, placeholder, className, 'data-testid': testId, }: DropdownTriggerProps): react_jsx_runtime.JSX.Element;
243
+
244
+ interface DropdownContentProps extends BaseDropdownProps {
245
+ /** Disable animations (useful for mobile or performance) */
246
+ disableAnimation?: boolean;
247
+ }
248
+ /**
249
+ * @brief Pure dropdown container for composing dropdown contents
250
+ *
251
+ * This component provides the dropdown container that appears when
252
+ * the dropdown is open. It accepts any children for maximum flexibility.
253
+ * Use this for custom compositions, or use pre-made dropdowns like
254
+ * DropdownSearchable or DropdownSimple.
255
+ *
256
+ * @param props Container configuration
257
+ * @returns JSX element for dropdown container or null if closed
258
+ */
259
+ declare function DropdownContent({ children, className, disableAnimation, 'data-testid': testId, }: DropdownContentProps): react_jsx_runtime.JSX.Element;
260
+
261
+ /**
262
+ * @brief Search input component for filtering dropdown items
263
+ *
264
+ * This component provides a search input that filters the dropdown items
265
+ * as the user types. It prevents tabbing out of the input
266
+ * and includes proper accessibility attributes.
267
+ *
268
+ * @param props Search input configuration
269
+ * @returns JSX element for the search input
270
+ */
271
+ declare const DropdownSearch: ({ value, onChange, inputRef, placeholder, className, "data-testid": testId, }: DropdownSearchProps) => react_jsx_runtime.JSX.Element;
272
+
273
+ /**
274
+ * @brief List component for displaying dropdown options
275
+ * @description Renders a scrollable list of items with support for grouping, separators,
276
+ * icons, descriptions, and custom styling. Handles scrolling the selected item into view
277
+ * when the list opens and provides proper accessibility attributes. Items can be grouped
278
+ * into sections with headers, and separators can be added between items.
279
+ * @template T The type of items in the dropdown
280
+ * @param props List configuration and items
281
+ * @param props.items Array of items to display in the dropdown
282
+ * @param props.onSelect Optional custom callback when an item is selected
283
+ * @param props.hasResults Whether there are results to display
284
+ * @param props.selectedItem Currently selected item
285
+ * @param props.getItemKey Function to get unique key for each item
286
+ * @param props.getItemDisplay Function to get display text for each item
287
+ * @param props.renderItem Optional custom render function for items
288
+ * @param props.getItemDescription Optional function to get description text
289
+ * @param props.getItemIcon Optional function to get icon element
290
+ * @param props.getItemSection Optional function to get section metadata
291
+ * @param props.getItemSeparator Optional function to determine if separator should appear
292
+ * @param props.getItemDisabled Optional function to determine if item is disabled
293
+ * @param props.getItemClassName Optional function to get custom className
294
+ * @param props.className Additional CSS classes for the list container
295
+ * @param props['data-testid'] Test ID for the list element
296
+ * @returns JSX element for the item list
297
+ */
298
+ declare function DropdownList<T>({ items, onSelect: customOnSelect, hasResults, selectedItem, getItemKey, getItemDisplay, renderItem, getItemDescription, getItemIcon, getItemSection, getItemSeparator, getItemDisabled, getItemClassName, className, 'data-testid': testId, }: DropdownListProps<T>): react_jsx_runtime.JSX.Element;
299
+
300
+ /**
301
+ * @file DropdownSearchable.tsx
302
+ * @brief Pre-made dropdown with search and list
303
+ */
304
+ /**
305
+ * @brief Pre-configured dropdown with search input and list
306
+ *
307
+ * This is a convenience component that combines DropdownSearch and DropdownList.
308
+ * Use this for the common case where you want a searchable dropdown.
309
+ * For custom compositions, use DropdownContent with your own children.
310
+ *
311
+ * @param props Dropdown configuration
312
+ * @returns JSX element for searchable dropdown or null if closed
313
+ */
314
+ declare function DropdownSearchable({ searchPlaceholder, className, 'data-testid': testId, hideSearchThreshold, }: {
315
+ searchPlaceholder?: string;
316
+ className?: string;
317
+ 'data-testid'?: string;
318
+ /**
319
+ * Maximum number of items before search is shown.
320
+ * If items.length <= hideSearchThreshold, search input is hidden.
321
+ * Useful for small lists (e.g., 4 navigation items) where search is unnecessary.
322
+ * Default: undefined (always show search)
323
+ */
324
+ hideSearchThreshold?: number;
325
+ }): react_jsx_runtime.JSX.Element;
326
+
327
+ /**
328
+ * @file DropdownSimple.tsx
329
+ * @brief Pre-made dropdown with list only (no search)
330
+ */
331
+ /**
332
+ * @brief Pre-configured dropdown with list only
333
+ *
334
+ * This is a convenience component that provides just a list without search.
335
+ * Use this for simple dropdowns with small item counts where search isn't needed.
336
+ * For custom compositions, use DropdownContent with your own children.
337
+ *
338
+ * @param props Dropdown configuration
339
+ * @returns JSX element for simple dropdown or null if closed
340
+ */
341
+ declare function DropdownSimple({ className, 'data-testid': testId, }: {
342
+ className?: string;
343
+ 'data-testid'?: string;
344
+ }): react_jsx_runtime.JSX.Element;
345
+
346
+ /**
347
+ * @brief Props for DropdownMenu component
348
+ */
349
+ interface DropdownMenuProps<T> extends Omit<DropdownRootProps<T>, 'selectedItem' | 'triggerRef'> {
350
+ /** Custom trigger component (button, icon, etc.) */
351
+ trigger: ReactNode;
352
+ /** Optional className for the dropdown content container */
353
+ contentClassName?: string;
354
+ /** Optional className for the dropdown list */
355
+ listClassName?: string;
356
+ /** Optional callback invoked when dropdown open state changes */
357
+ onOpenChange?: (isOpen: boolean) => void;
358
+ }
359
+ /**
360
+ * @brief DropdownMenu component - optimized for action menus
361
+ *
362
+ * This is a variant component that simplifies creating action menus (like context menus,
363
+ * three-dot menus, etc.) with built-in support for separators, disabled states, and custom styling.
364
+ *
365
+ * @example
366
+ * ```tsx
367
+ * interface MenuItem {
368
+ * id: string;
369
+ * label: string;
370
+ * icon: ReactNode;
371
+ * onClick: () => void;
372
+ * disabled?: boolean;
373
+ * showSeparator?: boolean;
374
+ * }
375
+ *
376
+ * const items: MenuItem[] = [
377
+ * { id: '1', label: 'Edit', icon: <Edit />, onClick: handleEdit },
378
+ * { id: '2', label: 'Delete', icon: <Trash />, onClick: handleDelete, showSeparator: true },
379
+ * ];
380
+ *
381
+ * <DropdownMenu
382
+ * items={items}
383
+ * trigger={<MoreHorizontal />}
384
+ * onSelect={(item) => item.onClick()}
385
+ * getItemKey={(item) => item.id}
386
+ * getItemDisplay={(item) => item.label}
387
+ * getItemIcon={(item) => item.icon}
388
+ * getItemSeparator={(item) => item.showSeparator ?? false}
389
+ * getItemDisabled={(item) => item.disabled ?? false}
390
+ * contentClassName="right-0! top-full! mt-2! bg-white border border-gray-200 rounded-2xl shadow-lg p-2 gap-1.5 z-50 min-w-[200px]"
391
+ * />
392
+ * ```
393
+ */
394
+ declare function DropdownMenu<T>({ items, onSelect, getItemKey, getItemDisplay, getItemIcon, getItemDescription, getItemSeparator, getItemDisabled, getItemClassName, getItemSection, filterItems, disabled, placeholder, className, dropdownPlacement, closeOnSelect, trigger, contentClassName, listClassName, onOpenChange, usePortal, 'data-testid': testId, }: DropdownMenuProps<T>): react_jsx_runtime.JSX.Element;
395
+
396
+ /**
397
+ * @brief Hook to access dropdown context
398
+ * @throws Error if used outside DropdownProvider
399
+ * @returns Dropdown context value
400
+ */
401
+ declare function useDropdownContext<T>(): DropdownContextValue<T>;
402
+ /**
403
+ * @brief Props for DropdownProvider component
404
+ */
405
+ interface DropdownProviderProps<T> {
406
+ children: ReactNode;
407
+ value: DropdownContextValue<T>;
408
+ }
409
+ /**
410
+ * @brief Provider component for dropdown context
411
+ * @param props Provider props with children and context value
412
+ * @returns JSX element providing context to children
413
+ */
414
+ declare function DropdownProvider<T>({ children, value }: DropdownProviderProps<T>): react_jsx_runtime.JSX.Element;
415
+ /**
416
+ * @brief Hook for managing keyboard navigation in dropdown
417
+ * @param items Items to navigate through
418
+ * @param getItemKey Function to get unique key for items
419
+ * @param onSelect Callback when item is selected
420
+ * @param closeDropdown Callback to close dropdown
421
+ * @returns Object with keyboard event handlers
422
+ */
423
+ declare function useKeyboardNavigation<T>(items: T[], getItemKey: (item: T) => string, onSelect: (item: T) => void, closeDropdown: () => void): {
424
+ handleKeyDown: (event: React.KeyboardEvent) => void;
425
+ resetFocus: () => void;
426
+ focusedIndex: number;
427
+ };
428
+ /**
429
+ * @brief Hook for managing click outside detection
430
+ * @param dropdownRef Ref to dropdown element
431
+ * @param closeDropdown Callback to close dropdown
432
+ * @param isOpen Current open state
433
+ */
434
+ declare function useClickOutside(dropdownRef: React.RefObject<HTMLElement | null>, closeDropdown: () => void, isOpen: boolean): void;
435
+
436
+ /**
437
+ * @brief Compound dropdown component
438
+ *
439
+ * This provides a compound component pattern where all sub-components
440
+ * are attached to the main DropdownRoot component for easy composition.
441
+ * Includes both pure composition components and pre-made convenience components.
442
+ */
443
+ declare const Dropdown: {
444
+ Root: typeof DropdownRoot;
445
+ Trigger: typeof DropdownTrigger;
446
+ Content: typeof DropdownContent;
447
+ Search: ({ value, onChange, inputRef, placeholder, className, "data-testid": testId, }: DropdownSearchProps) => react_jsx_runtime.JSX.Element;
448
+ List: typeof DropdownList;
449
+ Simple: typeof DropdownSimple;
450
+ Searchable: typeof DropdownSearchable;
451
+ Menu: typeof DropdownMenu;
452
+ };
453
+
454
+ export { type BaseDropdownProps, DropdownContent, type DropdownContentProps$1 as DropdownContentProps, type DropdownContextValue, DropdownList, type DropdownListProps, DropdownMenu, type DropdownMenuProps, type DropdownOptionProps, type DropdownPlacement, DropdownProvider, DropdownRoot, type DropdownRootProps, DropdownSearch, type DropdownSearchProps, DropdownSearchable, type DropdownSectionMeta, DropdownSimple, DropdownTrigger, type DropdownTriggerProps, Dropdown as default, useClickOutside, useDropdownContext, useKeyboardNavigation };