infinity-ui-elements 1.8.30 → 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/index.js CHANGED
@@ -8,9 +8,9 @@ var reactSpinners = require('react-spinners');
8
8
  var clsx = require('clsx');
9
9
  var tailwindMerge = require('tailwind-merge');
10
10
  var lucideReact = require('lucide-react');
11
+ var reactDom = require('react-dom');
11
12
  var Calendar = require('react-calendar');
12
13
  require('react-calendar/dist/Calendar.css');
13
- var reactDom = require('react-dom');
14
14
  var reactTable = require('@tanstack/react-table');
15
15
 
16
16
  function _interopNamespaceDefault(e) {
@@ -2098,16 +2098,64 @@ const formatDateDefault = (date) => {
2098
2098
  day: "numeric",
2099
2099
  });
2100
2100
  };
2101
- const DatePicker = React__namespace.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, infoHeading, infoDescription, LinkComponent, linkText, linkHref, onLinkClick, ...props }, ref) => {
2101
+ // Helper function to format date based on format string
2102
+ const formatDateByPattern = (date, format) => {
2103
+ const day = date.getDate();
2104
+ const month = date.getMonth() + 1; // getMonth() returns 0-11
2105
+ const year = date.getFullYear();
2106
+ const monthNames = [
2107
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
2108
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2109
+ ];
2110
+ const monthNamesFull = [
2111
+ "January", "February", "March", "April", "May", "June",
2112
+ "July", "August", "September", "October", "November", "December"
2113
+ ];
2114
+ const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2115
+ const dayNamesFull = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
2116
+ // Pad numbers with leading zeros
2117
+ const pad = (n, length = 2) => {
2118
+ return n.toString().padStart(length, "0");
2119
+ };
2120
+ let formatted = format;
2121
+ // Replace format patterns
2122
+ formatted = formatted.replace(/YYYY/g, year.toString());
2123
+ formatted = formatted.replace(/YY/g, year.toString().slice(-2));
2124
+ formatted = formatted.replace(/MMMM/g, monthNamesFull[month - 1]);
2125
+ formatted = formatted.replace(/MMM/g, monthNames[month - 1]);
2126
+ formatted = formatted.replace(/MM/g, pad(month));
2127
+ formatted = formatted.replace(/M/g, month.toString());
2128
+ formatted = formatted.replace(/DDDD/g, dayNamesFull[date.getDay()]);
2129
+ formatted = formatted.replace(/DDD/g, dayNames[date.getDay()]);
2130
+ formatted = formatted.replace(/DD/g, pad(day));
2131
+ formatted = formatted.replace(/D/g, day.toString());
2132
+ return formatted;
2133
+ };
2134
+ const DatePicker = React__namespace.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) => {
2102
2135
  const [uncontrolledValue, setUncontrolledValue] = React__namespace.useState(parseDate(defaultValue));
2103
2136
  const [isOpen, setIsOpen] = React__namespace.useState(false);
2104
2137
  const datePickerRef = React__namespace.useRef(null);
2105
2138
  const calendarRef = React__namespace.useRef(null);
2106
2139
  const [dropdownPlacement, setDropdownPlacement] = React__namespace.useState("bottom");
2140
+ const [isInsideModal, setIsInsideModal] = React__namespace.useState(false);
2141
+ const [position, setPosition] = React__namespace.useState({
2142
+ top: 0,
2143
+ left: 0,
2144
+ width: 0,
2145
+ bottom: 0,
2146
+ });
2147
+ const [calendarHeight, setCalendarHeight] = React__namespace.useState(300); // Default height estimate
2107
2148
  const value = controlledValue !== undefined
2108
2149
  ? parseDate(controlledValue)
2109
2150
  : uncontrolledValue;
2110
2151
  const hasValue = value !== null;
2152
+ // Create a formatter function that uses format prop if provided, otherwise formatDate
2153
+ const formatDateValue = React__namespace.useCallback((date) => {
2154
+ if (format) {
2155
+ return formatDateByPattern(date, format);
2156
+ }
2157
+ return formatDate(date);
2158
+ }, [format, formatDate]);
2111
2159
  // Determine which helper text to show
2112
2160
  const displayHelperText = errorText || successText || helperText;
2113
2161
  const currentValidationState = errorText
@@ -2179,13 +2227,74 @@ const DatePicker = React__namespace.forwardRef(({ className, value: controlledVa
2179
2227
  setDropdownPlacement("top");
2180
2228
  }
2181
2229
  }, []);
2230
+ // Check if date picker is inside a modal
2231
+ React__namespace.useEffect(() => {
2232
+ if (isOpen && datePickerRef.current) {
2233
+ let element = datePickerRef.current;
2234
+ let foundModal = false;
2235
+ while (element && !foundModal) {
2236
+ const styles = window.getComputedStyle(element);
2237
+ const zIndex = parseInt(styles.zIndex, 10);
2238
+ // Check if element has modal z-index (10000) or is a modal container
2239
+ if (zIndex === 10000 || element.getAttribute("role") === "dialog") {
2240
+ foundModal = true;
2241
+ setIsInsideModal(true);
2242
+ break;
2243
+ }
2244
+ element = element.parentElement;
2245
+ }
2246
+ if (!foundModal) {
2247
+ setIsInsideModal(false);
2248
+ }
2249
+ }
2250
+ }, [isOpen]);
2251
+ // Update position when calendar opens or window resizes
2252
+ React__namespace.useEffect(() => {
2253
+ if (isOpen && datePickerRef.current) {
2254
+ const updatePosition = () => {
2255
+ const rect = datePickerRef.current?.getBoundingClientRect();
2256
+ if (rect) {
2257
+ setPosition({
2258
+ top: rect.top,
2259
+ left: rect.left,
2260
+ width: rect.width,
2261
+ bottom: rect.bottom,
2262
+ });
2263
+ // Update dropdown placement based on available space
2264
+ updateDropdownPlacement();
2265
+ }
2266
+ };
2267
+ updatePosition();
2268
+ window.addEventListener("resize", updatePosition);
2269
+ window.addEventListener("scroll", updatePosition, true);
2270
+ return () => {
2271
+ window.removeEventListener("resize", updatePosition);
2272
+ window.removeEventListener("scroll", updatePosition, true);
2273
+ };
2274
+ }
2275
+ }, [isOpen, updateDropdownPlacement]);
2182
2276
  React__namespace.useEffect(() => {
2183
2277
  if (!isOpen)
2184
2278
  return;
2185
2279
  if (typeof window === "undefined")
2186
2280
  return;
2187
- let rafId = requestAnimationFrame(updateDropdownPlacement);
2188
- const handleUpdate = () => updateDropdownPlacement();
2281
+ // Use requestAnimationFrame to ensure calendar is rendered before calculating placement
2282
+ let rafId = requestAnimationFrame(() => {
2283
+ updateDropdownPlacement();
2284
+ });
2285
+ const handleUpdate = () => {
2286
+ updateDropdownPlacement();
2287
+ // Also update position when scrolling/resizing
2288
+ if (datePickerRef.current) {
2289
+ const rect = datePickerRef.current.getBoundingClientRect();
2290
+ setPosition({
2291
+ top: rect.top,
2292
+ left: rect.left,
2293
+ width: rect.width,
2294
+ bottom: rect.bottom,
2295
+ });
2296
+ }
2297
+ };
2189
2298
  window.addEventListener("resize", handleUpdate);
2190
2299
  window.addEventListener("scroll", handleUpdate, true);
2191
2300
  return () => {
@@ -2196,14 +2305,44 @@ const DatePicker = React__namespace.forwardRef(({ className, value: controlledVa
2196
2305
  }, [isOpen, updateDropdownPlacement]);
2197
2306
  React__namespace.useEffect(() => {
2198
2307
  if (isOpen) {
2199
- updateDropdownPlacement();
2308
+ // Delay to ensure calendar is rendered
2309
+ const timer = setTimeout(() => {
2310
+ updateDropdownPlacement();
2311
+ }, 0);
2312
+ return () => clearTimeout(timer);
2200
2313
  }
2201
2314
  }, [isOpen, updateDropdownPlacement]);
2315
+ // Measure calendar height and adjust position after render
2316
+ React__namespace.useLayoutEffect(() => {
2317
+ if (isOpen && calendarRef.current && datePickerRef.current) {
2318
+ const measuredHeight = calendarRef.current.offsetHeight;
2319
+ setCalendarHeight(measuredHeight);
2320
+ const rect = datePickerRef.current.getBoundingClientRect();
2321
+ // Recalculate placement if needed based on actual calendar height
2322
+ const spaceBelow = window.innerHeight - rect.bottom;
2323
+ const spaceAbove = rect.top;
2324
+ if (measuredHeight > spaceBelow && spaceAbove > spaceBelow) {
2325
+ setDropdownPlacement("top");
2326
+ }
2327
+ else {
2328
+ setDropdownPlacement("bottom");
2329
+ }
2330
+ // Update position
2331
+ setPosition({
2332
+ top: rect.top,
2333
+ left: rect.left,
2334
+ width: rect.width,
2335
+ bottom: rect.bottom,
2336
+ });
2337
+ }
2338
+ }, [isOpen]);
2202
2339
  // Close calendar when clicking outside
2203
2340
  React__namespace.useEffect(() => {
2204
2341
  const handleClickOutside = (event) => {
2205
2342
  if (datePickerRef.current &&
2206
- !datePickerRef.current.contains(event.target)) {
2343
+ !datePickerRef.current.contains(event.target) &&
2344
+ calendarRef.current &&
2345
+ !calendarRef.current.contains(event.target)) {
2207
2346
  handleOpenChange(false);
2208
2347
  }
2209
2348
  };
@@ -2251,9 +2390,23 @@ const DatePicker = React__namespace.forwardRef(({ className, value: controlledVa
2251
2390
  ? "text-feedback-ink-positive-intense"
2252
2391
  : currentValidationState === "negative"
2253
2392
  ? "text-feedback-ink-negative-subtle"
2254
- : "text-surface-ink-neutral-muted") }), jsxRuntime.jsx("span", { className: cn("flex-1 text-left truncate", !hasValue && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: hasValue && value ? formatDate(value) : placeholder }), showClearButton && hasValue && !isDisabled && (jsxRuntime.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: jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" }) })), isOpen && !isDisabled && (jsxRuntime.jsx("div", { ref: calendarRef, className: cn("absolute z-50 left-0 bg-surface-fill-neutral-intense rounded-large shadow-lg p-4", dropdownPlacement === "bottom"
2255
- ? "top-full mt-1"
2256
- : "bottom-full mb-1", calendarClassName), onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: "react-calendar-wrapper w-fit", children: jsxRuntime.jsx(Calendar, { onChange: handleCalendarChange, value: value ?? null, minDate: minDateParsed ?? undefined, maxDate: maxDateParsed ?? undefined, locale: "en-US", formatShortWeekday: (locale, date) => {
2393
+ : "text-surface-ink-neutral-muted") }), jsxRuntime.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 && (jsxRuntime.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: jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" }) }))] }), jsxRuntime.jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
2394
+ ? "default"
2395
+ : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" }), typeof document !== "undefined" &&
2396
+ isOpen &&
2397
+ !isDisabled &&
2398
+ (() => {
2399
+ // Calculate calendar position using fixed positioning (viewport-relative)
2400
+ const gap = 4; // 4px gap between trigger and calendar
2401
+ const calendarTop = dropdownPlacement === "bottom"
2402
+ ? position.bottom + gap
2403
+ : position.top - calendarHeight - gap;
2404
+ const calendarPopup = (jsxRuntime.jsx("div", { ref: calendarRef, style: {
2405
+ position: "fixed",
2406
+ top: `${calendarTop}px`,
2407
+ left: `${position.left}px`,
2408
+ zIndex: isInsideModal ? 10001 : 9999,
2409
+ }, className: cn("bg-surface-fill-neutral-intense rounded-large shadow-lg p-4 w-fit", calendarClassName), onClick: (e) => e.stopPropagation(), children: jsxRuntime.jsx("div", { className: "react-calendar-wrapper w-fit", children: jsxRuntime.jsx(Calendar, { onChange: handleCalendarChange, value: value ?? null, minDate: minDateParsed ?? undefined, maxDate: maxDateParsed ?? undefined, locale: "en-US", formatShortWeekday: (locale, date) => {
2257
2410
  const weekdayNames = [
2258
2411
  "Su",
2259
2412
  "Mo",
@@ -2264,9 +2417,9 @@ const DatePicker = React__namespace.forwardRef(({ className, value: controlledVa
2264
2417
  "Sa",
2265
2418
  ];
2266
2419
  return weekdayNames[date.getDay()];
2267
- } }) }) }))] }), jsxRuntime.jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
2268
- ? "default"
2269
- : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
2420
+ } }) }) }));
2421
+ return reactDom.createPortal(calendarPopup, document.body);
2422
+ })()] }));
2270
2423
  });
