mautourco-components 0.2.4 → 0.2.7
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/README.md +190 -190
- package/dist/components/atoms/Avatar/Avatar.d.ts +14 -14
- package/dist/components/atoms/Avatar/Avatar.js +31 -31
- package/dist/components/atoms/Button/Button.css +320 -320
- package/dist/components/atoms/Button/Button.d.ts +27 -27
- package/dist/components/atoms/Button/Button.js +35 -35
- package/dist/components/atoms/Checkbox/Checkbox.d.ts +13 -13
- package/dist/components/atoms/Checkbox/Checkbox.js +39 -39
- package/dist/components/atoms/Icon/Icon.d.ts +10 -10
- package/dist/components/atoms/Icon/Icon.js +123 -123
- package/dist/components/atoms/Icon/icons/ArrivalIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/ArrivalIcon.js +31 -31
- package/dist/components/atoms/Icon/icons/BuildingIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/BuildingIcon.js +36 -36
- package/dist/components/atoms/Icon/icons/CalendarIcon.d.ts +12 -12
- package/dist/components/atoms/Icon/icons/CalendarIcon.js +41 -41
- package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.js +36 -36
- package/dist/components/atoms/Icon/icons/CarIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/CarIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/Check.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/Check.js +30 -30
- package/dist/components/atoms/Icon/icons/CheckCircleIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/CheckCircleIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/Chevron.d.ts +9 -9
- package/dist/components/atoms/Icon/icons/Chevron.js +54 -54
- package/dist/components/atoms/Icon/icons/ChevronDownIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/ChevronDownIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/Close.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/Close.js +30 -30
- package/dist/components/atoms/Icon/icons/DeleteIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/DeleteIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/DepartureIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/DepartureIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/EyeIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/EyeIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/FacebookIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/FacebookIcon.js +36 -36
- package/dist/components/atoms/Icon/icons/HomeIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/HomeIcon.js +25 -25
- package/dist/components/atoms/Icon/icons/InfoIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/InfoIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/LinkedInIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/LinkedInIcon.js +36 -36
- package/dist/components/atoms/Icon/icons/MapPinIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/MapPinIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/MautoucoLogo.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/MautoucoLogo.js +37 -37
- package/dist/components/atoms/Icon/icons/MenuIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/MenuIcon.js +37 -37
- package/dist/components/atoms/Icon/icons/MinusIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/MinusIcon.js +25 -25
- package/dist/components/atoms/Icon/icons/MoreIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/MoreIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/PlaneIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/PlaneIcon.js +36 -36
- package/dist/components/atoms/Icon/icons/PlusIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/PlusIcon.js +25 -25
- package/dist/components/atoms/Icon/icons/Search.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/Search.js +30 -30
- package/dist/components/atoms/Icon/icons/Settings.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/Settings.js +30 -30
- package/dist/components/atoms/Icon/icons/ShipIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/ShipIcon.js +36 -36
- package/dist/components/atoms/Icon/icons/StrollerIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/StrollerIcon.js +30 -30
- package/dist/components/atoms/Icon/icons/TwitterIcon.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/TwitterIcon.js +36 -36
- package/dist/components/atoms/Icon/icons/User.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/User.js +30 -30
- package/dist/components/atoms/Icon/icons/UserIcon.d.ts +12 -12
- package/dist/components/atoms/Icon/icons/UserIcon.js +41 -41
- package/dist/components/atoms/Icon/icons/Youtube.d.ts +8 -8
- package/dist/components/atoms/Icon/icons/Youtube.js +36 -36
- package/dist/components/atoms/Illustration/Illustration.d.ts +14 -14
- package/dist/components/atoms/Illustration/Illustration.js +33 -33
- package/dist/components/atoms/Illustration/illustrations.d.ts +51 -51
- package/dist/components/atoms/Illustration/illustrations.js +97 -97
- package/dist/components/atoms/Inputs/DropdownInput/DropdownInput.d.ts +12 -12
- package/dist/components/atoms/Inputs/DropdownInput/DropdownInput.js +53 -53
- package/dist/components/atoms/Inputs/Input/Input.d.ts +15 -15
- package/dist/components/atoms/Inputs/Input/Input.js +27 -27
- package/dist/components/atoms/Inputs/Textarea/Textarea.d.ts +14 -14
- package/dist/components/atoms/Inputs/Textarea/Textarea.js +15 -15
- package/dist/components/atoms/Link/Link.d.ts +44 -44
- package/dist/components/atoms/Link/Link.js +76 -76
- package/dist/components/atoms/RatingStar/RatingStar.d.ts +40 -40
- package/dist/components/atoms/RatingStar/RatingStar.js +54 -54
- package/dist/components/atoms/SegmentedButton/SegmentedButton.d.ts +27 -27
- package/dist/components/atoms/SegmentedButton/SegmentedButton.js +49 -49
- package/dist/components/atoms/SelectedValue/SelectedValue.d.ts +11 -11
- package/dist/components/atoms/SelectedValue/SelectedValue.js +29 -29
- package/dist/components/atoms/Slider/Slider.d.ts +52 -52
- package/dist/components/atoms/Slider/Slider.js +30 -30
- package/dist/components/atoms/Spinner/Spinner.d.ts +9 -9
- package/dist/components/atoms/Spinner/Spinner.js +38 -38
- package/dist/components/atoms/Spinner/variants/ButtonSpinner.d.ts +8 -8
- package/dist/components/atoms/Spinner/variants/ButtonSpinner.js +19 -19
- package/dist/components/atoms/Spinner/variants/LoadingSpinner.d.ts +7 -7
- package/dist/components/atoms/Spinner/variants/LoadingSpinner.js +7 -7
- package/dist/components/atoms/Tab/Tab.css +266 -266
- package/dist/components/atoms/Tab/Tab.d.ts +22 -22
- package/dist/components/atoms/Tab/Tab.js +54 -54
- package/dist/components/atoms/Typography/Typography.d.ts +24 -24
- package/dist/components/atoms/Typography/Typography.js +100 -100
- package/dist/components/molecules/Calendar/CalendarInput.d.ts +34 -34
- package/dist/components/molecules/Calendar/CalendarInput.js +49 -49
- package/dist/components/molecules/Calendar/DateTime.d.ts +25 -25
- package/dist/components/molecules/Calendar/DateTime.js +106 -106
- package/dist/components/molecules/Calendar/TimePicker.d.ts +16 -16
- package/dist/components/molecules/Calendar/TimePicker.js +91 -91
- package/dist/components/molecules/LocationDropdown/LocationDropdown.d.ts +34 -34
- package/dist/components/molecules/LocationDropdown/LocationDropdown.js +120 -120
- package/dist/components/molecules/LocationDropdown/index.d.ts +2 -2
- package/dist/components/molecules/LocationDropdown/index.js +1 -1
- package/dist/components/molecules/MultiSelectDropdown/MultiSelectDropdown.d.ts +29 -29
- package/dist/components/molecules/MultiSelectDropdown/MultiSelectDropdown.js +106 -106
- package/dist/components/molecules/RatingTab/RatingTab.d.ts +39 -39
- package/dist/components/molecules/RatingTab/RatingTab.js +41 -41
- package/dist/components/molecules/TabGroup/TabGroup.d.ts +17 -17
- package/dist/components/molecules/TabGroup/TabGroup.js +30 -30
- package/dist/components/molecules/UserCard/UserCard.d.ts +20 -20
- package/dist/components/molecules/UserCard/UserCard.js +57 -57
- package/dist/components/organisms/CardContainer/CardContainer.d.ts +37 -37
- package/dist/components/organisms/CardContainer/CardContainer.js +27 -27
- package/dist/components/organisms/DateTimePicker/DateTimePicker.d.ts +15 -15
- package/dist/components/organisms/DateTimePicker/DateTimePicker.js +66 -66
- package/dist/components/organisms/Dialog/Dialog.d.ts +103 -103
- package/dist/components/organisms/Dialog/Dialog.js +162 -162
- package/dist/components/organisms/Footer/Footer.d.ts +38 -38
- package/dist/components/organisms/Footer/Footer.js +74 -74
- package/dist/components/organisms/PaxSelector/PaxSelector.d.ts +63 -63
- package/dist/components/organisms/PaxSelector/PaxSelector.js +402 -402
- package/dist/components/organisms/RoundTrip/RoundTrip.d.ts +54 -54
- package/dist/components/organisms/RoundTrip/RoundTrip.js +179 -179
- package/dist/components/organisms/RoundTrip/index.d.ts +2 -2
- package/dist/components/organisms/RoundTrip/index.js +1 -1
- package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.d.ts +35 -35
- package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.js +192 -192
- package/dist/components/organisms/SearchBarTransfer/index.d.ts +2 -2
- package/dist/components/organisms/SearchBarTransfer/index.js +1 -1
- package/dist/components/organisms/TopNavigation/DesktopNav.d.ts +33 -33
- package/dist/components/organisms/TopNavigation/DesktopNav.js +32 -26
- package/dist/components/organisms/TopNavigation/MobileNav.d.ts +32 -32
- package/dist/components/organisms/TopNavigation/MobileNav.js +45 -45
- package/dist/components/organisms/TopNavigation/TopNavigation.d.ts +33 -33
- package/dist/components/organisms/TopNavigation/TopNavigation.js +20 -20
- package/dist/components/organisms/TransferLine/TransferLine.d.ts +53 -53
- package/dist/components/organisms/TransferLine/TransferLine.js +179 -179
- package/dist/components/ui/button.d.ts +10 -10
- package/dist/components/ui/button.js +56 -56
- package/dist/components/ui/calendar.d.ts +8 -8
- package/dist/components/ui/calendar.js +87 -87
- package/dist/components/ui/popover.d.ts +7 -7
- package/dist/components/ui/popover.js +42 -42
- package/dist/hooks/useMobile.d.ts +5 -5
- package/dist/hooks/useMobile.js +26 -26
- package/dist/index.d.ts +49 -49
- package/dist/index.js +46 -46
- package/dist/lib/utils.d.ts +7 -7
- package/dist/lib/utils.js +13 -13
- package/dist/styles/components/avatar.css +122 -122
- package/dist/styles/components/calendar.css +140 -140
- package/dist/styles/components/checkbox.css +206 -206
- package/dist/styles/components/dropdown.css +269 -269
- package/dist/styles/components/forms.css +209 -209
- package/dist/styles/components/illustration.css +123 -123
- package/dist/styles/components/molecule/calendarInput.css +133 -133
- package/dist/styles/components/molecule/dateTime.css +126 -126
- package/dist/styles/components/molecule/location-dropdown.css +132 -132
- package/dist/styles/components/molecule/timePicker.css +122 -122
- package/dist/styles/components/multiselect-dropdown.css +286 -286
- package/dist/styles/components/organism/card-container.css +148 -148
- package/dist/styles/components/organism/dialog.css +168 -168
- package/dist/styles/components/organism/footer.css +119 -119
- package/dist/styles/components/organism/pax-selector.css +617 -617
- package/dist/styles/components/organism/round-trip.css +139 -139
- package/dist/styles/components/organism/search-bar-transfer.css +158 -161
- package/dist/styles/components/organism/topnavigation.css +143 -143
- package/dist/styles/components/organism/transfer-line.css +138 -138
- package/dist/styles/components/rating-star.css +145 -145
- package/dist/styles/components/rating-tab.css +179 -179
- package/dist/styles/components/scrollbar.css +155 -155
- package/dist/styles/components/segmented-button.css +214 -214
- package/dist/styles/components/selected-value.css +175 -175
- package/dist/styles/components/slider.css +182 -182
- package/dist/styles/components/typography.css +245 -245
- package/dist/styles/tokens/tokens.css +119 -119
- package/dist/styles/tokens/tokens.d.ts +3108 -3108
- package/dist/styles/tokens/tokens.js +2652 -2652
- package/package.json +103 -103
- package/src/components/atoms/Avatar/Avatar.tsx +60 -60
- package/src/components/atoms/Button/Button.css +200 -200
- package/src/components/atoms/Button/Button.tsx +82 -82
- package/src/components/atoms/Checkbox/Checkbox.tsx +83 -83
- package/src/components/atoms/Icon/Icon.tsx +163 -163
- package/src/components/atoms/Icon/icons/ArrivalIcon.tsx +52 -52
- package/src/components/atoms/Icon/icons/BuildingIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/CalendarIcon.tsx +63 -63
- package/src/components/atoms/Icon/icons/CalendarOutlineIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/CarIcon.tsx +44 -44
- package/src/components/atoms/Icon/icons/Check.tsx +36 -36
- package/src/components/atoms/Icon/icons/CheckCircleIcon.tsx +48 -48
- package/src/components/atoms/Icon/icons/Chevron.tsx +73 -73
- package/src/components/atoms/Icon/icons/ChevronDownIcon.tsx +46 -46
- package/src/components/atoms/Icon/icons/Close.tsx +39 -39
- package/src/components/atoms/Icon/icons/DeleteIcon.tsx +44 -44
- package/src/components/atoms/Icon/icons/DepartureIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/EyeIcon.tsx +44 -44
- package/src/components/atoms/Icon/icons/FacebookIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/HomeIcon.tsx +52 -52
- package/src/components/atoms/Icon/icons/InfoIcon.tsx +44 -44
- package/src/components/atoms/Icon/icons/LinkedInIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/MapPinIcon.tsx +44 -44
- package/src/components/atoms/Icon/icons/MautoucoLogo.tsx +93 -93
- package/src/components/atoms/Icon/icons/MenuIcon.tsx +49 -49
- package/src/components/atoms/Icon/icons/MinusIcon.tsx +45 -45
- package/src/components/atoms/Icon/icons/MoreIcon.tsx +44 -44
- package/src/components/atoms/Icon/icons/PlaneIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/PlusIcon.tsx +45 -45
- package/src/components/atoms/Icon/icons/Search.tsx +37 -37
- package/src/components/atoms/Icon/icons/Settings.tsx +38 -38
- package/src/components/atoms/Icon/icons/ShipIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/StrollerIcon.tsx +44 -44
- package/src/components/atoms/Icon/icons/TwitterIcon.tsx +50 -50
- package/src/components/atoms/Icon/icons/User.tsx +37 -37
- package/src/components/atoms/Icon/icons/UserIcon.tsx +63 -63
- package/src/components/atoms/Icon/icons/Youtube.tsx +50 -50
- package/src/components/atoms/Illustration/Illustration.tsx +28 -28
- package/src/components/atoms/Illustration/illustrations.ts +116 -116
- package/src/components/atoms/Inputs/DropdownInput/DropdownInput.tsx +96 -96
- package/src/components/atoms/Inputs/Textarea/Textarea.tsx +51 -51
- package/src/components/atoms/Link/Link.tsx +168 -168
- package/src/components/atoms/RatingStar/RatingStar.tsx +114 -114
- package/src/components/atoms/SegmentedButton/SegmentedButton.tsx +94 -94
- package/src/components/atoms/SelectedValue/SelectedValue.tsx +59 -59
- package/src/components/atoms/Slider/Slider.tsx +95 -95
- package/src/components/atoms/Spinner/Spinner.tsx +56 -56
- package/src/components/atoms/Spinner/variants/ButtonSpinner.tsx +37 -37
- package/src/components/atoms/Spinner/variants/LoadingSpinner.tsx +22 -22
- package/src/components/atoms/Tab/Tab.css +147 -147
- package/src/components/atoms/Tab/Tab.tsx +96 -96
- package/src/components/atoms/Typography/Typography.tsx +153 -153
- package/src/components/molecules/Calendar/CalendarInput.tsx +135 -135
- package/src/components/molecules/Calendar/DateTime.tsx +172 -172
- package/src/components/molecules/Calendar/TimePicker.tsx +174 -174
- package/src/components/molecules/LocationDropdown/LocationDropdown.tsx +234 -234
- package/src/components/molecules/LocationDropdown/index.ts +2 -2
- package/src/components/molecules/RatingTab/RatingTab.tsx +96 -96
- package/src/components/molecules/TabGroup/TabGroup.tsx +60 -60
- package/src/components/molecules/UserCard/UserCard.stories.tsx +36 -36
- package/src/components/molecules/UserCard/UserCard.tsx +173 -173
- package/src/components/organisms/CardContainer/CardContainer.tsx +66 -66
- package/src/components/organisms/DateTimePicker/DateTimePicker.tsx +110 -110
- package/src/components/organisms/Dialog/Dialog.tsx +352 -352
- package/src/components/organisms/Footer/Footer.tsx +290 -290
- package/src/components/organisms/PaxSelector/PaxSelector.tsx +979 -979
- package/src/components/organisms/RoundTrip/RoundTrip.tsx +335 -335
- package/src/components/organisms/RoundTrip/index.ts +2 -2
- package/src/components/organisms/SearchBarTransfer/SearchBarTransfer.tsx +388 -388
- package/src/components/organisms/SearchBarTransfer/index.ts +2 -2
- package/src/components/organisms/TopNavigation/DesktopNav.tsx +133 -122
- package/src/components/organisms/TopNavigation/MobileNav.tsx +212 -212
- package/src/components/organisms/TopNavigation/TopNavigation.tsx +45 -45
- package/src/components/organisms/TransferLine/TransferLine.tsx +369 -369
- package/src/components/ui/button.tsx +60 -60
- package/src/components/ui/calendar.tsx +246 -246
- package/src/components/ui/popover.tsx +46 -46
- package/src/styles/components/avatar.css +58 -58
- package/src/styles/components/calendar.css +85 -85
- package/src/styles/components/checkbox.css +130 -130
- package/src/styles/components/dropdown.css +214 -214
- package/src/styles/components/forms.css +147 -147
- package/src/styles/components/illustration.css +7 -7
- package/src/styles/components/molecule/calendarInput.css +156 -156
- package/src/styles/components/molecule/dateTime.css +14 -14
- package/src/styles/components/molecule/location-dropdown.css +204 -204
- package/src/styles/components/molecule/timePicker.css +78 -78
- package/src/styles/components/multiselect-dropdown.css +230 -230
- package/src/styles/components/organism/card-container.css +49 -49
- package/src/styles/components/organism/dialog.css +241 -241
- package/src/styles/components/organism/footer.css +113 -113
- package/src/styles/components/organism/pax-selector.css +702 -702
- package/src/styles/components/organism/round-trip.css +55 -55
- package/src/styles/components/organism/search-bar-transfer.css +128 -127
- package/src/styles/components/organism/topnavigation.css +161 -161
- package/src/styles/components/organism/transfer-line.css +86 -86
- package/src/styles/components/rating-star.css +39 -39
- package/src/styles/components/rating-tab.css +83 -83
- package/src/styles/components/scrollbar.css +63 -63
- package/src/styles/components/segmented-button.css +134 -134
- package/src/styles/components/selected-value.css +80 -80
- package/src/styles/components/slider.css +86 -86
- package/src/styles/components/typography.css +251 -251
- package/src/styles/fonts.css +50 -0
- package/src/styles/tokens/tokens.css +119 -119
- package/src/styles/tokens/tokens.js +12 -6
|
@@ -1,352 +1,352 @@
|
|
|
1
|
-
import React, { useEffect, useCallback, useRef } from 'react';
|
|
2
|
-
import { createPortal } from 'react-dom';
|
|
3
|
-
import { useMobile } from '../../../hooks/useMobile';
|
|
4
|
-
import Button from '../../atoms/Button/Button';
|
|
5
|
-
import Icon from '../../atoms/Icon/Icon';
|
|
6
|
-
import '../../../styles/components/organism/dialog.css';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Size variants for the Dialog component
|
|
10
|
-
*/
|
|
11
|
-
export type DialogSize = 'desktop' | 'tablet' | 'mobile' | 'full' | 'responsive';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Props for the Dialog component
|
|
15
|
-
*/
|
|
16
|
-
export interface DialogProps {
|
|
17
|
-
/**
|
|
18
|
-
* Whether the dialog is open
|
|
19
|
-
*/
|
|
20
|
-
isOpen: boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Callback when the dialog should close
|
|
23
|
-
*/
|
|
24
|
-
onClose: () => void;
|
|
25
|
-
/**
|
|
26
|
-
* Title displayed in the dialog header
|
|
27
|
-
*/
|
|
28
|
-
title: string;
|
|
29
|
-
/**
|
|
30
|
-
* Content to be rendered in the dialog body
|
|
31
|
-
*/
|
|
32
|
-
children: React.ReactNode;
|
|
33
|
-
/**
|
|
34
|
-
* Footer content or buttons. If not provided, default Cancel/Save buttons are shown
|
|
35
|
-
*/
|
|
36
|
-
footer?: React.ReactNode;
|
|
37
|
-
/**
|
|
38
|
-
* Size variant of the dialog
|
|
39
|
-
* @default 'responsive'
|
|
40
|
-
*/
|
|
41
|
-
size?: DialogSize;
|
|
42
|
-
/**
|
|
43
|
-
* Text for the primary action button (when using default footer)
|
|
44
|
-
* @default 'Save and update'
|
|
45
|
-
*/
|
|
46
|
-
primaryButtonText?: string;
|
|
47
|
-
/**
|
|
48
|
-
* Text for the secondary action button (when using default footer)
|
|
49
|
-
* @default 'Cancel'
|
|
50
|
-
*/
|
|
51
|
-
secondaryButtonText?: string;
|
|
52
|
-
/**
|
|
53
|
-
* Callback for primary button click (when using default footer)
|
|
54
|
-
*/
|
|
55
|
-
onPrimaryClick?: () => void;
|
|
56
|
-
/**
|
|
57
|
-
* Callback for secondary button click (when using default footer)
|
|
58
|
-
*/
|
|
59
|
-
onSecondaryClick?: () => void;
|
|
60
|
-
/**
|
|
61
|
-
* Whether to show the footer
|
|
62
|
-
* @default true
|
|
63
|
-
*/
|
|
64
|
-
showFooter?: boolean;
|
|
65
|
-
/**
|
|
66
|
-
* Whether to show the close button in the header
|
|
67
|
-
* @default true
|
|
68
|
-
*/
|
|
69
|
-
showCloseButton?: boolean;
|
|
70
|
-
/**
|
|
71
|
-
* Whether clicking the overlay closes the dialog
|
|
72
|
-
* @default true
|
|
73
|
-
*/
|
|
74
|
-
closeOnOverlayClick?: boolean;
|
|
75
|
-
/**
|
|
76
|
-
* Whether pressing Escape closes the dialog
|
|
77
|
-
* @default true
|
|
78
|
-
*/
|
|
79
|
-
closeOnEscape?: boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Additional CSS classes for the dialog content
|
|
82
|
-
*/
|
|
83
|
-
className?: string;
|
|
84
|
-
/**
|
|
85
|
-
* ID for aria-labelledby
|
|
86
|
-
*/
|
|
87
|
-
ariaLabelledBy?: string;
|
|
88
|
-
/**
|
|
89
|
-
* ID for aria-describedby
|
|
90
|
-
*/
|
|
91
|
-
ariaDescribedBy?: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Dialog component - A modal dialog that adapts to different screen sizes
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```tsx
|
|
99
|
-
* <Dialog
|
|
100
|
-
* isOpen={isOpen}
|
|
101
|
-
* onClose={() => setIsOpen(false)}
|
|
102
|
-
* title="Edit Information"
|
|
103
|
-
* onPrimaryClick={handleSave}
|
|
104
|
-
* >
|
|
105
|
-
* <p>Your content here</p>
|
|
106
|
-
* </Dialog>
|
|
107
|
-
* ```
|
|
108
|
-
*/
|
|
109
|
-
export const Dialog: React.FC<DialogProps> = ({
|
|
110
|
-
isOpen,
|
|
111
|
-
onClose,
|
|
112
|
-
title,
|
|
113
|
-
children,
|
|
114
|
-
footer,
|
|
115
|
-
size = 'responsive',
|
|
116
|
-
primaryButtonText = 'Save and update',
|
|
117
|
-
secondaryButtonText = 'Cancel',
|
|
118
|
-
onPrimaryClick,
|
|
119
|
-
onSecondaryClick,
|
|
120
|
-
showFooter = true,
|
|
121
|
-
showCloseButton = true,
|
|
122
|
-
closeOnOverlayClick = true,
|
|
123
|
-
closeOnEscape = true,
|
|
124
|
-
className = '',
|
|
125
|
-
ariaLabelledBy,
|
|
126
|
-
ariaDescribedBy,
|
|
127
|
-
}) => {
|
|
128
|
-
const { isMobile, isTablet, isDesktop } = useMobile();
|
|
129
|
-
const dialogRef = useRef<HTMLDivElement>(null);
|
|
130
|
-
const previousFocusRef = useRef<HTMLElement | null>(null);
|
|
131
|
-
|
|
132
|
-
// Determine the actual size based on responsive setting
|
|
133
|
-
const getActualSize = (): Exclude<DialogSize, 'responsive'> => {
|
|
134
|
-
if (size !== 'responsive') return size;
|
|
135
|
-
if (isMobile) return 'mobile';
|
|
136
|
-
if (isTablet) return 'tablet';
|
|
137
|
-
return 'desktop';
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const actualSize = getActualSize();
|
|
141
|
-
const isMobileSize = actualSize === 'mobile';
|
|
142
|
-
const isTabletSize = actualSize === 'tablet';
|
|
143
|
-
|
|
144
|
-
// Handle escape key
|
|
145
|
-
const handleKeyDown = useCallback(
|
|
146
|
-
(event: KeyboardEvent) => {
|
|
147
|
-
if (closeOnEscape && event.key === 'Escape') {
|
|
148
|
-
onClose();
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
[closeOnEscape, onClose]
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
// Handle overlay click
|
|
155
|
-
const handleOverlayClick = useCallback(
|
|
156
|
-
(event: React.MouseEvent<HTMLDivElement>) => {
|
|
157
|
-
if (closeOnOverlayClick && event.target === event.currentTarget) {
|
|
158
|
-
onClose();
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
[closeOnOverlayClick, onClose]
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
// Focus management and scroll lock
|
|
165
|
-
useEffect(() => {
|
|
166
|
-
if (isOpen) {
|
|
167
|
-
// Store current focus
|
|
168
|
-
previousFocusRef.current = document.activeElement as HTMLElement;
|
|
169
|
-
|
|
170
|
-
// Focus the dialog
|
|
171
|
-
if (dialogRef.current) {
|
|
172
|
-
dialogRef.current.focus();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Lock body scroll
|
|
176
|
-
document.body.style.overflow = 'hidden';
|
|
177
|
-
|
|
178
|
-
// Add escape listener
|
|
179
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
180
|
-
} else {
|
|
181
|
-
// Unlock body scroll
|
|
182
|
-
document.body.style.overflow = '';
|
|
183
|
-
|
|
184
|
-
// Restore focus
|
|
185
|
-
if (previousFocusRef.current) {
|
|
186
|
-
previousFocusRef.current.focus();
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return () => {
|
|
191
|
-
document.body.style.overflow = '';
|
|
192
|
-
document.removeEventListener('keydown', handleKeyDown);
|
|
193
|
-
};
|
|
194
|
-
}, [isOpen, handleKeyDown]);
|
|
195
|
-
|
|
196
|
-
// Handle secondary button click
|
|
197
|
-
const handleSecondaryClick = () => {
|
|
198
|
-
if (onSecondaryClick) {
|
|
199
|
-
onSecondaryClick();
|
|
200
|
-
} else {
|
|
201
|
-
onClose();
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
// Handle primary button click
|
|
206
|
-
const handlePrimaryClick = () => {
|
|
207
|
-
if (onPrimaryClick) {
|
|
208
|
-
onPrimaryClick();
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// Generate unique IDs for accessibility
|
|
213
|
-
const titleId = ariaLabelledBy || `dialog-title-${title.replace(/\s+/g, '-').toLowerCase()}`;
|
|
214
|
-
const bodyId = ariaDescribedBy || `dialog-body-${title.replace(/\s+/g, '-').toLowerCase()}`;
|
|
215
|
-
|
|
216
|
-
// Build class names
|
|
217
|
-
const contentClasses = [
|
|
218
|
-
'dialog__content',
|
|
219
|
-
`dialog__content--${actualSize}`,
|
|
220
|
-
size === 'responsive' ? 'dialog__content--responsive' : '',
|
|
221
|
-
className,
|
|
222
|
-
]
|
|
223
|
-
.filter(Boolean)
|
|
224
|
-
.join(' ');
|
|
225
|
-
|
|
226
|
-
const headerClasses = [
|
|
227
|
-
'dialog__header',
|
|
228
|
-
isMobileSize ? 'dialog__header--mobile' : '',
|
|
229
|
-
size === 'responsive' ? 'dialog__header--responsive' : '',
|
|
230
|
-
]
|
|
231
|
-
.filter(Boolean)
|
|
232
|
-
.join(' ');
|
|
233
|
-
|
|
234
|
-
const titleClasses = [
|
|
235
|
-
'dialog__title',
|
|
236
|
-
isMobileSize ? 'dialog__title--mobile' : '',
|
|
237
|
-
isTabletSize ? 'dialog__title--tablet' : '',
|
|
238
|
-
size === 'responsive' ? 'dialog__title--responsive' : '',
|
|
239
|
-
]
|
|
240
|
-
.filter(Boolean)
|
|
241
|
-
.join(' ');
|
|
242
|
-
|
|
243
|
-
const closeButtonClasses = 'dialog__close-button';
|
|
244
|
-
|
|
245
|
-
const bodyClasses = [
|
|
246
|
-
'dialog__body',
|
|
247
|
-
isMobileSize ? 'dialog__body--mobile' : '',
|
|
248
|
-
size === 'responsive' ? 'dialog__body--responsive' : '',
|
|
249
|
-
]
|
|
250
|
-
.filter(Boolean)
|
|
251
|
-
.join(' ');
|
|
252
|
-
|
|
253
|
-
const footerClasses = [
|
|
254
|
-
'dialog__footer',
|
|
255
|
-
isMobileSize ? 'dialog__footer--mobile' : '',
|
|
256
|
-
size === 'responsive' ? 'dialog__footer--responsive' : '',
|
|
257
|
-
]
|
|
258
|
-
.filter(Boolean)
|
|
259
|
-
.join(' ');
|
|
260
|
-
|
|
261
|
-
const footerButtonsClasses = [
|
|
262
|
-
'dialog__footer-buttons',
|
|
263
|
-
isMobileSize ? 'dialog__footer-buttons--mobile' : '',
|
|
264
|
-
size === 'responsive' ? 'dialog__footer-buttons--responsive' : '',
|
|
265
|
-
]
|
|
266
|
-
.filter(Boolean)
|
|
267
|
-
.join(' ');
|
|
268
|
-
|
|
269
|
-
if (!isOpen) {
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const dialogContent = (
|
|
274
|
-
<>
|
|
275
|
-
{/* Overlay */}
|
|
276
|
-
<div
|
|
277
|
-
className="dialog-overlay"
|
|
278
|
-
onClick={handleOverlayClick}
|
|
279
|
-
aria-hidden="true"
|
|
280
|
-
/>
|
|
281
|
-
|
|
282
|
-
{/* Dialog container */}
|
|
283
|
-
<div
|
|
284
|
-
className="dialog"
|
|
285
|
-
role="dialog"
|
|
286
|
-
aria-modal="true"
|
|
287
|
-
aria-labelledby={titleId}
|
|
288
|
-
aria-describedby={bodyId}
|
|
289
|
-
ref={dialogRef}
|
|
290
|
-
tabIndex={-1}
|
|
291
|
-
onClick={handleOverlayClick}
|
|
292
|
-
>
|
|
293
|
-
{/* Dialog content */}
|
|
294
|
-
<div className={contentClasses}>
|
|
295
|
-
{/* Header */}
|
|
296
|
-
<div className={headerClasses}>
|
|
297
|
-
<h2 id={titleId} className={titleClasses}>
|
|
298
|
-
{title}
|
|
299
|
-
</h2>
|
|
300
|
-
{showCloseButton && (
|
|
301
|
-
<button
|
|
302
|
-
type="button"
|
|
303
|
-
className={closeButtonClasses}
|
|
304
|
-
onClick={onClose}
|
|
305
|
-
aria-label="Close dialog"
|
|
306
|
-
>
|
|
307
|
-
<Icon name="close" size="md" />
|
|
308
|
-
</button>
|
|
309
|
-
)}
|
|
310
|
-
</div>
|
|
311
|
-
|
|
312
|
-
{/* Body */}
|
|
313
|
-
<div id={bodyId} className={bodyClasses}>
|
|
314
|
-
<div className="dialog__body-content">{children}</div>
|
|
315
|
-
</div>
|
|
316
|
-
|
|
317
|
-
{/* Footer */}
|
|
318
|
-
{showFooter && (
|
|
319
|
-
<div className={footerClasses}>
|
|
320
|
-
{footer ? (
|
|
321
|
-
footer
|
|
322
|
-
) : (
|
|
323
|
-
<div className={footerButtonsClasses}>
|
|
324
|
-
<Button
|
|
325
|
-
variant="outline-secondary"
|
|
326
|
-
size={isMobileSize ? 'sm' : 'md'}
|
|
327
|
-
onClick={handleSecondaryClick}
|
|
328
|
-
>
|
|
329
|
-
{secondaryButtonText}
|
|
330
|
-
</Button>
|
|
331
|
-
<Button
|
|
332
|
-
variant="secondary"
|
|
333
|
-
size={isMobileSize ? 'sm' : 'md'}
|
|
334
|
-
onClick={handlePrimaryClick}
|
|
335
|
-
>
|
|
336
|
-
{primaryButtonText}
|
|
337
|
-
</Button>
|
|
338
|
-
</div>
|
|
339
|
-
)}
|
|
340
|
-
</div>
|
|
341
|
-
)}
|
|
342
|
-
</div>
|
|
343
|
-
</div>
|
|
344
|
-
</>
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
// Use portal to render at the document body level
|
|
348
|
-
return createPortal(dialogContent, document.body);
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
export default Dialog;
|
|
352
|
-
|
|
1
|
+
import React, { useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
import { useMobile } from '../../../hooks/useMobile';
|
|
4
|
+
import Button from '../../atoms/Button/Button';
|
|
5
|
+
import Icon from '../../atoms/Icon/Icon';
|
|
6
|
+
import '../../../styles/components/organism/dialog.css';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Size variants for the Dialog component
|
|
10
|
+
*/
|
|
11
|
+
export type DialogSize = 'desktop' | 'tablet' | 'mobile' | 'full' | 'responsive';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Props for the Dialog component
|
|
15
|
+
*/
|
|
16
|
+
export interface DialogProps {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the dialog is open
|
|
19
|
+
*/
|
|
20
|
+
isOpen: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Callback when the dialog should close
|
|
23
|
+
*/
|
|
24
|
+
onClose: () => void;
|
|
25
|
+
/**
|
|
26
|
+
* Title displayed in the dialog header
|
|
27
|
+
*/
|
|
28
|
+
title: string;
|
|
29
|
+
/**
|
|
30
|
+
* Content to be rendered in the dialog body
|
|
31
|
+
*/
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Footer content or buttons. If not provided, default Cancel/Save buttons are shown
|
|
35
|
+
*/
|
|
36
|
+
footer?: React.ReactNode;
|
|
37
|
+
/**
|
|
38
|
+
* Size variant of the dialog
|
|
39
|
+
* @default 'responsive'
|
|
40
|
+
*/
|
|
41
|
+
size?: DialogSize;
|
|
42
|
+
/**
|
|
43
|
+
* Text for the primary action button (when using default footer)
|
|
44
|
+
* @default 'Save and update'
|
|
45
|
+
*/
|
|
46
|
+
primaryButtonText?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Text for the secondary action button (when using default footer)
|
|
49
|
+
* @default 'Cancel'
|
|
50
|
+
*/
|
|
51
|
+
secondaryButtonText?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Callback for primary button click (when using default footer)
|
|
54
|
+
*/
|
|
55
|
+
onPrimaryClick?: () => void;
|
|
56
|
+
/**
|
|
57
|
+
* Callback for secondary button click (when using default footer)
|
|
58
|
+
*/
|
|
59
|
+
onSecondaryClick?: () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Whether to show the footer
|
|
62
|
+
* @default true
|
|
63
|
+
*/
|
|
64
|
+
showFooter?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Whether to show the close button in the header
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
showCloseButton?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Whether clicking the overlay closes the dialog
|
|
72
|
+
* @default true
|
|
73
|
+
*/
|
|
74
|
+
closeOnOverlayClick?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Whether pressing Escape closes the dialog
|
|
77
|
+
* @default true
|
|
78
|
+
*/
|
|
79
|
+
closeOnEscape?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Additional CSS classes for the dialog content
|
|
82
|
+
*/
|
|
83
|
+
className?: string;
|
|
84
|
+
/**
|
|
85
|
+
* ID for aria-labelledby
|
|
86
|
+
*/
|
|
87
|
+
ariaLabelledBy?: string;
|
|
88
|
+
/**
|
|
89
|
+
* ID for aria-describedby
|
|
90
|
+
*/
|
|
91
|
+
ariaDescribedBy?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Dialog component - A modal dialog that adapts to different screen sizes
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```tsx
|
|
99
|
+
* <Dialog
|
|
100
|
+
* isOpen={isOpen}
|
|
101
|
+
* onClose={() => setIsOpen(false)}
|
|
102
|
+
* title="Edit Information"
|
|
103
|
+
* onPrimaryClick={handleSave}
|
|
104
|
+
* >
|
|
105
|
+
* <p>Your content here</p>
|
|
106
|
+
* </Dialog>
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export const Dialog: React.FC<DialogProps> = ({
|
|
110
|
+
isOpen,
|
|
111
|
+
onClose,
|
|
112
|
+
title,
|
|
113
|
+
children,
|
|
114
|
+
footer,
|
|
115
|
+
size = 'responsive',
|
|
116
|
+
primaryButtonText = 'Save and update',
|
|
117
|
+
secondaryButtonText = 'Cancel',
|
|
118
|
+
onPrimaryClick,
|
|
119
|
+
onSecondaryClick,
|
|
120
|
+
showFooter = true,
|
|
121
|
+
showCloseButton = true,
|
|
122
|
+
closeOnOverlayClick = true,
|
|
123
|
+
closeOnEscape = true,
|
|
124
|
+
className = '',
|
|
125
|
+
ariaLabelledBy,
|
|
126
|
+
ariaDescribedBy,
|
|
127
|
+
}) => {
|
|
128
|
+
const { isMobile, isTablet, isDesktop } = useMobile();
|
|
129
|
+
const dialogRef = useRef<HTMLDivElement>(null);
|
|
130
|
+
const previousFocusRef = useRef<HTMLElement | null>(null);
|
|
131
|
+
|
|
132
|
+
// Determine the actual size based on responsive setting
|
|
133
|
+
const getActualSize = (): Exclude<DialogSize, 'responsive'> => {
|
|
134
|
+
if (size !== 'responsive') return size;
|
|
135
|
+
if (isMobile) return 'mobile';
|
|
136
|
+
if (isTablet) return 'tablet';
|
|
137
|
+
return 'desktop';
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const actualSize = getActualSize();
|
|
141
|
+
const isMobileSize = actualSize === 'mobile';
|
|
142
|
+
const isTabletSize = actualSize === 'tablet';
|
|
143
|
+
|
|
144
|
+
// Handle escape key
|
|
145
|
+
const handleKeyDown = useCallback(
|
|
146
|
+
(event: KeyboardEvent) => {
|
|
147
|
+
if (closeOnEscape && event.key === 'Escape') {
|
|
148
|
+
onClose();
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
[closeOnEscape, onClose]
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Handle overlay click
|
|
155
|
+
const handleOverlayClick = useCallback(
|
|
156
|
+
(event: React.MouseEvent<HTMLDivElement>) => {
|
|
157
|
+
if (closeOnOverlayClick && event.target === event.currentTarget) {
|
|
158
|
+
onClose();
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
[closeOnOverlayClick, onClose]
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// Focus management and scroll lock
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (isOpen) {
|
|
167
|
+
// Store current focus
|
|
168
|
+
previousFocusRef.current = document.activeElement as HTMLElement;
|
|
169
|
+
|
|
170
|
+
// Focus the dialog
|
|
171
|
+
if (dialogRef.current) {
|
|
172
|
+
dialogRef.current.focus();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Lock body scroll
|
|
176
|
+
document.body.style.overflow = 'hidden';
|
|
177
|
+
|
|
178
|
+
// Add escape listener
|
|
179
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
180
|
+
} else {
|
|
181
|
+
// Unlock body scroll
|
|
182
|
+
document.body.style.overflow = '';
|
|
183
|
+
|
|
184
|
+
// Restore focus
|
|
185
|
+
if (previousFocusRef.current) {
|
|
186
|
+
previousFocusRef.current.focus();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return () => {
|
|
191
|
+
document.body.style.overflow = '';
|
|
192
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
193
|
+
};
|
|
194
|
+
}, [isOpen, handleKeyDown]);
|
|
195
|
+
|
|
196
|
+
// Handle secondary button click
|
|
197
|
+
const handleSecondaryClick = () => {
|
|
198
|
+
if (onSecondaryClick) {
|
|
199
|
+
onSecondaryClick();
|
|
200
|
+
} else {
|
|
201
|
+
onClose();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Handle primary button click
|
|
206
|
+
const handlePrimaryClick = () => {
|
|
207
|
+
if (onPrimaryClick) {
|
|
208
|
+
onPrimaryClick();
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Generate unique IDs for accessibility
|
|
213
|
+
const titleId = ariaLabelledBy || `dialog-title-${title.replace(/\s+/g, '-').toLowerCase()}`;
|
|
214
|
+
const bodyId = ariaDescribedBy || `dialog-body-${title.replace(/\s+/g, '-').toLowerCase()}`;
|
|
215
|
+
|
|
216
|
+
// Build class names
|
|
217
|
+
const contentClasses = [
|
|
218
|
+
'dialog__content',
|
|
219
|
+
`dialog__content--${actualSize}`,
|
|
220
|
+
size === 'responsive' ? 'dialog__content--responsive' : '',
|
|
221
|
+
className,
|
|
222
|
+
]
|
|
223
|
+
.filter(Boolean)
|
|
224
|
+
.join(' ');
|
|
225
|
+
|
|
226
|
+
const headerClasses = [
|
|
227
|
+
'dialog__header',
|
|
228
|
+
isMobileSize ? 'dialog__header--mobile' : '',
|
|
229
|
+
size === 'responsive' ? 'dialog__header--responsive' : '',
|
|
230
|
+
]
|
|
231
|
+
.filter(Boolean)
|
|
232
|
+
.join(' ');
|
|
233
|
+
|
|
234
|
+
const titleClasses = [
|
|
235
|
+
'dialog__title',
|
|
236
|
+
isMobileSize ? 'dialog__title--mobile' : '',
|
|
237
|
+
isTabletSize ? 'dialog__title--tablet' : '',
|
|
238
|
+
size === 'responsive' ? 'dialog__title--responsive' : '',
|
|
239
|
+
]
|
|
240
|
+
.filter(Boolean)
|
|
241
|
+
.join(' ');
|
|
242
|
+
|
|
243
|
+
const closeButtonClasses = 'dialog__close-button';
|
|
244
|
+
|
|
245
|
+
const bodyClasses = [
|
|
246
|
+
'dialog__body',
|
|
247
|
+
isMobileSize ? 'dialog__body--mobile' : '',
|
|
248
|
+
size === 'responsive' ? 'dialog__body--responsive' : '',
|
|
249
|
+
]
|
|
250
|
+
.filter(Boolean)
|
|
251
|
+
.join(' ');
|
|
252
|
+
|
|
253
|
+
const footerClasses = [
|
|
254
|
+
'dialog__footer',
|
|
255
|
+
isMobileSize ? 'dialog__footer--mobile' : '',
|
|
256
|
+
size === 'responsive' ? 'dialog__footer--responsive' : '',
|
|
257
|
+
]
|
|
258
|
+
.filter(Boolean)
|
|
259
|
+
.join(' ');
|
|
260
|
+
|
|
261
|
+
const footerButtonsClasses = [
|
|
262
|
+
'dialog__footer-buttons',
|
|
263
|
+
isMobileSize ? 'dialog__footer-buttons--mobile' : '',
|
|
264
|
+
size === 'responsive' ? 'dialog__footer-buttons--responsive' : '',
|
|
265
|
+
]
|
|
266
|
+
.filter(Boolean)
|
|
267
|
+
.join(' ');
|
|
268
|
+
|
|
269
|
+
if (!isOpen) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const dialogContent = (
|
|
274
|
+
<>
|
|
275
|
+
{/* Overlay */}
|
|
276
|
+
<div
|
|
277
|
+
className="dialog-overlay"
|
|
278
|
+
onClick={handleOverlayClick}
|
|
279
|
+
aria-hidden="true"
|
|
280
|
+
/>
|
|
281
|
+
|
|
282
|
+
{/* Dialog container */}
|
|
283
|
+
<div
|
|
284
|
+
className="dialog"
|
|
285
|
+
role="dialog"
|
|
286
|
+
aria-modal="true"
|
|
287
|
+
aria-labelledby={titleId}
|
|
288
|
+
aria-describedby={bodyId}
|
|
289
|
+
ref={dialogRef}
|
|
290
|
+
tabIndex={-1}
|
|
291
|
+
onClick={handleOverlayClick}
|
|
292
|
+
>
|
|
293
|
+
{/* Dialog content */}
|
|
294
|
+
<div className={contentClasses}>
|
|
295
|
+
{/* Header */}
|
|
296
|
+
<div className={headerClasses}>
|
|
297
|
+
<h2 id={titleId} className={titleClasses}>
|
|
298
|
+
{title}
|
|
299
|
+
</h2>
|
|
300
|
+
{showCloseButton && (
|
|
301
|
+
<button
|
|
302
|
+
type="button"
|
|
303
|
+
className={closeButtonClasses}
|
|
304
|
+
onClick={onClose}
|
|
305
|
+
aria-label="Close dialog"
|
|
306
|
+
>
|
|
307
|
+
<Icon name="close" size="md" />
|
|
308
|
+
</button>
|
|
309
|
+
)}
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
{/* Body */}
|
|
313
|
+
<div id={bodyId} className={bodyClasses}>
|
|
314
|
+
<div className="dialog__body-content">{children}</div>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
{/* Footer */}
|
|
318
|
+
{showFooter && (
|
|
319
|
+
<div className={footerClasses}>
|
|
320
|
+
{footer ? (
|
|
321
|
+
footer
|
|
322
|
+
) : (
|
|
323
|
+
<div className={footerButtonsClasses}>
|
|
324
|
+
<Button
|
|
325
|
+
variant="outline-secondary"
|
|
326
|
+
size={isMobileSize ? 'sm' : 'md'}
|
|
327
|
+
onClick={handleSecondaryClick}
|
|
328
|
+
>
|
|
329
|
+
{secondaryButtonText}
|
|
330
|
+
</Button>
|
|
331
|
+
<Button
|
|
332
|
+
variant="secondary"
|
|
333
|
+
size={isMobileSize ? 'sm' : 'md'}
|
|
334
|
+
onClick={handlePrimaryClick}
|
|
335
|
+
>
|
|
336
|
+
{primaryButtonText}
|
|
337
|
+
</Button>
|
|
338
|
+
</div>
|
|
339
|
+
)}
|
|
340
|
+
</div>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
</>
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
// Use portal to render at the document body level
|
|
348
|
+
return createPortal(dialogContent, document.body);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
export default Dialog;
|
|
352
|
+
|