@pushwoosh/dumb-components 1.1.148 → 1.1.149
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.
- package/AccessForbidden/AccessForbidden.d.ts +3 -0
- package/AccessForbidden/AccessForbidden.js +3 -0
- package/Alert/Alert.d.ts +7 -0
- package/Alert/Alert.js +7 -0
- package/Alert/types.d.ts +6 -0
- package/ApplicationPreloader/ApplicationPreloader.d.ts +8 -0
- package/ApplicationPreloader/ApplicationPreloader.js +6 -0
- package/Badge/Badge.d.ts +6 -0
- package/Badge/Badge.js +6 -0
- package/Badge/types.d.ts +6 -0
- package/Banner/Banner.d.ts +7 -0
- package/Banner/Banner.js +7 -0
- package/Banner/types.d.ts +16 -0
- package/Button/Button.d.ts +19 -0
- package/Button/GhostButton.d.ts +1 -0
- package/Button/GhostButton.js +1 -0
- package/Button/RemoveButton.d.ts +6 -0
- package/Button/RemoveButton.js +6 -0
- package/Button/types.d.ts +21 -0
- package/Calendar/CalendarDropdown/CalendarDropdown.d.ts +12 -0
- package/Calendar/CalendarDropdown/CalendarDropdown.js +12 -0
- package/Calendar/CalendarDropdown/helpers.d.ts +4 -0
- package/Calendar/CalendarDropdown/helpers.js +4 -0
- package/Calendar/CalendarDropdown/types.d.ts +33 -0
- package/Calendar/CalendarSuperForm/CalendarSuperForm.d.ts +7 -0
- package/Calendar/CalendarSuperForm/CalendarSuperForm.js +7 -0
- package/Calendar/CalendarSuperForm/types.d.ts +36 -10
- package/Calendar/helpers.d.ts +10 -0
- package/Calendar/helpers.js +10 -0
- package/Calendar/types.d.ts +16 -1
- package/CardMetric/CardMetric.d.ts +7 -0
- package/CardMetric/CardMetric.js +7 -0
- package/CardMetric/types.d.ts +13 -0
- package/Checkbox/Checkbox.d.ts +6 -0
- package/Checkbox/Checkbox.js +6 -0
- package/Checkbox/types.d.ts +13 -0
- package/Chip/Chip.d.ts +7 -0
- package/Chip/Chip.js +7 -0
- package/Chip/Chip.types.d.ts +9 -0
- package/CodeEditor/CodeEditor.d.ts +7 -0
- package/CodeEditor/CodeEditor.js +7 -0
- package/CodeEditor/languages/html.d.ts +1 -0
- package/CodeEditor/languages/html.js +1 -0
- package/CodeEditor/languages/json.d.ts +1 -0
- package/CodeEditor/languages/json.js +1 -0
- package/CodeEditor/languages/liquid.d.ts +1 -0
- package/CodeEditor/languages/liquid.js +1 -0
- package/CodeEditor/types.d.ts +11 -0
- package/CrashScreen/CrashScreen.d.ts +4 -0
- package/CrashScreen/CrashScreen.js +4 -0
- package/DateTimePicker/components/DatePicker/DatePicker.d.ts +5 -0
- package/DateTimePicker/components/DatePicker/DatePicker.js +5 -0
- package/DateTimePicker/components/DatePicker/types.d.ts +9 -0
- package/DateTimePicker/components/DatePickerField/DatePickerField.d.ts +5 -0
- package/DateTimePicker/components/DatePickerField/DatePickerField.js +5 -0
- package/DateTimePicker/components/DatePickerField/types.d.ts +9 -0
- package/DateTimePicker/components/DateTimePicker/DateTimePicker.d.ts +5 -0
- package/DateTimePicker/components/DateTimePicker/DateTimePicker.js +5 -0
- package/DateTimePicker/components/DateTimePicker/types.d.ts +6 -0
- package/DateTimePicker/components/DateTimePickerField/DateTimePickerField.d.ts +5 -0
- package/DateTimePicker/components/DateTimePickerField/DateTimePickerField.js +5 -0
- package/DateTimePicker/components/DateTimePickerField/types.d.ts +9 -0
- package/DateTimePicker/types.d.ts +5 -0
- package/Drawer/components/Drawer/Drawer.d.ts +11 -0
- package/Drawer/components/Drawer/Drawer.js +11 -0
- package/Drawer/components/Drawer/types.d.ts +9 -0
- package/Drawer/components/DrawerContent/styles.d.ts +3 -0
- package/Drawer/components/DrawerContent/styles.js +3 -0
- package/Drawer/components/DrawerDocs/DrawerDocs.d.ts +7 -0
- package/Drawer/components/DrawerDocs/DrawerDocs.js +7 -0
- package/Drawer/components/DrawerDocs/hooks.d.ts +4 -0
- package/Drawer/components/DrawerDocs/hooks.js +4 -0
- package/Drawer/components/DrawerDocs/types.d.ts +4 -0
- package/DropdownMenu/components/DropdownMenu/DropdownMenu.d.ts +12 -0
- package/DropdownMenu/components/DropdownMenu/DropdownMenu.js +12 -0
- package/DropdownMenu/components/DropdownMenu/types.d.ts +15 -0
- package/DropdownMenu/components/DropdownMenuButton/DropdownMenuButton.d.ts +8 -0
- package/DropdownMenu/components/DropdownMenuButton/DropdownMenuButton.js +8 -0
- package/DropdownMenu/components/DropdownMenuButton/types.d.ts +16 -0
- package/DropdownMenu/components/DropdownMenuItem/DropdownMenuItem.d.ts +7 -0
- package/DropdownMenu/components/DropdownMenuItem/DropdownMenuItem.js +7 -0
- package/DropdownMenu/components/DropdownMenuItem/types.d.ts +6 -0
- package/DropdownMenu/components/DropdownMenuSection/DropdownMenuSection.d.ts +8 -0
- package/DropdownMenu/components/DropdownMenuSection/DropdownMenuSection.js +8 -0
- package/DropdownMenu/components/DropdownMenuSection/types.d.ts +3 -0
- package/DropdownMenu/components/DropdownMenuSubmenu/DropdownMenuSubmenu.d.ts +9 -0
- package/DropdownMenu/components/DropdownMenuSubmenu/DropdownMenuSubmenu.js +9 -0
- package/DropdownMenu/components/DropdownMenuSubmenu/types.d.ts +5 -0
- package/EditableField/EditableField.d.ts +9 -0
- package/EditableField/EditableField.js +9 -0
- package/EditableField/types.d.ts +12 -0
- package/EmailTemplatePreview/EmailTemplatePreview.d.ts +7 -0
- package/EmailTemplatePreview/EmailTemplatePreview.js +7 -0
- package/EmailTemplatePreview/components/NoEmailTemplatePreview/NoEmailTemplatePreview.d.ts +4 -0
- package/EmailTemplatePreview/components/NoEmailTemplatePreview/NoEmailTemplatePreview.js +4 -0
- package/EmailTemplatePreview/helpers.d.ts +8 -0
- package/EmailTemplatePreview/helpers.js +8 -0
- package/EmailTemplatePreview/types.d.ts +7 -0
- package/EnhancedInput/EnhancedInput.d.ts +7 -0
- package/EnhancedInput/EnhancedInput.js +7 -0
- package/EnhancedInput/SearchInput.d.ts +6 -0
- package/EnhancedInput/SearchInput.js +6 -0
- package/EnhancedInput/types.d.ts +5 -0
- package/ExportResult/ExportResult.d.ts +7 -0
- package/ExportResult/ExportResult.js +7 -0
- package/ExportResult/types.d.ts +7 -0
- package/ExternalImagePreview/ExternalImagePreview.d.ts +7 -0
- package/ExternalImagePreview/ExternalImagePreview.js +7 -0
- package/ExternalImagePreview/types.d.ts +7 -0
- package/FieldBox/FieldBox.d.ts +9 -0
- package/FieldBox/FieldBox.js +9 -0
- package/FieldBox/types.d.ts +10 -0
- package/FrequencyCapping/FrequencyCappingEdit.d.ts +7 -0
- package/FrequencyCapping/FrequencyCappingEdit.js +7 -0
- package/FrequencyCapping/FrequencyCappingInApps.d.ts +7 -0
- package/FrequencyCapping/FrequencyCappingInApps.js +7 -0
- package/FrequencyCapping/FrequencyCappingReadonly.d.ts +3 -0
- package/FrequencyCapping/FrequencyCappingReadonly.js +3 -0
- package/FrequencyCapping/types.d.ts +37 -0
- package/FrequencyCapping/types.js +1 -0
- package/GetStartedPage/GetStartedPage.d.ts +7 -0
- package/GetStartedPage/GetStartedPage.js +7 -0
- package/GetStartedPage/types.d.ts +7 -0
- package/InputFile/InputFile.d.ts +7 -0
- package/InputFile/InputFile.js +7 -0
- package/InputFile/types.d.ts +18 -0
- package/Link/Link.d.ts +18 -0
- package/Link/types.d.ts +17 -0
- package/Modal/components/Modal/Modal.d.ts +11 -0
- package/Modal/components/Modal/Modal.js +11 -0
- package/Modal/components/Modal/types.d.ts +6 -0
- package/Modal/components/ModalConfirm/ModalConfirm.d.ts +8 -0
- package/Modal/components/ModalConfirm/ModalConfirm.js +8 -0
- package/Modal/components/ModalConfirm/ModalConfirmProvider.d.ts +7 -0
- package/Modal/components/ModalConfirm/ModalConfirmProvider.js +7 -0
- package/Modal/components/ModalConfirm/hooks.d.ts +8 -0
- package/Modal/components/ModalConfirm/hooks.js +8 -0
- package/Modal/components/ModalConfirm/types.d.ts +16 -0
- package/Modal/components/ModalContent/styles.d.ts +5 -0
- package/Modal/components/ModalContent/styles.js +5 -0
- package/Modal/components/ModalTitle/ModalTitle.d.ts +6 -0
- package/Modal/components/ModalTitle/ModalTitle.js +6 -0
- package/Modal/components/ModalTitle/types.d.ts +3 -0
- package/NotificationInbox/NotificationBar.d.ts +7 -0
- package/NotificationInbox/NotificationBar.js +7 -0
- package/NotificationInbox/NotificationBell.d.ts +7 -0
- package/NotificationInbox/NotificationBell.js +7 -0
- package/NotificationInbox/NotificationInbox.d.ts +7 -0
- package/NotificationInbox/NotificationInbox.js +7 -0
- package/NotificationInbox/types.d.ts +31 -0
- package/NumberPicker/NumberPicker.d.ts +7 -0
- package/NumberPicker/NumberPicker.js +7 -0
- package/NumberPicker/types.d.ts +12 -0
- package/PageHeader/components/AutosizeInput/AutosizeInput.d.ts +7 -0
- package/PageHeader/components/AutosizeInput/AutosizeInput.js +7 -0
- package/PageHeader/components/AutosizeInput/types.d.ts +7 -0
- package/PageHeader/components/PageHeader/PageHeader.d.ts +7 -0
- package/PageHeader/components/PageHeader/PageHeader.js +7 -0
- package/PageHeader/components/PageHeader/types.d.ts +13 -0
- package/PageHeader/components/PageHeaderEditable/PageHeaderEditable.d.ts +7 -0
- package/PageHeader/components/PageHeaderEditable/PageHeaderEditable.js +7 -0
- package/PageHeader/components/PageHeaderEditable/types.d.ts +19 -0
- package/Pagination/Pagination.d.ts +7 -0
- package/Pagination/Pagination.js +7 -0
- package/Pagination/types.d.ts +7 -0
- package/Popover/Popover.d.ts +10 -0
- package/Popover/Popover.js +10 -0
- package/Popover/styles.d.ts +1 -0
- package/Popover/styles.js +1 -0
- package/Popover/types.d.ts +9 -0
- package/ProgressBar/ProgressBar.d.ts +6 -0
- package/ProgressBar/ProgressBar.js +6 -0
- package/ProgressBar/types.d.ts +7 -0
- package/PushPreview/PushPreview.d.ts +7 -0
- package/PushPreview/PushPreview.js +7 -0
- package/PushPreview/types.d.ts +17 -0
- package/Radio/Radio.d.ts +7 -0
- package/Radio/Radio.js +7 -0
- package/Radio/types.d.ts +14 -0
- package/RadioCard/RadioCard.d.ts +7 -0
- package/RadioCard/RadioCard.js +7 -0
- package/RadioCard/types.d.ts +7 -0
- package/ReCaptcha/ReCaptcha.d.ts +8 -0
- package/ReCaptcha/ReCaptcha.js +8 -0
- package/ReCaptcha/helpers.d.ts +4 -0
- package/ReCaptcha/helpers.js +4 -0
- package/ReCaptcha/hooks.d.ts +4 -0
- package/ReCaptcha/hooks.js +4 -0
- package/ReCaptcha/types.d.ts +5 -0
- package/RichMessageEditor/RichMessageEditor.d.ts +8 -0
- package/RichMessageEditor/RichMessageEditor.js +8 -0
- package/RichMessageEditor/RichMessageEditor.types.d.ts +10 -0
- package/RichMessageEditor/RichMessageViewer.d.ts +10 -0
- package/RichMessageEditor/RichMessageViewer.js +7 -0
- package/RichMessageEditor/slate/types.d.ts +5 -0
- package/RichMessageEditor/types.d.ts +1 -0
- package/RichmediaPreview/RichmediaPreview.d.ts +7 -0
- package/RichmediaPreview/RichmediaPreview.js +7 -0
- package/RichmediaPreview/hooks.d.ts +4 -0
- package/RichmediaPreview/hooks.js +4 -0
- package/RichmediaPreview/types.d.ts +13 -0
- package/Select/Select.d.ts +22 -0
- package/Select/Select.js +22 -0
- package/Select/constants.d.ts +1 -0
- package/Select/constants.js +1 -0
- package/Select/types.d.ts +19 -0
- package/SendRate/SendRateEdit.d.ts +7 -0
- package/SendRate/SendRateEdit.js +7 -0
- package/SendRate/SendRateReadonly.d.ts +3 -0
- package/SendRate/SendRateReadonly.js +3 -0
- package/SendRate/types.d.ts +20 -0
- package/Skeleton/TextSkeleton.d.ts +6 -0
- package/Skeleton/TextSkeleton.js +6 -0
- package/Skeleton/styles.d.ts +2 -0
- package/Skeleton/styles.js +2 -0
- package/Skeleton/types.d.ts +11 -0
- package/SortButton/SortButton.d.ts +6 -0
- package/SortButton/SortButton.js +6 -0
- package/SortButton/types.d.ts +11 -0
- package/Spinner/Spinner.d.ts +6 -0
- package/Spinner/Spinner.js +6 -0
- package/Spinner/types.d.ts +4 -0
- package/StatisticCard/StatisticCard.d.ts +7 -0
- package/StatisticCard/StatisticCard.js +7 -0
- package/StatisticCard/types.d.ts +21 -0
- package/Switch/Switch.d.ts +6 -0
- package/Switch/Switch.js +6 -0
- package/Switch/types.d.ts +8 -0
- package/Tabs/components/Tab/Tab.d.ts +4 -0
- package/Tabs/components/Tab/Tab.js +4 -0
- package/Tabs/components/Tab/types.d.ts +6 -0
- package/Tabs/components/TabBar/TabBar.d.ts +3 -0
- package/Tabs/components/TabBar/TabBar.js +3 -0
- package/Tabs/components/TabBar/types.d.ts +2 -0
- package/Tabs/components/TabPanel/TabPanel.d.ts +4 -0
- package/Tabs/components/TabPanel/TabPanel.js +4 -0
- package/Tabs/components/TabPanel/types.d.ts +3 -0
- package/Tabs/components/Tabs/Tabs.d.ts +11 -0
- package/Tabs/components/Tabs/Tabs.js +11 -0
- package/Tabs/components/Tabs/types.d.ts +4 -0
- package/Tabs/types.d.ts +4 -0
- package/Toast/Toast.d.ts +5 -0
- package/Toast/Toast.js +5 -0
- package/Toast/ToastProvider.d.ts +7 -0
- package/Toast/ToastProvider.js +7 -0
- package/Toast/hooks.d.ts +8 -0
- package/Toast/hooks.js +8 -0
- package/Toast/types.d.ts +24 -0
- package/Tooltip/Tooltip.d.ts +7 -0
- package/Tooltip/Tooltip.js +7 -0
- package/Tooltip/TooltipTrigger.d.ts +13 -0
- package/Tooltip/TooltipTrigger.js +7 -0
- package/Tooltip/types.d.ts +12 -0
- package/hooks/useElementWidth.d.ts +4 -3
- package/hooks/useElementWidth.js +4 -3
- package/hooks/useLayer.d.ts +7 -0
- package/hooks/useLayer.js +7 -0
- package/hooks/useOnClickOutside.d.ts +7 -0
- package/hooks/useOnClickOutside.js +7 -0
- package/native/Input.d.ts +3 -0
- package/native/Input.js +1 -0
- package/native/NativeTimeInput.d.ts +1 -0
- package/native/NativeTimeInput.js +1 -0
- package/native/Select.d.ts +1 -0
- package/native/Select.js +1 -0
- package/native/Textarea.d.ts +1 -0
- package/native/Textarea.js +1 -0
- package/package.json +2 -3
- package/polyglot/react.d.ts +19 -0
- package/polyglot/react.js +19 -0
- package/polyglot/types.d.ts +4 -0
- package/shared/dropdownField/FieldInput.d.ts +1 -0
- package/shared/dropdownField/FieldInput.js +1 -0
- package/shared/dropdownField/FieldShell.d.ts +4 -0
- package/shared/dropdownField/FieldShell.js +4 -0
- package/shared/dropdownField/SuggestionsDropdown.d.ts +4 -0
- package/shared/dropdownField/SuggestionsDropdown.js +4 -0
- package/shared/dropdownField/constants.d.ts +1 -0
- package/shared/dropdownField/constants.js +1 -0
- package/shared/dropdownField/shared.d.ts +7 -0
- package/shared/dropdownField/shared.js +7 -0
- package/shared/dropdownField/types.d.ts +7 -0
- package/shared/dropdownField/useActiveIndex.d.ts +4 -0
- package/shared/dropdownField/useActiveIndex.js +4 -0
- package/shared/dropdownField/useAsyncItems.d.ts +4 -0
- package/shared/dropdownField/useAsyncItems.js +4 -0
- package/shared/dropdownField/useDebouncedValue.d.ts +1 -0
- package/shared/dropdownField/useDebouncedValue.js +1 -0
- package/shared/dropdownField/useDropdownItems.d.ts +4 -0
- package/shared/dropdownField/useDropdownItems.js +4 -0
- package/shared/dropdownField/useDropdownPosition.d.ts +4 -0
- package/shared/dropdownField/useDropdownPosition.js +4 -0
- package/shared/dropdownField/useFieldOpenEffects.d.ts +4 -0
- package/shared/dropdownField/useFieldOpenEffects.js +4 -0
- package/shared/dropdownField/useListNavigation.d.ts +5 -0
- package/shared/dropdownField/useListNavigation.js +5 -0
- package/Alert/ai-documentation.md +0 -132
- package/Badge/ai-documentation.md +0 -155
- package/Banner/ai-documentation.md +0 -285
- package/Button/ai-documentation.md +0 -247
- package/CardMetric/ai-documentation.md +0 -277
- package/Checkbox/ai-documentation.md +0 -231
- package/Chip/ai-documentation.md +0 -281
- package/CodeEditor/ai-documentation.md +0 -350
- package/DateTimePicker/ai-documentation.md +0 -400
- package/Drawer/ai-documentation.md +0 -417
- package/EnhancedInput/ai-documentation.md +0 -352
- package/InputFile/ai-documentation.md +0 -420
- package/Modal/ai-documentation.md +0 -285
- package/PageHeader/ai-documentation.md +0 -511
- package/Pagination/ai-documentation.md +0 -509
- package/Popover/ai-documentation.md +0 -625
- package/ProgressBar/ai-documentation.md +0 -533
- package/Radio/ai-documentation.md +0 -243
- package/Select/ai-documentation.md +0 -374
- package/Skeleton/ai-documentation.md +0 -574
- package/Spinner/ai-documentation.md +0 -607
- package/Switch/ai-documentation.md +0 -312
- package/Tabs/ai-documentation.md +0 -475
- package/Toast/ai-documentation.md +0 -220
- package/Tooltip/ai-documentation.md +0 -340
- package/ai-documentation.md +0 -124
- package/native/ai-documentation.md +0 -485
|
@@ -1,625 +0,0 @@
|
|
|
1
|
-
# Popover Component - @pushwoosh/dumb-components
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
- **Component Name**: Popover
|
|
6
|
-
- **Import Path**: `@pushwoosh/dumb-components`
|
|
7
|
-
- **Files**:
|
|
8
|
-
- `/src/Popover/Popover.tsx` - Main component implementation
|
|
9
|
-
- `/src/Popover/types.ts` - TypeScript type definitions
|
|
10
|
-
- `/src/Popover/styles.ts` - Styled components and container
|
|
11
|
-
- `/src/Popover/index.ts` - Component exports
|
|
12
|
-
- **Type**: Overlay/Display
|
|
13
|
-
- **Description**: A floating popover component that displays content positioned relative to an anchor element. Built on tippy.js for robust positioning and portal rendering, with support for custom positioning, loading states, and responsive behavior.
|
|
14
|
-
|
|
15
|
-
## TypeScript Interfaces
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
import type { Placement } from 'tippy.js';
|
|
19
|
-
|
|
20
|
-
export interface PopoverProps {
|
|
21
|
-
readonly anchorEl: RefObject<HTMLElement>;
|
|
22
|
-
readonly internalWindowRef?: RefObject<HTMLDivElement> | null;
|
|
23
|
-
readonly position?: Placement;
|
|
24
|
-
readonly open: boolean;
|
|
25
|
-
readonly loading?: boolean;
|
|
26
|
-
readonly zIndex?: number;
|
|
27
|
-
readonly windowWidth?: number;
|
|
28
|
-
readonly children: ReactNode | undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Re-exported from tippy.js
|
|
32
|
-
export type Placement =
|
|
33
|
-
| 'top' | 'top-start' | 'top-end'
|
|
34
|
-
| 'bottom' | 'bottom-start' | 'bottom-end'
|
|
35
|
-
| 'left' | 'left-start' | 'left-end'
|
|
36
|
-
| 'right' | 'right-start' | 'right-end';
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Import & Usage
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
import { Popover, type Placement } from '@pushwoosh/dumb-components';
|
|
43
|
-
|
|
44
|
-
// Basic usage
|
|
45
|
-
function MyComponent() {
|
|
46
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
47
|
-
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div>
|
|
51
|
-
<button
|
|
52
|
-
ref={anchorRef}
|
|
53
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
54
|
-
>
|
|
55
|
-
Open Popover
|
|
56
|
-
</button>
|
|
57
|
-
|
|
58
|
-
<Popover
|
|
59
|
-
open={isOpen}
|
|
60
|
-
anchorEl={anchorRef}
|
|
61
|
-
position="bottom-start"
|
|
62
|
-
>
|
|
63
|
-
<div style={{ padding: '16px' }}>
|
|
64
|
-
<h3>Popover Content</h3>
|
|
65
|
-
<p>This is the content inside the popover.</p>
|
|
66
|
-
<button onClick={() => setIsOpen(false)}>
|
|
67
|
-
Close
|
|
68
|
-
</button>
|
|
69
|
-
</div>
|
|
70
|
-
</Popover>
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Click outside to close
|
|
76
|
-
function ClickOutsidePopover() {
|
|
77
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
78
|
-
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
79
|
-
const popoverRef = useRef<HTMLDivElement>(null);
|
|
80
|
-
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
83
|
-
if (
|
|
84
|
-
isOpen &&
|
|
85
|
-
anchorRef.current &&
|
|
86
|
-
popoverRef.current &&
|
|
87
|
-
!anchorRef.current.contains(event.target as Node) &&
|
|
88
|
-
!popoverRef.current.contains(event.target as Node)
|
|
89
|
-
) {
|
|
90
|
-
setIsOpen(false);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
95
|
-
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
96
|
-
}, [isOpen]);
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<div>
|
|
100
|
-
<button
|
|
101
|
-
ref={anchorRef}
|
|
102
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
103
|
-
>
|
|
104
|
-
Click Outside to Close
|
|
105
|
-
</button>
|
|
106
|
-
|
|
107
|
-
<Popover
|
|
108
|
-
open={isOpen}
|
|
109
|
-
anchorEl={anchorRef}
|
|
110
|
-
internalWindowRef={popoverRef}
|
|
111
|
-
position="bottom"
|
|
112
|
-
>
|
|
113
|
-
<div style={{ padding: '20px', minWidth: '200px' }}>
|
|
114
|
-
<h4>Interactive Popover</h4>
|
|
115
|
-
<input placeholder="Type something..." />
|
|
116
|
-
<br />
|
|
117
|
-
<button style={{ marginTop: '10px' }}>Action</button>
|
|
118
|
-
</div>
|
|
119
|
-
</Popover>
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Advanced Examples
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
// Menu popover
|
|
129
|
-
function MenuPopover() {
|
|
130
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
131
|
-
const [selectedOption, setSelectedOption] = useState('');
|
|
132
|
-
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
133
|
-
|
|
134
|
-
const menuItems = [
|
|
135
|
-
{ id: 'edit', label: 'Edit', icon: '✏️' },
|
|
136
|
-
{ id: 'copy', label: 'Copy', icon: '📋' },
|
|
137
|
-
{ id: 'move', label: 'Move', icon: '📁' },
|
|
138
|
-
{ id: 'delete', label: 'Delete', icon: '🗑️', danger: true },
|
|
139
|
-
];
|
|
140
|
-
|
|
141
|
-
const handleMenuItemClick = (itemId: string) => {
|
|
142
|
-
setSelectedOption(itemId);
|
|
143
|
-
setIsOpen(false);
|
|
144
|
-
|
|
145
|
-
// Handle the action
|
|
146
|
-
switch (itemId) {
|
|
147
|
-
case 'edit':
|
|
148
|
-
console.log('Edit action');
|
|
149
|
-
break;
|
|
150
|
-
case 'copy':
|
|
151
|
-
console.log('Copy action');
|
|
152
|
-
break;
|
|
153
|
-
// ... other actions
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
return (
|
|
158
|
-
<div>
|
|
159
|
-
<button
|
|
160
|
-
ref={anchorRef}
|
|
161
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
162
|
-
style={{
|
|
163
|
-
padding: '8px 16px',
|
|
164
|
-
display: 'flex',
|
|
165
|
-
alignItems: 'center',
|
|
166
|
-
gap: '8px'
|
|
167
|
-
}}
|
|
168
|
-
>
|
|
169
|
-
Actions ▼
|
|
170
|
-
</button>
|
|
171
|
-
|
|
172
|
-
<Popover
|
|
173
|
-
open={isOpen}
|
|
174
|
-
anchorEl={anchorRef}
|
|
175
|
-
position="bottom-start"
|
|
176
|
-
windowWidth={180}
|
|
177
|
-
>
|
|
178
|
-
<div style={{ padding: '8px 0' }}>
|
|
179
|
-
{menuItems.map(item => (
|
|
180
|
-
<button
|
|
181
|
-
key={item.id}
|
|
182
|
-
onClick={() => handleMenuItemClick(item.id)}
|
|
183
|
-
style={{
|
|
184
|
-
width: '100%',
|
|
185
|
-
padding: '12px 16px',
|
|
186
|
-
border: 'none',
|
|
187
|
-
background: 'transparent',
|
|
188
|
-
textAlign: 'left',
|
|
189
|
-
display: 'flex',
|
|
190
|
-
alignItems: 'center',
|
|
191
|
-
gap: '8px',
|
|
192
|
-
cursor: 'pointer',
|
|
193
|
-
color: item.danger ? '#dc3545' : 'inherit',
|
|
194
|
-
}}
|
|
195
|
-
onMouseEnter={(e) => {
|
|
196
|
-
e.currentTarget.style.backgroundColor = '#f8f9fa';
|
|
197
|
-
}}
|
|
198
|
-
onMouseLeave={(e) => {
|
|
199
|
-
e.currentTarget.style.backgroundColor = 'transparent';
|
|
200
|
-
}}
|
|
201
|
-
>
|
|
202
|
-
<span>{item.icon}</span>
|
|
203
|
-
{item.label}
|
|
204
|
-
</button>
|
|
205
|
-
))}
|
|
206
|
-
</div>
|
|
207
|
-
</Popover>
|
|
208
|
-
</div>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Form popover
|
|
213
|
-
function FormPopover() {
|
|
214
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
215
|
-
const [formData, setFormData] = useState({ name: '', email: '' });
|
|
216
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
217
|
-
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
218
|
-
|
|
219
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
220
|
-
e.preventDefault();
|
|
221
|
-
setIsLoading(true);
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
// Simulate API call
|
|
225
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
226
|
-
console.log('Form submitted:', formData);
|
|
227
|
-
setIsOpen(false);
|
|
228
|
-
setFormData({ name: '', email: '' });
|
|
229
|
-
} catch (error) {
|
|
230
|
-
console.error('Failed to submit:', error);
|
|
231
|
-
} finally {
|
|
232
|
-
setIsLoading(false);
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
return (
|
|
237
|
-
<div>
|
|
238
|
-
<button
|
|
239
|
-
ref={anchorRef}
|
|
240
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
241
|
-
>
|
|
242
|
-
Add User
|
|
243
|
-
</button>
|
|
244
|
-
|
|
245
|
-
<Popover
|
|
246
|
-
open={isOpen}
|
|
247
|
-
anchorEl={anchorRef}
|
|
248
|
-
position="bottom-start"
|
|
249
|
-
loading={isLoading}
|
|
250
|
-
windowWidth={300}
|
|
251
|
-
>
|
|
252
|
-
<form onSubmit={handleSubmit} style={{ padding: '20px' }}>
|
|
253
|
-
<h4 style={{ marginTop: 0 }}>Add New User</h4>
|
|
254
|
-
|
|
255
|
-
<div style={{ marginBottom: '16px' }}>
|
|
256
|
-
<label style={{ display: 'block', marginBottom: '4px' }}>
|
|
257
|
-
Name
|
|
258
|
-
</label>
|
|
259
|
-
<input
|
|
260
|
-
type="text"
|
|
261
|
-
value={formData.name}
|
|
262
|
-
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
|
|
263
|
-
style={{ width: '100%', padding: '8px' }}
|
|
264
|
-
required
|
|
265
|
-
/>
|
|
266
|
-
</div>
|
|
267
|
-
|
|
268
|
-
<div style={{ marginBottom: '16px' }}>
|
|
269
|
-
<label style={{ display: 'block', marginBottom: '4px' }}>
|
|
270
|
-
Email
|
|
271
|
-
</label>
|
|
272
|
-
<input
|
|
273
|
-
type="email"
|
|
274
|
-
value={formData.email}
|
|
275
|
-
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
|
|
276
|
-
style={{ width: '100%', padding: '8px' }}
|
|
277
|
-
required
|
|
278
|
-
/>
|
|
279
|
-
</div>
|
|
280
|
-
|
|
281
|
-
<div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
|
|
282
|
-
<button
|
|
283
|
-
type="button"
|
|
284
|
-
onClick={() => setIsOpen(false)}
|
|
285
|
-
disabled={isLoading}
|
|
286
|
-
style={{ padding: '8px 16px' }}
|
|
287
|
-
>
|
|
288
|
-
Cancel
|
|
289
|
-
</button>
|
|
290
|
-
<button
|
|
291
|
-
type="submit"
|
|
292
|
-
disabled={isLoading}
|
|
293
|
-
style={{ padding: '8px 16px', backgroundColor: '#007bff', color: 'white' }}
|
|
294
|
-
>
|
|
295
|
-
{isLoading ? 'Adding...' : 'Add User'}
|
|
296
|
-
</button>
|
|
297
|
-
</div>
|
|
298
|
-
</form>
|
|
299
|
-
</Popover>
|
|
300
|
-
</div>
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Info popover with rich content
|
|
305
|
-
function InfoPopover() {
|
|
306
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
307
|
-
const anchorRef = useRef<HTMLSpanElement>(null);
|
|
308
|
-
|
|
309
|
-
return (
|
|
310
|
-
<div>
|
|
311
|
-
<p>
|
|
312
|
-
Our premium plan includes{' '}
|
|
313
|
-
<span
|
|
314
|
-
ref={anchorRef}
|
|
315
|
-
onMouseEnter={() => setIsOpen(true)}
|
|
316
|
-
onMouseLeave={() => setIsOpen(false)}
|
|
317
|
-
style={{
|
|
318
|
-
textDecoration: 'underline',
|
|
319
|
-
cursor: 'help',
|
|
320
|
-
color: '#007bff',
|
|
321
|
-
}}
|
|
322
|
-
>
|
|
323
|
-
advanced analytics
|
|
324
|
-
</span>
|
|
325
|
-
{' '}and reporting features.
|
|
326
|
-
</p>
|
|
327
|
-
|
|
328
|
-
<Popover
|
|
329
|
-
open={isOpen}
|
|
330
|
-
anchorEl={anchorRef}
|
|
331
|
-
position="top"
|
|
332
|
-
windowWidth={320}
|
|
333
|
-
>
|
|
334
|
-
<div style={{ padding: '16px' }}>
|
|
335
|
-
<h4 style={{ marginTop: 0, marginBottom: '12px' }}>
|
|
336
|
-
Advanced Analytics
|
|
337
|
-
</h4>
|
|
338
|
-
<ul style={{ margin: 0, paddingLeft: '20px' }}>
|
|
339
|
-
<li>Real-time dashboard updates</li>
|
|
340
|
-
<li>Custom report generation</li>
|
|
341
|
-
<li>Data export capabilities</li>
|
|
342
|
-
<li>Advanced filtering options</li>
|
|
343
|
-
<li>Historical trend analysis</li>
|
|
344
|
-
</ul>
|
|
345
|
-
|
|
346
|
-
<div style={{ marginTop: '12px', padding: '8px', backgroundColor: '#f8f9fa', borderRadius: '4px' }}>
|
|
347
|
-
<small style={{ color: '#666' }}>
|
|
348
|
-
💡 Tip: Use keyboard shortcuts to navigate dashboards faster
|
|
349
|
-
</small>
|
|
350
|
-
</div>
|
|
351
|
-
</div>
|
|
352
|
-
</Popover>
|
|
353
|
-
</div>
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Multi-step popover
|
|
358
|
-
function MultiStepPopover() {
|
|
359
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
360
|
-
const [currentStep, setCurrentStep] = useState(1);
|
|
361
|
-
const [formData, setFormData] = useState({
|
|
362
|
-
category: '',
|
|
363
|
-
title: '',
|
|
364
|
-
description: '',
|
|
365
|
-
});
|
|
366
|
-
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
367
|
-
|
|
368
|
-
const totalSteps = 3;
|
|
369
|
-
|
|
370
|
-
const renderStepContent = () => {
|
|
371
|
-
switch (currentStep) {
|
|
372
|
-
case 1:
|
|
373
|
-
return (
|
|
374
|
-
<div>
|
|
375
|
-
<h4>Step 1: Category</h4>
|
|
376
|
-
<select
|
|
377
|
-
value={formData.category}
|
|
378
|
-
onChange={(e) => setFormData(prev => ({ ...prev, category: e.target.value }))}
|
|
379
|
-
style={{ width: '100%', padding: '8px' }}
|
|
380
|
-
>
|
|
381
|
-
<option value="">Select category...</option>
|
|
382
|
-
<option value="bug">Bug Report</option>
|
|
383
|
-
<option value="feature">Feature Request</option>
|
|
384
|
-
<option value="question">Question</option>
|
|
385
|
-
</select>
|
|
386
|
-
</div>
|
|
387
|
-
);
|
|
388
|
-
case 2:
|
|
389
|
-
return (
|
|
390
|
-
<div>
|
|
391
|
-
<h4>Step 2: Title</h4>
|
|
392
|
-
<input
|
|
393
|
-
type="text"
|
|
394
|
-
placeholder="Enter a title..."
|
|
395
|
-
value={formData.title}
|
|
396
|
-
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
|
|
397
|
-
style={{ width: '100%', padding: '8px' }}
|
|
398
|
-
/>
|
|
399
|
-
</div>
|
|
400
|
-
);
|
|
401
|
-
case 3:
|
|
402
|
-
return (
|
|
403
|
-
<div>
|
|
404
|
-
<h4>Step 3: Description</h4>
|
|
405
|
-
<textarea
|
|
406
|
-
placeholder="Describe your request..."
|
|
407
|
-
value={formData.description}
|
|
408
|
-
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
|
|
409
|
-
style={{ width: '100%', padding: '8px', height: '80px' }}
|
|
410
|
-
/>
|
|
411
|
-
</div>
|
|
412
|
-
);
|
|
413
|
-
default:
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
const handleNext = () => {
|
|
419
|
-
if (currentStep < totalSteps) {
|
|
420
|
-
setCurrentStep(prev => prev + 1);
|
|
421
|
-
} else {
|
|
422
|
-
// Submit form
|
|
423
|
-
console.log('Form submitted:', formData);
|
|
424
|
-
setIsOpen(false);
|
|
425
|
-
setCurrentStep(1);
|
|
426
|
-
setFormData({ category: '', title: '', description: '' });
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
const handleBack = () => {
|
|
431
|
-
if (currentStep > 1) {
|
|
432
|
-
setCurrentStep(prev => prev - 1);
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
return (
|
|
437
|
-
<div>
|
|
438
|
-
<button
|
|
439
|
-
ref={anchorRef}
|
|
440
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
441
|
-
>
|
|
442
|
-
Create Request
|
|
443
|
-
</button>
|
|
444
|
-
|
|
445
|
-
<Popover
|
|
446
|
-
open={isOpen}
|
|
447
|
-
anchorEl={anchorRef}
|
|
448
|
-
position="bottom-start"
|
|
449
|
-
windowWidth={350}
|
|
450
|
-
>
|
|
451
|
-
<div style={{ padding: '20px' }}>
|
|
452
|
-
<div style={{ marginBottom: '16px' }}>
|
|
453
|
-
<div style={{ fontSize: '14px', color: '#666' }}>
|
|
454
|
-
Step {currentStep} of {totalSteps}
|
|
455
|
-
</div>
|
|
456
|
-
<div style={{
|
|
457
|
-
width: '100%',
|
|
458
|
-
height: '4px',
|
|
459
|
-
backgroundColor: '#e9ecef',
|
|
460
|
-
borderRadius: '2px',
|
|
461
|
-
marginTop: '4px'
|
|
462
|
-
}}>
|
|
463
|
-
<div style={{
|
|
464
|
-
width: `${(currentStep / totalSteps) * 100}%`,
|
|
465
|
-
height: '100%',
|
|
466
|
-
backgroundColor: '#007bff',
|
|
467
|
-
borderRadius: '2px',
|
|
468
|
-
transition: 'width 0.3s ease'
|
|
469
|
-
}} />
|
|
470
|
-
</div>
|
|
471
|
-
</div>
|
|
472
|
-
|
|
473
|
-
{renderStepContent()}
|
|
474
|
-
|
|
475
|
-
<div style={{
|
|
476
|
-
display: 'flex',
|
|
477
|
-
gap: '8px',
|
|
478
|
-
justifyContent: 'flex-end',
|
|
479
|
-
marginTop: '20px'
|
|
480
|
-
}}>
|
|
481
|
-
{currentStep > 1 && (
|
|
482
|
-
<button
|
|
483
|
-
onClick={handleBack}
|
|
484
|
-
style={{ padding: '8px 16px' }}
|
|
485
|
-
>
|
|
486
|
-
Back
|
|
487
|
-
</button>
|
|
488
|
-
)}
|
|
489
|
-
<button
|
|
490
|
-
onClick={() => setIsOpen(false)}
|
|
491
|
-
style={{ padding: '8px 16px' }}
|
|
492
|
-
>
|
|
493
|
-
Cancel
|
|
494
|
-
</button>
|
|
495
|
-
<button
|
|
496
|
-
onClick={handleNext}
|
|
497
|
-
style={{
|
|
498
|
-
padding: '8px 16px',
|
|
499
|
-
backgroundColor: '#007bff',
|
|
500
|
-
color: 'white'
|
|
501
|
-
}}
|
|
502
|
-
>
|
|
503
|
-
{currentStep === totalSteps ? 'Submit' : 'Next'}
|
|
504
|
-
</button>
|
|
505
|
-
</div>
|
|
506
|
-
</div>
|
|
507
|
-
</Popover>
|
|
508
|
-
</div>
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
## Props Reference
|
|
514
|
-
|
|
515
|
-
| Prop | Type | Default | Required | Description |
|
|
516
|
-
|------|------|---------|----------|-------------|
|
|
517
|
-
| `anchorEl` | `RefObject<HTMLElement>` | - | Yes | Reference to the element that anchors the popover |
|
|
518
|
-
| `internalWindowRef` | `RefObject<HTMLDivElement> \| null` | `null` | No | Reference to the popover window for external access |
|
|
519
|
-
| `position` | `Placement` | `'right-start'` | No | Position of the popover relative to anchor |
|
|
520
|
-
| `open` | `boolean` | - | Yes | Whether the popover is visible |
|
|
521
|
-
| `loading` | `boolean` | `false` | No | Whether the popover shows a loading state |
|
|
522
|
-
| `zIndex` | `number` | `1000` | No | CSS z-index for the popover |
|
|
523
|
-
| `windowWidth` | `number` | `undefined` | No | Fixed width for the popover window |
|
|
524
|
-
| `children` | `ReactNode \| undefined` | - | Yes | Content to display inside the popover |
|
|
525
|
-
|
|
526
|
-
## AI Agent Guidelines
|
|
527
|
-
|
|
528
|
-
### Recommended Patterns
|
|
529
|
-
|
|
530
|
-
1. **Anchor Element**: Always use refs for anchor elements
|
|
531
|
-
```typescript
|
|
532
|
-
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
533
|
-
|
|
534
|
-
<button ref={anchorRef}>Trigger</button>
|
|
535
|
-
<Popover anchorEl={anchorRef} open={isOpen}>
|
|
536
|
-
{/* content */}
|
|
537
|
-
</Popover>
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
2. **Controlled State**: Manage popover visibility in parent component
|
|
541
|
-
```typescript
|
|
542
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
543
|
-
|
|
544
|
-
// Good - controlled
|
|
545
|
-
<Popover open={isOpen} onClose={() => setIsOpen(false)}>
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
3. **Click Outside Handling**: Implement click outside to close
|
|
549
|
-
```typescript
|
|
550
|
-
useEffect(() => {
|
|
551
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
552
|
-
if (isOpen && !anchorRef.current?.contains(event.target as Node)) {
|
|
553
|
-
setIsOpen(false);
|
|
554
|
-
}
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
558
|
-
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
559
|
-
}, [isOpen]);
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
### Common Mistakes to Avoid
|
|
563
|
-
|
|
564
|
-
1. **Missing Anchor Ref**: Don't forget to attach the ref to the anchor element
|
|
565
|
-
2. **Portal Issues**: The popover uses portal rendering - don't nest it inside positioned containers
|
|
566
|
-
3. **Z-Index Conflicts**: Ensure proper z-index layering with other overlays
|
|
567
|
-
4. **Memory Leaks**: Clean up event listeners in useEffect cleanup
|
|
568
|
-
5. **Accessibility**: Ensure keyboard navigation and ARIA attributes are handled
|
|
569
|
-
|
|
570
|
-
### Integration Tips
|
|
571
|
-
|
|
572
|
-
1. **Positioning**: Use appropriate placement for your use case
|
|
573
|
-
```typescript
|
|
574
|
-
// Common placements
|
|
575
|
-
position="bottom-start" // Dropdown menus
|
|
576
|
-
position="top" // Tooltips above
|
|
577
|
-
position="right" // Side panels
|
|
578
|
-
position="left-end" // Context menus
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
2. **Content Sizing**: Use windowWidth for consistent sizing
|
|
582
|
-
```typescript
|
|
583
|
-
// Fixed width popover
|
|
584
|
-
<Popover windowWidth={300}>
|
|
585
|
-
{/* content will be max 300px wide */}
|
|
586
|
-
</Popover>
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
3. **Loading States**: Handle async content loading
|
|
590
|
-
```typescript
|
|
591
|
-
<Popover loading={isLoading}>
|
|
592
|
-
{isLoading ? <div>Loading...</div> : <div>Content</div>}
|
|
593
|
-
</Popover>
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
4. **Form Integration**: Works well with forms inside popover content
|
|
597
|
-
```typescript
|
|
598
|
-
<Popover>
|
|
599
|
-
<form onSubmit={handleSubmit}>
|
|
600
|
-
{/* form fields */}
|
|
601
|
-
</form>
|
|
602
|
-
</Popover>
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
5. **Keyboard Accessibility**: Handle Escape key to close
|
|
606
|
-
```typescript
|
|
607
|
-
useEffect(() => {
|
|
608
|
-
const handleKeyDown = (event: KeyboardEvent) => {
|
|
609
|
-
if (event.key === 'Escape' && isOpen) {
|
|
610
|
-
setIsOpen(false);
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
615
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
616
|
-
}, [isOpen]);
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
## Related Components
|
|
620
|
-
|
|
621
|
-
- **Tooltip**: For simple hover-based information display
|
|
622
|
-
- **Modal**: For full-screen overlays
|
|
623
|
-
- **Dropdown**: For select-style interfaces
|
|
624
|
-
- **Menu**: For navigation or action lists
|
|
625
|
-
- **Button**: Often used as popover triggers
|