mautourco-components 0.1.2 → 0.2.1

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.
Files changed (161) hide show
  1. package/README.md +10 -6
  2. package/dist/components/atoms/Checkbox/Checkbox.js +7 -1
  3. package/dist/components/atoms/Icon/Icon.d.ts +1 -1
  4. package/dist/components/atoms/Icon/Icon.js +22 -1
  5. package/dist/components/atoms/Icon/icons/BuildingIcon.d.ts +8 -0
  6. package/dist/components/atoms/Icon/icons/BuildingIcon.js +36 -0
  7. package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.d.ts +8 -0
  8. package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.js +36 -0
  9. package/dist/components/atoms/Icon/icons/HomeIcon.d.ts +8 -0
  10. package/dist/components/atoms/Icon/icons/HomeIcon.js +25 -0
  11. package/dist/components/atoms/Icon/icons/MinusIcon.d.ts +8 -0
  12. package/dist/components/atoms/Icon/icons/MinusIcon.js +25 -0
  13. package/dist/components/atoms/Icon/icons/PlaneIcon.d.ts +8 -0
  14. package/dist/components/atoms/Icon/icons/PlaneIcon.js +36 -0
  15. package/dist/components/atoms/Icon/icons/PlusIcon.d.ts +8 -0
  16. package/dist/components/atoms/Icon/icons/PlusIcon.js +25 -0
  17. package/dist/components/atoms/Icon/icons/ShipIcon.d.ts +8 -0
  18. package/dist/components/atoms/Icon/icons/ShipIcon.js +36 -0
  19. package/dist/components/atoms/Illustration/Illustration.d.ts +14 -0
  20. package/dist/components/atoms/Illustration/Illustration.js +33 -0
  21. package/dist/components/atoms/Illustration/illustrations.d.ts +51 -0
  22. package/dist/components/atoms/Illustration/illustrations.js +97 -0
  23. package/dist/components/atoms/RatingStar/RatingStar.d.ts +40 -0
  24. package/dist/components/atoms/RatingStar/RatingStar.js +54 -0
  25. package/dist/components/atoms/SegmentedButton/SegmentedButton.d.ts +27 -0
  26. package/dist/components/atoms/SegmentedButton/SegmentedButton.js +49 -0
  27. package/dist/components/atoms/Slider/Slider.d.ts +52 -0
  28. package/dist/components/atoms/Slider/Slider.js +30 -0
  29. package/dist/components/molecules/Calendar/CalendarInput.d.ts +34 -0
  30. package/dist/components/molecules/Calendar/CalendarInput.js +49 -0
  31. package/dist/components/molecules/Calendar/DateTime.d.ts +25 -0
  32. package/dist/components/molecules/Calendar/DateTime.js +106 -0
  33. package/dist/components/molecules/Calendar/TimePicker.d.ts +16 -0
  34. package/dist/components/molecules/Calendar/TimePicker.js +91 -0
  35. package/dist/components/molecules/LocationDropdown/LocationDropdown.d.ts +34 -0
  36. package/dist/components/molecules/LocationDropdown/LocationDropdown.js +120 -0
  37. package/dist/components/molecules/LocationDropdown/index.d.ts +2 -0
  38. package/dist/components/molecules/LocationDropdown/index.js +1 -0
  39. package/dist/components/molecules/RatingTab/RatingTab.d.ts +39 -0
  40. package/dist/components/molecules/RatingTab/RatingTab.js +41 -0
  41. package/dist/components/molecules/TabGroup/TabGroup.d.ts +17 -0
  42. package/dist/components/molecules/TabGroup/TabGroup.js +30 -0
  43. package/dist/components/organisms/CardContainer/CardContainer.d.ts +37 -0
  44. package/dist/components/organisms/CardContainer/CardContainer.js +27 -0
  45. package/dist/components/organisms/DateTimePicker/DateTimePicker.d.ts +15 -0
  46. package/dist/components/organisms/DateTimePicker/DateTimePicker.js +66 -0
  47. package/dist/components/organisms/Dialog/Dialog.d.ts +103 -0
  48. package/dist/components/organisms/Dialog/Dialog.js +162 -0
  49. package/dist/components/organisms/PaxSelector/PaxSelector.d.ts +63 -0
  50. package/dist/components/organisms/PaxSelector/PaxSelector.js +402 -0
  51. package/dist/components/organisms/RoundTrip/RoundTrip.d.ts +54 -0
  52. package/dist/components/organisms/RoundTrip/RoundTrip.js +179 -0
  53. package/dist/components/organisms/RoundTrip/index.d.ts +2 -0
  54. package/dist/components/organisms/RoundTrip/index.js +1 -0
  55. package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.d.ts +35 -0
  56. package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.js +192 -0
  57. package/dist/components/organisms/SearchBarTransfer/index.d.ts +2 -0
  58. package/dist/components/organisms/SearchBarTransfer/index.js +1 -0
  59. package/dist/components/organisms/TransferLine/TransferLine.d.ts +53 -0
  60. package/dist/components/organisms/TransferLine/TransferLine.js +179 -0
  61. package/dist/components/ui/button.d.ts +10 -0
  62. package/dist/components/ui/button.js +56 -0
  63. package/dist/components/ui/calendar.d.ts +8 -0
  64. package/dist/components/ui/calendar.js +87 -0
  65. package/dist/components/ui/popover.d.ts +7 -0
  66. package/dist/components/ui/popover.js +42 -0
  67. package/dist/index.d.ts +26 -0
  68. package/dist/index.js +18 -0
  69. package/dist/lib/utils.d.ts +7 -0
  70. package/dist/lib/utils.js +13 -0
  71. package/dist/styles/components/avatar.css +2165 -0
  72. package/dist/styles/components/calendar.css +2214 -0
  73. package/dist/styles/components/checkbox.css +2212 -0
  74. package/dist/styles/components/dropdown.css +2295 -0
  75. package/dist/styles/components/forms.css +2229 -0
  76. package/dist/styles/components/illustration.css +2081 -0
  77. package/dist/styles/components/molecule/calendarInput.css +2308 -0
  78. package/dist/styles/components/molecule/dateTime.css +2092 -0
  79. package/dist/styles/components/molecule/location-dropdown.css +2405 -0
  80. package/dist/styles/components/molecule/timePicker.css +2204 -0
  81. package/dist/styles/components/multiselect-dropdown.css +2312 -0
  82. package/dist/styles/components/organism/card-container.css +2128 -0
  83. package/dist/styles/components/organism/dialog.css +2441 -0
  84. package/dist/styles/components/organism/footer.css +2387 -0
  85. package/dist/styles/components/organism/pax-selector.css +2800 -0
  86. package/dist/styles/components/organism/round-trip.css +2143 -0
  87. package/dist/styles/components/organism/search-bar-transfer.css +2258 -0
  88. package/dist/styles/components/organism/topnavigation.css +2499 -0
  89. package/dist/styles/components/organism/transfer-line.css +2208 -0
  90. package/dist/styles/components/rating-star.css +2113 -0
  91. package/dist/styles/components/rating-tab.css +2157 -0
  92. package/dist/styles/components/scrollbar.css +2143 -0
  93. package/dist/styles/components/segmented-button.css +2218 -0
  94. package/dist/styles/components/selected-value.css +2159 -0
  95. package/dist/styles/components/slider.css +2164 -0
  96. package/dist/styles/components/typography.css +2417 -0
  97. package/dist/styles/tokens/_tokens.scss +2072 -0
  98. package/dist/styles/tokens/tokens.css +2075 -0
  99. package/package.json +24 -12
  100. package/src/components/atoms/Button/Button.css +34 -34
  101. package/src/components/atoms/Checkbox/Checkbox.tsx +83 -69
  102. package/src/components/atoms/Icon/Icon.tsx +30 -2
  103. package/src/components/atoms/Icon/icons/BuildingIcon.tsx +50 -0
  104. package/src/components/atoms/Icon/icons/CalendarOutlineIcon.tsx +50 -0
  105. package/src/components/atoms/Icon/icons/HomeIcon.tsx +52 -0
  106. package/src/components/atoms/Icon/icons/MinusIcon.tsx +45 -0
  107. package/src/components/atoms/Icon/icons/PlaneIcon.tsx +50 -0
  108. package/src/components/atoms/Icon/icons/PlusIcon.tsx +45 -0
  109. package/src/components/atoms/Icon/icons/ShipIcon.tsx +50 -0
  110. package/src/components/atoms/Illustration/Illustration.tsx +28 -0
  111. package/src/components/atoms/Illustration/illustrations.ts +116 -0
  112. package/src/components/atoms/RatingStar/RatingStar.tsx +114 -0
  113. package/src/components/atoms/SegmentedButton/SegmentedButton.tsx +94 -0
  114. package/src/components/atoms/Slider/Slider.tsx +95 -0
  115. package/src/components/molecules/Calendar/CalendarInput.tsx +135 -0
  116. package/src/components/molecules/Calendar/DateTime.tsx +172 -0
  117. package/src/components/molecules/Calendar/TimePicker.tsx +174 -0
  118. package/src/components/molecules/LocationDropdown/LocationDropdown.tsx +234 -0
  119. package/src/components/molecules/LocationDropdown/index.ts +2 -0
  120. package/src/components/molecules/RatingTab/RatingTab.tsx +96 -0
  121. package/src/components/molecules/TabGroup/TabGroup.tsx +60 -0
  122. package/src/components/molecules/UserCard/UserCard.stories.tsx +2 -2
  123. package/src/components/organisms/CardContainer/CardContainer.tsx +66 -0
  124. package/src/components/organisms/DateTimePicker/DateTimePicker.tsx +110 -0
  125. package/src/components/organisms/Dialog/Dialog.tsx +352 -0
  126. package/src/components/organisms/PaxSelector/PaxSelector.tsx +979 -0
  127. package/src/components/organisms/RoundTrip/RoundTrip.tsx +335 -0
  128. package/src/components/organisms/RoundTrip/index.ts +2 -0
  129. package/src/components/organisms/SearchBarTransfer/SearchBarTransfer.tsx +388 -0
  130. package/src/components/organisms/SearchBarTransfer/index.ts +2 -0
  131. package/src/components/organisms/TransferLine/TransferLine.tsx +369 -0
  132. package/src/components/ui/button.tsx +60 -0
  133. package/src/components/ui/calendar.tsx +246 -0
  134. package/src/components/ui/popover.tsx +46 -0
  135. package/src/styles/components/calendar.css +86 -0
  136. package/src/styles/components/checkbox.css +130 -132
  137. package/src/styles/components/dropdown.css +40 -40
  138. package/src/styles/components/forms.css +51 -51
  139. package/src/styles/components/illustration.css +7 -0
  140. package/src/styles/components/molecule/calendarInput.css +157 -0
  141. package/src/styles/components/molecule/dateTime.css +14 -0
  142. package/src/styles/components/molecule/location-dropdown.css +204 -0
  143. package/src/styles/components/molecule/timePicker.css +79 -0
  144. package/src/styles/components/multiselect-dropdown.css +230 -231
  145. package/src/styles/components/organism/card-container.css +49 -0
  146. package/src/styles/components/organism/dialog.css +241 -0
  147. package/src/styles/components/organism/pax-selector.css +702 -0
  148. package/src/styles/components/organism/round-trip.css +55 -0
  149. package/src/styles/components/organism/search-bar-transfer.css +128 -0
  150. package/src/styles/components/organism/transfer-line.css +86 -0
  151. package/src/styles/components/rating-star.css +39 -0
  152. package/src/styles/components/rating-tab.css +83 -0
  153. package/src/styles/components/segmented-button.css +134 -0
  154. package/src/styles/components/selected-value.css +16 -16
  155. package/src/styles/components/slider.css +86 -0
  156. package/src/styles/components/typography.css +36 -36
  157. package/src/styles/tokens/tokens.css +1093 -1093
  158. package/dist/components/atoms/Typography/Heading/Heading.d.ts +0 -9
  159. package/dist/components/atoms/Typography/Heading/Heading.js +0 -25
  160. package/dist/components/atoms/Typography/Text/Text.d.ts +0 -10
  161. package/dist/components/atoms/Typography/Text/Text.js +0 -77
