infinity-ui-elements 1.8.32 → 1.8.33
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/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/DatePicker/DatePicker.stories.d.ts +1 -0
- package/dist/components/DatePicker/DatePicker.stories.d.ts.map +1 -1
- package/dist/components/Radio/RadioGroup.stories.d.ts +1 -0
- package/dist/components/Radio/RadioGroup.stories.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.js +165 -12
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +165 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -6,9 +6,9 @@ import { PulseLoader, ClipLoader } from 'react-spinners';
|
|
|
6
6
|
import { clsx } from 'clsx';
|
|
7
7
|
import { twMerge } from 'tailwind-merge';
|
|
8
8
|
import { ExternalLink, Calendar, X, Loader2, Search, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
9
|
+
import { createPortal } from 'react-dom';
|
|
9
10
|
import Calendar$1 from 'react-calendar';
|
|
10
11
|
import 'react-calendar/dist/Calendar.css';
|
|
11
|
-
import { createPortal } from 'react-dom';
|
|
12
12
|
import { flexRender } from '@tanstack/react-table';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -2077,16 +2077,64 @@ const formatDateDefault = (date) => {
|
|
|
2077
2077
|
day: "numeric",
|
|
2078
2078
|
});
|
|
2079
2079
|
};
|
|
2080
|
-
|
|
2080
|
+
// Helper function to format date based on format string
|
|
2081
|
+
const formatDateByPattern = (date, format) => {
|
|
2082
|
+
const day = date.getDate();
|
|
2083
|
+
const month = date.getMonth() + 1; // getMonth() returns 0-11
|
|
2084
|
+
const year = date.getFullYear();
|
|
2085
|
+
const monthNames = [
|
|
2086
|
+
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
2087
|
+
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
2088
|
+
];
|
|
2089
|
+
const monthNamesFull = [
|
|
2090
|
+
"January", "February", "March", "April", "May", "June",
|
|
2091
|
+
"July", "August", "September", "October", "November", "December"
|
|
2092
|
+
];
|
|
2093
|
+
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
2094
|
+
const dayNamesFull = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
2095
|
+
// Pad numbers with leading zeros
|
|
2096
|
+
const pad = (n, length = 2) => {
|
|
2097
|
+
return n.toString().padStart(length, "0");
|
|
2098
|
+
};
|
|
2099
|
+
let formatted = format;
|
|
2100
|
+
// Replace format patterns
|
|
2101
|
+
formatted = formatted.replace(/YYYY/g, year.toString());
|
|
2102
|
+
formatted = formatted.replace(/YY/g, year.toString().slice(-2));
|
|
2103
|
+
formatted = formatted.replace(/MMMM/g, monthNamesFull[month - 1]);
|
|
2104
|
+
formatted = formatted.replace(/MMM/g, monthNames[month - 1]);
|
|
2105
|
+
formatted = formatted.replace(/MM/g, pad(month));
|
|
2106
|
+
formatted = formatted.replace(/M/g, month.toString());
|
|
2107
|
+
formatted = formatted.replace(/DDDD/g, dayNamesFull[date.getDay()]);
|
|
2108
|
+
formatted = formatted.replace(/DDD/g, dayNames[date.getDay()]);
|
|
2109
|
+
formatted = formatted.replace(/DD/g, pad(day));
|
|
2110
|
+
formatted = formatted.replace(/D/g, day.toString());
|
|
2111
|
+
return formatted;
|
|
2112
|
+
};
|
|
2113
|
+
const DatePicker = React.forwardRef(({ className, value: controlledValue, defaultValue, onChange, placeholder = "Select a date", label, helperText, errorText, successText, validationState = "none", isDisabled = false, isRequired = false, isOptional = false, size = "medium", showClearButton = true, onClear, containerClassName, labelClassName, triggerClassName, calendarClassName, minDate, maxDate, formatDate = formatDateDefault, format, infoHeading, infoDescription, LinkComponent, linkText, linkHref, onLinkClick, ...props }, ref) => {
|
|
2081
2114
|
const [uncontrolledValue, setUncontrolledValue] = React.useState(parseDate(defaultValue));
|
|
2082
2115
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
2083
2116
|
const datePickerRef = React.useRef(null);
|
|
2084
2117
|
const calendarRef = React.useRef(null);
|
|
2085
2118
|
const [dropdownPlacement, setDropdownPlacement] = React.useState("bottom");
|
|
2119
|
+
const [isInsideModal, setIsInsideModal] = React.useState(false);
|
|
2120
|
+
const [position, setPosition] = React.useState({
|
|
2121
|
+
top: 0,
|
|
2122
|
+
left: 0,
|
|
2123
|
+
width: 0,
|
|
2124
|
+
bottom: 0,
|
|
2125
|
+
});
|
|
2126
|
+
const [calendarHeight, setCalendarHeight] = React.useState(300); // Default height estimate
|
|
2086
2127
|
const value = controlledValue !== undefined
|
|
2087
2128
|
? parseDate(controlledValue)
|
|
2088
2129
|
: uncontrolledValue;
|
|
2089
2130
|
const hasValue = value !== null;
|
|
2131
|
+
// Create a formatter function that uses format prop if provided, otherwise formatDate
|
|
2132
|
+
const formatDateValue = React.useCallback((date) => {
|
|
2133
|
+
if (format) {
|
|
2134
|
+
return formatDateByPattern(date, format);
|
|
2135
|
+
}
|
|
2136
|
+
return formatDate(date);
|
|
2137
|
+
}, [format, formatDate]);
|
|
2090
2138
|
// Determine which helper text to show
|
|
2091
2139
|
const displayHelperText = errorText || successText || helperText;
|
|
2092
2140
|
const currentValidationState = errorText
|
|
@@ -2158,13 +2206,74 @@ const DatePicker = React.forwardRef(({ className, value: controlledValue, defaul
|
|
|
2158
2206
|
setDropdownPlacement("top");
|
|
2159
2207
|
}
|
|
2160
2208
|
}, []);
|
|
2209
|
+
// Check if date picker is inside a modal
|
|
2210
|
+
React.useEffect(() => {
|
|
2211
|
+
if (isOpen && datePickerRef.current) {
|
|
2212
|
+
let element = datePickerRef.current;
|
|
2213
|
+
let foundModal = false;
|
|
2214
|
+
while (element && !foundModal) {
|
|
2215
|
+
const styles = window.getComputedStyle(element);
|
|
2216
|
+
const zIndex = parseInt(styles.zIndex, 10);
|
|
2217
|
+
// Check if element has modal z-index (10000) or is a modal container
|
|
2218
|
+
if (zIndex === 10000 || element.getAttribute("role") === "dialog") {
|
|
2219
|
+
foundModal = true;
|
|
2220
|
+
setIsInsideModal(true);
|
|
2221
|
+
break;
|
|
2222
|
+
}
|
|
2223
|
+
element = element.parentElement;
|
|
2224
|
+
}
|
|
2225
|
+
if (!foundModal) {
|
|
2226
|
+
setIsInsideModal(false);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
}, [isOpen]);
|
|
2230
|
+
// Update position when calendar opens or window resizes
|
|
2231
|
+
React.useEffect(() => {
|
|
2232
|
+
if (isOpen && datePickerRef.current) {
|
|
2233
|
+
const updatePosition = () => {
|
|
2234
|
+
const rect = datePickerRef.current?.getBoundingClientRect();
|
|
2235
|
+
if (rect) {
|
|
2236
|
+
setPosition({
|
|
2237
|
+
top: rect.top,
|
|
2238
|
+
left: rect.left,
|
|
2239
|
+
width: rect.width,
|
|
2240
|
+
bottom: rect.bottom,
|
|
2241
|
+
});
|
|
2242
|
+
// Update dropdown placement based on available space
|
|
2243
|
+
updateDropdownPlacement();
|
|
2244
|
+
}
|
|
2245
|
+
};
|
|
2246
|
+
updatePosition();
|
|
2247
|
+
window.addEventListener("resize", updatePosition);
|
|
2248
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
2249
|
+
return () => {
|
|
2250
|
+
window.removeEventListener("resize", updatePosition);
|
|
2251
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
}, [isOpen, updateDropdownPlacement]);
|
|
2161
2255
|
React.useEffect(() => {
|
|
2162
2256
|
if (!isOpen)
|
|
2163
2257
|
return;
|
|
2164
2258
|
if (typeof window === "undefined")
|
|
2165
2259
|
return;
|
|
2166
|
-
|
|
2167
|
-
|
|
2260
|
+
// Use requestAnimationFrame to ensure calendar is rendered before calculating placement
|
|
2261
|
+
let rafId = requestAnimationFrame(() => {
|
|
2262
|
+
updateDropdownPlacement();
|
|
2263
|
+
});
|
|
2264
|
+
const handleUpdate = () => {
|
|
2265
|
+
updateDropdownPlacement();
|
|
2266
|
+
// Also update position when scrolling/resizing
|
|
2267
|
+
if (datePickerRef.current) {
|
|
2268
|
+
const rect = datePickerRef.current.getBoundingClientRect();
|
|
2269
|
+
setPosition({
|
|
2270
|
+
top: rect.top,
|
|
2271
|
+
left: rect.left,
|
|
2272
|
+
width: rect.width,
|
|
2273
|
+
bottom: rect.bottom,
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2168
2277
|
window.addEventListener("resize", handleUpdate);
|
|
2169
2278
|
window.addEventListener("scroll", handleUpdate, true);
|
|
2170
2279
|
return () => {
|
|
@@ -2175,14 +2284,44 @@ const DatePicker = React.forwardRef(({ className, value: controlledValue, defaul
|
|
|
2175
2284
|
}, [isOpen, updateDropdownPlacement]);
|
|
2176
2285
|
React.useEffect(() => {
|
|
2177
2286
|
if (isOpen) {
|
|
2178
|
-
|
|
2287
|
+
// Delay to ensure calendar is rendered
|
|
2288
|
+
const timer = setTimeout(() => {
|
|
2289
|
+
updateDropdownPlacement();
|
|
2290
|
+
}, 0);
|
|
2291
|
+
return () => clearTimeout(timer);
|
|
2179
2292
|
}
|
|
2180
2293
|
}, [isOpen, updateDropdownPlacement]);
|
|
2294
|
+
// Measure calendar height and adjust position after render
|
|
2295
|
+
React.useLayoutEffect(() => {
|
|
2296
|
+
if (isOpen && calendarRef.current && datePickerRef.current) {
|
|
2297
|
+
const measuredHeight = calendarRef.current.offsetHeight;
|
|
2298
|
+
setCalendarHeight(measuredHeight);
|
|
2299
|
+
const rect = datePickerRef.current.getBoundingClientRect();
|
|
2300
|
+
// Recalculate placement if needed based on actual calendar height
|
|
2301
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
2302
|
+
const spaceAbove = rect.top;
|
|
2303
|
+
if (measuredHeight > spaceBelow && spaceAbove > spaceBelow) {
|
|
2304
|
+
setDropdownPlacement("top");
|
|
2305
|
+
}
|
|
2306
|
+
else {
|
|
2307
|
+
setDropdownPlacement("bottom");
|
|
2308
|
+
}
|
|
2309
|
+
// Update position
|
|
2310
|
+
setPosition({
|
|
2311
|
+
top: rect.top,
|
|
2312
|
+
left: rect.left,
|
|
2313
|
+
width: rect.width,
|
|
2314
|
+
bottom: rect.bottom,
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
}, [isOpen]);
|
|
2181
2318
|
// Close calendar when clicking outside
|
|
2182
2319
|
React.useEffect(() => {
|
|
2183
2320
|
const handleClickOutside = (event) => {
|
|
2184
2321
|
if (datePickerRef.current &&
|
|
2185
|
-
!datePickerRef.current.contains(event.target)
|
|
2322
|
+
!datePickerRef.current.contains(event.target) &&
|
|
2323
|
+
calendarRef.current &&
|
|
2324
|
+
!calendarRef.current.contains(event.target)) {
|
|
2186
2325
|
handleOpenChange(false);
|
|
2187
2326
|
}
|
|
2188
2327
|
};
|
|
@@ -2230,9 +2369,23 @@ const DatePicker = React.forwardRef(({ className, value: controlledValue, defaul
|
|
|
2230
2369
|
? "text-feedback-ink-positive-intense"
|
|
2231
2370
|
: currentValidationState === "negative"
|
|
2232
2371
|
? "text-feedback-ink-negative-subtle"
|
|
2233
|
-
: "text-surface-ink-neutral-muted") }), jsx("span", { className: cn("flex-1 text-left truncate", !hasValue && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: hasValue && value ?
|
|
2234
|
-
|
|
2235
|
-
|
|
2372
|
+
: "text-surface-ink-neutral-muted") }), jsx("span", { className: cn("flex-1 text-left truncate", !hasValue && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: hasValue && value ? formatDateValue(value) : placeholder }), showClearButton && hasValue && !isDisabled && (jsx("button", { type: "button", onClick: handleClear, className: "shrink-0 flex items-center justify-center text-surface-ink-neutral-muted hover:text-surface-ink-neutral-normal transition-colors", tabIndex: -1, "aria-label": "Clear date", children: jsx(X, { className: "w-4 h-4" }) }))] }), jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
|
|
2373
|
+
? "default"
|
|
2374
|
+
: currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" }), typeof document !== "undefined" &&
|
|
2375
|
+
isOpen &&
|
|
2376
|
+
!isDisabled &&
|
|
2377
|
+
(() => {
|
|
2378
|
+
// Calculate calendar position using fixed positioning (viewport-relative)
|
|
2379
|
+
const gap = 4; // 4px gap between trigger and calendar
|
|
2380
|
+
const calendarTop = dropdownPlacement === "bottom"
|
|
2381
|
+
? position.bottom + gap
|
|
2382
|
+
: position.top - calendarHeight - gap;
|
|
2383
|
+
const calendarPopup = (jsx("div", { ref: calendarRef, style: {
|
|
2384
|
+
position: "fixed",
|
|
2385
|
+
top: `${calendarTop}px`,
|
|
2386
|
+
left: `${position.left}px`,
|
|
2387
|
+
zIndex: isInsideModal ? 10001 : 9999,
|
|
2388
|
+
}, className: cn("bg-surface-fill-neutral-intense rounded-large shadow-lg p-4 w-fit", calendarClassName), onClick: (e) => e.stopPropagation(), children: jsx("div", { className: "react-calendar-wrapper w-fit", children: jsx(Calendar$1, { onChange: handleCalendarChange, value: value ?? null, minDate: minDateParsed ?? undefined, maxDate: maxDateParsed ?? undefined, locale: "en-US", formatShortWeekday: (locale, date) => {
|
|
2236
2389
|
const weekdayNames = [
|
|
2237
2390
|
"Su",
|
|
2238
2391
|
"Mo",
|
|
@@ -2243,9 +2396,9 @@ const DatePicker = React.forwardRef(({ className, value: controlledValue, defaul
|
|
|
2243
2396
|
"Sa",
|
|
2244
2397
|
];
|
|
2245
2398
|
return weekdayNames[date.getDay()];
|
|
2246
|
-
} }) }) }))
|
|
2247
|
-
|
|
2248
|
-
|
|
2399
|
+
} }) }) }));
|
|
2400
|
+
return createPortal(calendarPopup, document.body);
|
|
2401
|
+
})()] }));
|
|
2249
2402
|
});
|
|
2250
2403
|
DatePicker.displayName = "DatePicker";
|
|
2251
2404
|
|