2271
2424
  DatePicker.displayName = "DatePicker";
2272
2425
 
@@ -3145,8 +3298,16 @@ const RadioGroup = React__namespace.forwardRef(({ value: controlledValue, defaul
3145
3298
  if (currentIndex === -1)
3146
3299
  return;
3147
3300
  const direction = orientation === "horizontal"
3148
- ? (e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0)
3149
- : (e.key === "ArrowDown" ? 1 : e.key === "ArrowUp" ? -1 : 0);
3301
+ ? e.key === "ArrowRight"
3302
+ ? 1
3303
+ : e.key === "ArrowLeft"
3304
+ ? -1
3305
+ : 0
3306
+ : e.key === "ArrowDown"
3307
+ ? 1
3308
+ : e.key === "ArrowUp"
3309
+ ? -1
3310
+ : 0;
3150
3311
  if (direction === 0)
3151
3312
  return;
3152
3313
  e.preventDefault();
@@ -3214,20 +3375,14 @@ const RadioGroup = React__namespace.forwardRef(({ value: controlledValue, defaul
3214
3375
  ...child.props,
3215
3376
  ref: (el) => {
3216
3377
  radioRefs.current[index] = el;
3217
- // Preserve original ref if it exists
3218
- const originalRef = child.ref;
3219
- if (typeof originalRef === "function") {
3220
- originalRef(el);
3221
- }
3222
- else if (originalRef && "current" in originalRef) {
3223
- originalRef.current = el;
3224
- }
3225
3378
  },
3226
3379
  name,
3227
3380
  checked: isChecked,
3228
3381
  onChange: (e) => {
3229
3382
  if (e.target.checked) {
3230
- handleChange(childValue || "");
3383
+ handleChange(typeof childValue === "string"
3384
+ ? childValue
3385
+ : String(childValue || ""));
3231
3386
  }
3232
3387
  // Call original onChange if it exists
3233
3388
  if (child.props.onChange) {
@@ -3241,7 +3396,13 @@ const RadioGroup = React__namespace.forwardRef(({ value: controlledValue, defaul
3241
3396
  });
3242
3397
  });
3243
3398
  };
3244
- return (jsxRuntime.jsxs("div", { ref: ref, className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), ...props, children: [label && (jsxRuntime.jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, LinkComponent: LinkComponent, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: id, className: "mb-2", labelClassName: labelClassName })), jsxRuntime.jsx("div", { ref: groupRef, role: "radiogroup", "aria-label": label, "aria-required": isRequired, "aria-invalid": currentValidationState === "error", "aria-disabled": isDisabled, className: cn("flex", orientation === "horizontal" ? "flex-row items-center" : "flex-col items-start", spacingConfig[spacing], groupClassName, className), onKeyDown: handleKeyDown, id: id, children: options ? renderOptions() : renderChildren() }), jsxRuntime.jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none" ? "default" : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
3399
+ return (jsxRuntime.jsxs("div", { ref: ref, className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), ...props, children: [label && (jsxRuntime.jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, LinkComponent: LinkComponent, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: id, className: "mb-2", labelClassName: labelClassName })), jsxRuntime.jsx("div", { ref: groupRef, role: "radiogroup", "aria-label": label, "aria-required": isRequired, "aria-invalid": currentValidationState === "error", "aria-disabled": isDisabled, className: cn("flex", orientation === "horizontal"
3400
+ ? "flex-row items-center"
3401
+ : "flex-col items-start", spacingConfig[spacing], groupClassName, className), onKeyDown: handleKeyDown, id: id, children: options ? renderOptions() : renderChildren() }), jsxRuntime.jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
3402
+ ? "default"
3403
+ : currentValidationState === "error"
3404
+ ? "negative"
3405
+ : "default", size: size, isDisabled: isDisabled, className: "mt-1" })] }));
3245
3406
  });
3246
3407
  RadioGroup.displayName = "RadioGroup";
3247
3408