@@ -0,0 +1,234 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import Icon from '../../atoms/Icon/Icon';
3
+ import { Text } from '../../atoms/Typography/Typography';
4
+ import '../../../styles/components/molecule/location-dropdown.css';
5
+
6
+ export interface LocationOption {
7
+ id: string | number;
8
+ label: string;
9
+ type: 'airport' | 'port' | 'accommodation';
10
+ disabled?: boolean;
11
+ }
12
+
13
+ export interface LocationGroup {
14
+ id: string;
15
+ label: string;
16
+ options: LocationOption[];
17
+ }
18
+
19
+ export interface LocationData {
20
+ id: string | number;
21
+ locationName: string;
22
+ }
23
+
24
+ export interface LocationDropdownProps {
25
+ options?: LocationOption[];
26
+ groups?: LocationGroup[];
27
+ selectedValue?: string | number | null;
28
+ placeholder?: string;
29
+ label?: string;
30
+ onSelectionChange: (location: LocationData | null) => void;
31
+ disabled?: boolean;
32
+ error?: boolean;
33
+ className?: string;
34
+ type?: 'airport-port' | 'accommodation' | 'pickup-dropoff';
35
+ maxHeight?: number;
36
+ direction?: 'pickup' | 'dropoff';
37
+ showGroupTitles?: boolean;
38
+ }
39
+
40
+ const LocationDropdown: React.FC<LocationDropdownProps> = ({
41
+ options = [],
42
+ groups = [],
43
+ selectedValue = null,
44
+ placeholder = 'Select a location',
45
+ label,
46
+ onSelectionChange,
47
+ disabled = false,
48
+ error = false,
49
+ className = '',
50
+ type = 'airport-port',
51
+ maxHeight = 240,
52
+ direction = undefined,
53
+ showGroupTitles = true
54
+ }) => {
55
+ const [isOpen, setIsOpen] = useState(false);
56
+ const dropdownRef = useRef<HTMLDivElement>(null);
57
+
58
+ // Close dropdown when clicking outside
59
+ useEffect(() => {
60
+ const handleClickOutside = (event: MouseEvent) => {
61
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
62
+ setIsOpen(false);
63
+ }
64
+ };
65
+
66
+ document.addEventListener('mousedown', handleClickOutside);
67
+ return () => {
68
+ document.removeEventListener('mousedown', handleClickOutside);
69
+ };
70
+ }, []);
71
+
72
+ const handleToggleDropdown = () => {
73
+ if (!disabled) {
74
+ setIsOpen(!isOpen);
75
+ }
76
+ };
77
+
78
+ const handleOptionSelect = (option: LocationOption) => {
79
+ if (disabled) return;
80
+ const locationData: LocationData = {
81
+ id: option.id,
82
+ locationName: option.label,
83
+ };
84
+ onSelectionChange(locationData);
85
+ setIsOpen(false);
86
+ };
87
+
88
+ const getSelectedOption = (): LocationOption | null => {
89
+ if (!selectedValue) return null;
90
+
91
+ // Search in flat options
92
+ const flatOption = options.find(opt => opt.id === selectedValue);
93
+ if (flatOption) return flatOption;
94
+
95
+ // Search in groups
96
+ for (const group of groups) {
97
+ const groupOption = group.options.find(opt => opt.id === selectedValue);
98
+ if (groupOption) return groupOption;
99
+ }
100
+
101
+ return null;
102
+ };
103
+
104
+ const getIconForType = (optionType: 'airport' | 'port' | 'accommodation') => {
105
+ switch (optionType) {
106
+ case 'airport':
107
+ return 'plane';
108
+ case 'port':
109
+ return 'ship';
110
+ case 'accommodation':
111
+ return 'map-pin';
112
+ default:
113
+ return 'map-pin';
114
+ }
115
+ };
116
+
117
+ const getInputIcon = () => {
118
+ if (selectedOption) {
119
+ // For airports, show arrival/departure based on direction
120
+ console.log({direction});
121
+
122
+ if (selectedOption.type === 'airport' && direction) {
123
+ return direction === 'pickup' ? 'arrival' : 'departure';
124
+ }
125
+ return getIconForType(selectedOption.type);
126
+ }
127
+ // Use arrival/departure icons based on direction for pickup/dropoff types
128
+ if ((type === 'pickup-dropoff' || type === 'airport-port') && direction) {
129
+ return direction === 'pickup' ? 'arrival' : 'departure';
130
+ }
131
+ return 'map-pin';
132
+ };
133
+
134
+ const getOptionIcon = (option: LocationOption, isSelected: boolean) => {
135
+ // For selected airports, show arrival/departure based on direction
136
+ if (isSelected && option.type === 'airport' && direction) {
137
+ return direction === 'pickup' ? 'arrival' : 'departure';
138
+ }
139
+ // For other types (port, accommodation), show their specific icon
140
+ return getIconForType(option.type);
141
+ };
142
+
143
+ const getDropdownState = () => {
144
+ if (disabled) return 'disabled';
145
+ if (error) return 'error';
146
+ if (isOpen) return 'open';
147
+ if (selectedValue) return 'selected';
148
+ return 'default';
149
+ };
150
+
151
+ const selectedOption = getSelectedOption();
152
+ const displayText = selectedOption ? selectedOption.label : placeholder;
153
+
154
+ // Prepare all options (flat or grouped)
155
+ const allOptions = groups.length > 0 ? groups : [{ id: 'default', label: '', options }];
156
+
157
+ return (
158
+ <div ref={dropdownRef} className={`location-dropdown location-dropdown--${type} ${className}`}>
159
+ {label && (
160
+ <div className="location-dropdown__label">
161
+ <Text size="sm" variant="medium">{label}</Text>
162
+ </div>
163
+ )}
164
+
165
+ <div
166
+ className={`location-dropdown__input location-dropdown__input--${getDropdownState()}`}
167
+ onClick={handleToggleDropdown}
168
+ >
169
+ <div className="location-dropdown__input-content">
170
+ <Icon
171
+ name={getInputIcon()}
172
+ size="sm"
173
+ className={`location-dropdown__input-icon ${!selectedOption ? 'location-dropdown__input-icon--placeholder' : ''}`}
174
+ />
175
+ <span className={`location-dropdown__input-text ${!selectedOption ? 'location-dropdown__input-text--placeholder' : ''}`}>
176
+ {displayText}
177
+ </span>
178
+ </div>
179
+ <Icon
180
+ name="chevron-down"
181
+ size="sm"
182
+ className="location-dropdown__input-chevron"
183
+ />
184
+ </div>
185
+
186
+ {isOpen && (
187
+ <div className="location-dropdown__panel">
188
+ <div className="location-dropdown__content" style={{ maxHeight: `${maxHeight}px` }}>
189
+ <div className="location-dropdown__options-wrapper">
190
+ {allOptions.map((group, groupIndex) => (
191
+ <div key={group.id} className="location-dropdown__group">
192
+ {showGroupTitles && group.label && groups.length > 0 && (
193
+ <>
194
+ {groupIndex > 0 && <div className="location-dropdown__divider" />}
195
+ <div className="location-dropdown__group-header">
196
+ <Text size="xs" variant="bold">{group.label}</Text>
197
+ </div>
198
+ </>
199
+ )}
200
+
201
+ <div className="location-dropdown__group-options">
202
+ {group.options.map((option) => {
203
+ const isSelected = selectedValue === option.id;
204
+ const isDisabled = option.disabled || disabled;
205
+
206
+ return (
207
+ <div
208
+ key={option.id}
209
+ className={`location-dropdown__option ${isSelected ? 'location-dropdown__option--selected' : ''} ${isDisabled ? 'location-dropdown__option--disabled' : ''}`}
210
+ onClick={() => !isDisabled && handleOptionSelect(option)}
211
+ >
212
+ <Icon
213
+ name={getOptionIcon(option, isSelected)}
214
+ size="sm"
215
+ className="location-dropdown__option-icon"
216
+ />
217
+ <span className="location-dropdown__option-text">
218
+ {option.label}
219
+ </span>
220
+ </div>
221
+ );
222
+ })}
223
+ </div>
224
+ </div>
225
+ ))}
226
+ </div>
227
+ </div>
228
+ </div>
229
+ )}
230
+ </div>
231
+ );
232
+ };
233
+
234
+ export default LocationDropdown;
@@ -0,0 +1,2 @@
1
+ export { default } from './LocationDropdown';
2
+ export type { LocationDropdownProps, LocationOption, LocationGroup } from './LocationDropdown';
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import Check from '../../atoms/Icon/icons/Check';
3
+ import { Text } from '../../atoms/Typography/Typography';
4
+ import RatingStar from '../../atoms/RatingStar/RatingStar';
5
+
6
+ export interface RatingTabProps {
7
+ /**
8
+ * Label displayed next to the checkbox (e.g. "5 stars").
9
+ */
10
+ label: string;
11
+ /**
12
+ * Rating value displayed in the star row (0–max, typically 1–5).
13
+ */
14
+ ratingValue: number;
15
+ /**
16
+ * Maximum number of stars.
17
+ */
18
+ maxStars?: number;
19
+ /**
20
+ * Whether this tab is selected.
21
+ */
22
+ checked?: boolean;
23
+ /**
24
+ * Whether the tab is disabled.
25
+ */
26
+ disabled?: boolean;
27
+ /**
28
+ * Called when the selection state changes.
29
+ */
30
+ onChange?: (checked: boolean) => void;
31
+ /**
32
+ * Additional CSS classes for the container.
33
+ */
34
+ className?: string;
35
+ }
36
+
37
+ /**
38
+ * RatingTab molecule
39
+ *
40
+ * Combines a checkbox-like selector, a label and the RatingStar atom.
41
+ * Used for filters like "5 stars", "4 stars", etc.
42
+ */
43
+ const RatingTab: React.FC<RatingTabProps> = ({
44
+ label,
45
+ ratingValue,
46
+ maxStars = 5,
47
+ checked = false,
48
+ disabled = false,
49
+ onChange,
50
+ className = '',
51
+ }) => {
52
+ const handleToggle = () => {
53
+ if (disabled || !onChange) return;
54
+ onChange(!checked);
55
+ };
56
+
57
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
58
+ if (disabled || !onChange) return;
59
+
60
+ if (event.key === ' ' || event.key === 'Enter') {
61
+ event.preventDefault();
62
+ onChange(!checked);
63
+ }
64
+ };
65
+
66
+ const stateClass = disabled ? 'rating-tab--disabled' : checked ? 'rating-tab--active' : 'rating-tab--default';
67
+
68
+ const containerClasses = ['rating-tab', stateClass, className].filter(Boolean).join(' ');
69
+
70
+ return (
71
+ <div
72
+ className={containerClasses}
73
+ role="checkbox"
74
+ aria-checked={checked}
75
+ aria-disabled={disabled}
76
+ tabIndex={disabled ? -1 : 0}
77
+ onClick={handleToggle}
78
+ onKeyDown={handleKeyDown}
79
+ >
80
+ <div className="rating-tab__left">
81
+ <div className="rating-tab__checkbox">
82
+ {checked && <Check size="sm" className="rating-tab__checkbox-icon" />}
83
+ </div>
84
+ <Text size="sm" className="rating-tab__label">
85
+ {label}
86
+ </Text>
87
+ </div>
88
+
89
+ <RatingStar value={ratingValue} max={maxStars} />
90
+ </div>
91
+ );
92
+ };
93
+
94
+ export default RatingTab;
95
+
96
+
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import Tab, { TabProps } from '../../atoms/Tab/Tab';
3
+
4
+ export type TabItem<T = string> = {
5
+ label: string;
6
+ value: T;
7
+ disabled?: boolean;
8
+ };
9
+
10
+ export interface TabGroupProps<T = string> {
11
+ items: TabItem<T>[];
12
+ size?: TabProps['size'];
13
+ variant?: TabProps['variant'];
14
+ active?: T;
15
+ defaultActive?: T;
16
+ onChange?: (value: T) => void;
17
+ className?: string;
18
+ }
19
+
20
+ export function TabGroup<T = string>({
21
+ items,
22
+ size = 'lg',
23
+ variant = 'inline',
24
+ active,
25
+ defaultActive,
26
+ onChange,
27
+ className = '',
28
+ }: TabGroupProps<T>) {
29
+ const [internalActive, setInternalActive] = React.useState<T | undefined>(
30
+ defaultActive ?? items[0]?.value
31
+ );
32
+
33
+ const isControlled = active !== undefined;
34
+ const current = (isControlled ? active : internalActive) as T | undefined;
35
+
36
+ const handleClick = (item: TabItem<T>) => {
37
+ if (item.disabled) return;
38
+ if (!isControlled) setInternalActive(item.value);
39
+ onChange?.(item.value);
40
+ };
41
+
42
+ return (
43
+ <div className={`tab-group flex ${className}`} role="tablist">
44
+ {items.map((it) => (
45
+ <Tab
46
+ key={String(it.value)}
47
+ isActive={current === it.value}
48
+ size={size}
49
+ variant={variant}
50
+ disabled={it.disabled}
51
+ onClick={() => handleClick(it)}
52
+ >
53
+ {it.label}
54
+ </Tab>
55
+ ))}
56
+ </div>
57
+ );
58
+ }
59
+
60
+ export default TabGroup;
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react-webpack5';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
 
3
3
  import { UserCard } from './UserCard';
4
4
 
@@ -33,4 +33,4 @@ export const Default: Story = {
33
33
  <UserCard {...args} />
34
34
  </div>
35
35
  ),
36
- };
36
+ };
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+
3
+ export type CardContainerSpacing = 'compact' | 'normal' | 'relaxed' | 'chill';
4
+
5
+ export interface CardContainerProps {
6
+ /**
7
+ * The content to be displayed inside the card container
8
+ */
9
+ children: React.ReactNode;
10
+
11
+ /**
12
+ * Spacing variant for the card container padding
13
+ * - compact: 8px padding
14
+ * - normal: 12px padding
15
+ * - relaxed: 16px padding
16
+ * - chill: 24px padding
17
+ */
18
+ spacing?: CardContainerSpacing;
19
+
20
+ /**
21
+ * Additional CSS classes to apply to the card container
22
+ */
23
+ className?: string;
24
+
25
+ /**
26
+ * Optional click handler
27
+ */
28
+ onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
29
+
30
+ /**
31
+ * Optional data attributes for testing or tracking
32
+ */
33
+ 'data-testid'?: string;
34
+ }
35
+
36
+ /**
37
+ * CardContainer is a versatile UI element that groups related content,
38
+ * such as text, images, buttons, or interactive elements.
39
+ *
40
+ * It supports different spacing variants to control the visual density
41
+ * and padding of the container.
42
+ */
43
+ export const CardContainer: React.FC<CardContainerProps> = ({
44
+ children,
45
+ spacing = 'normal',
46
+ className = '',
47
+ onClick,
48
+ 'data-testid': testId,
49
+ }) => {
50
+ const baseClass = 'card-container';
51
+ const spacingClass = `card-container--spacing-${spacing}`;
52
+ const classes = [baseClass, spacingClass, className].filter(Boolean).join(' ');
53
+
54
+ return (
55
+ <div
56
+ className={classes}
57
+ onClick={onClick}
58
+ data-testid={testId}
59
+ >
60
+ {children}
61
+ </div>
62
+ );
63
+ };
64
+
65
+ export default CardContainer;
66
+
@@ -0,0 +1,110 @@
1
+ import React, { useState } from "react";
2
+ import CalendarInput from "../../molecules/Calendar/CalendarInput";
3
+ import DateTime, { DateTimeProps } from "../../molecules/Calendar/DateTime";
4
+ import { Popover, PopoverTrigger, PopoverContent } from "../../ui/popover";
5
+
6
+ import { format } from "date-fns";
7
+
8
+ export interface DateTimePickerProps extends Partial<DateTimeProps> {
9
+ placeholder?: string;
10
+ disabled?: boolean;
11
+ onValueChange?: (value: string | string[]) => void;
12
+ /** Whether the calendar icon has full bg*/
13
+ iconBGFull?: boolean;
14
+ /** Position of the calendar icon: left or right */
15
+ iconPosition?: "left" | "right";
16
+ /** Show chevron on the right when icon is on the left */
17
+ showChevron?: boolean;
18
+ }
19
+
20
+ const formatDateRange = (dateRange: any) => {
21
+ if (!dateRange || !dateRange.from) return "";
22
+ const from = dateRange.from;
23
+ const to = dateRange.to ?? dateRange.from;
24
+
25
+ if (+from === +to) return format(from, "dd/MM/yyyy");
26
+ return `${format(from, "dd/MM/yyyy")} - ${format(to, "dd/MM/yyyy")}`;
27
+ };
28
+
29
+ const DateTimePicker: React.FC<DateTimePickerProps> = ({
30
+ placeholder = "Select date",
31
+ disabled = false,
32
+ mode = "both",
33
+ selectionMode = "range",
34
+ numberOfMonths = 2,
35
+ disableBeforeToday = true,
36
+ disableToday = false,
37
+ onValueChange,
38
+ iconBGFull = true,
39
+ iconPosition = "right",
40
+ showChevron = false,
41
+ }) => {
42
+ const [value, setValue] = useState<string>("");
43
+ const [selectedDateRange, setSelectedDateRange] = useState<any>(undefined);
44
+ const [selectedTime, setSelectedTime] = useState<{ hour: string; minute: string; meridiem: "AM" | "PM" } | undefined>(undefined);
45
+ const [isOpen, setIsOpen] = useState(false);
46
+
47
+ const handleDtChange = React.useCallback((payload: { dateRange?: any; time?: { hour: string; minute: string; meridiem: "AM" | "PM" } }) => {
48
+ const datePart = formatDateRange(payload.dateRange);
49
+ const time = payload.time ? `${payload.time.hour}:${payload.time.minute} ${payload.time.meridiem}` : "";
50
+ const combined = [datePart, time].filter(Boolean).join(" ");
51
+ setValue(combined);
52
+ setSelectedDateRange(payload.dateRange);
53
+ setSelectedTime(payload.time);
54
+
55
+ // Return date strings in yyyy-MM-dd format for both modes
56
+ if (payload.dateRange) {
57
+ if (selectionMode === "range") {
58
+ // Return array of date strings for range mode
59
+ const from = payload.dateRange.from;
60
+ const to = payload.dateRange.to ?? payload.dateRange.from;
61
+
62
+ if (from && to) {
63
+ const fromStr = format(from, "yyyy-MM-dd");
64
+ const toStr = format(to, "yyyy-MM-dd");
65
+ onValueChange?.([fromStr, toStr]);
66
+ }
67
+ } else {
68
+ // Return single date string in yyyy-MM-dd format for single mode
69
+ const date = payload.dateRange.from;
70
+ if (date) {
71
+ const dateStr = format(date, "yyyy-MM-dd");
72
+ onValueChange?.(dateStr);
73
+ }
74
+ }
75
+ }
76
+ }, [onValueChange, selectionMode]);
77
+
78
+ return (
79
+ <Popover open={isOpen} onOpenChange={setIsOpen}>
80
+ <PopoverTrigger asChild>
81
+ <div>
82
+ <CalendarInput
83
+ placeholder={placeholder}
84
+ value={value}
85
+ disabled={disabled}
86
+ iconBGFull={iconBGFull}
87
+ iconPosition={iconPosition}
88
+ showChevron={showChevron}
89
+ isOpen={isOpen}
90
+ />
91
+ </div>
92
+ </PopoverTrigger>
93
+
94
+ <PopoverContent className="w-auto p-0 border-transparent">
95
+ <DateTime
96
+ mode={mode}
97
+ selectionMode={selectionMode}
98
+ numberOfMonths={numberOfMonths as 1 | 2}
99
+ disableBeforeToday={disableBeforeToday}
100
+ disableToday={disableToday}
101
+ onChange={handleDtChange}
102
+ defaultDateRange={selectedDateRange}
103
+ defaultTime={selectedTime}
104
+ />
105
+ </PopoverContent>
106
+ </Popover>
107
+ );
108
+ };
109
+
110
+ export default DateTimePicker;