@lumx/react 3.6.6-alpha.2 → 3.6.7-alpha.0

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/index.d.ts CHANGED
@@ -586,6 +586,8 @@ interface DatePickerControlledProps extends DatePickerProps {
586
586
  onPrevMonthChange(): void;
587
587
  /** On next month change callback. */
588
588
  onNextMonthChange(): void;
589
+ /** On month/year change callback. */
590
+ onMonthChange?: (newMonth: Date) => void;
589
591
  }
590
592
  /**
591
593
  * DatePickerControlled component.
@@ -792,6 +794,8 @@ interface PopoverProps extends GenericProps, HasTheme {
792
794
  placement?: Placement;
793
795
  /** Whether the popover should be rendered into a DOM node that exists outside the DOM hierarchy of the parent component. */
794
796
  usePortal?: boolean;
797
+ /** The element in which the focus trap should be set. Default to popover */
798
+ focusTrapZone?: RefObject<HTMLElement>;
795
799
  /** Z-axis position. */
796
800
  zIndex?: number;
797
801
  /** On close callback (on click away or Escape pressed). */
@@ -1646,6 +1650,17 @@ interface MessageProps extends GenericProps {
1646
1650
  kind?: Kind;
1647
1651
  /** Message custom icon SVG path. */
1648
1652
  icon?: string;
1653
+ /**
1654
+ * Displays a close button.
1655
+ *
1656
+ * NB: only available if `kind === 'info' && hasBackground === true`
1657
+ */
1658
+ closeButtonProps?: {
1659
+ /** The callback called when the button is clicked */
1660
+ onClick: () => void;
1661
+ /** The label of the close button. */
1662
+ label: string;
1663
+ };
1649
1664
  }
1650
1665
  /**
1651
1666
  * Message component.
package/index.js CHANGED
@@ -2262,6 +2262,21 @@ function usePreviousValue(value) {
2262
2262
  return prevValue;
2263
2263
  }
2264
2264
 
2265
+ const getYearDisplayName = locale => {
2266
+ let label;
2267
+ try {
2268
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2269
+ // @ts-ignore
2270
+ const displayNames = new Intl.DisplayNames(locale, {
2271
+ type: 'dateTimeField'
2272
+ });
2273
+ label = displayNames.of('year');
2274
+ } catch (error) {
2275
+ label = '';
2276
+ }
2277
+ return label;
2278
+ };
2279
+
2265
2280
  /**
2266
2281
  * Defines the props of the component.
2267
2282
  */
@@ -2279,6 +2294,7 @@ const COMPONENT_NAME$e = 'DatePickerControlled';
2279
2294
  * @return React element.
2280
2295
  */
2281
2296
  const DatePickerControlled = /*#__PURE__*/forwardRef((props, ref) => {
2297
+ var _RegExp$exec;
2282
2298
  const {
2283
2299
  locale = getCurrentLocale(),
2284
2300
  maxDate,
@@ -2290,7 +2306,8 @@ const DatePickerControlled = /*#__PURE__*/forwardRef((props, ref) => {
2290
2306
  previousButtonProps,
2291
2307
  selectedMonth,
2292
2308
  todayOrSelectedDateRef,
2293
- value
2309
+ value,
2310
+ onMonthChange
2294
2311
  } = props;
2295
2312
  const {
2296
2313
  weeks,
@@ -2299,6 +2316,37 @@ const DatePickerControlled = /*#__PURE__*/forwardRef((props, ref) => {
2299
2316
  const localeObj = parseLocale(locale);
2300
2317
  return getMonthCalendar(localeObj, selectedMonth, minDate, maxDate);
2301
2318
  }, [locale, minDate, maxDate, selectedMonth]);
2319
+ const selectedYear = selectedMonth.toLocaleDateString(locale, {
2320
+ year: 'numeric'
2321
+ }).slice(0, 4);
2322
+ const [textFieldYearValue, setTextFieldYearValue] = React.useState(selectedYear);
2323
+ const isYearValid = Number(textFieldYearValue) > 0 && Number(textFieldYearValue) <= 9999;
2324
+
2325
+ // Updates month offset when validating year. Adds or removes 12 months per year when updating year value.
2326
+ const updateMonthOffset = React.useCallback(() => {
2327
+ if (isYearValid) {
2328
+ const yearNumber = selectedMonth.getFullYear();
2329
+ const offset = (Number(textFieldYearValue) - yearNumber) * 12;
2330
+ if (onMonthChange) {
2331
+ onMonthChange(addMonthResetDay(selectedMonth, offset));
2332
+ }
2333
+ }
2334
+ }, [isYearValid, selectedMonth, textFieldYearValue, onMonthChange]);
2335
+ const monthYear = selectedMonth.toLocaleDateString(locale, {
2336
+ year: 'numeric',
2337
+ month: 'long'
2338
+ });
2339
+
2340
+ // Year can only be validatd by pressing Enter key or on Blur. The below handles the press Enter key case
2341
+ const handleKeyPress = React.useMemo(() => onEnterPressed(updateMonthOffset), [updateMonthOffset]);
2342
+
2343
+ // Required to update year in the TextField when the user changes year by using prev next month arrows
2344
+ React.useEffect(() => {
2345
+ if (Number(textFieldYearValue) !== selectedMonth.getFullYear()) {
2346
+ setTextFieldYearValue(selectedMonth.getFullYear().toString());
2347
+ }
2348
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2349
+ }, [selectedMonth]);
2302
2350
  const prevSelectedMonth = usePreviousValue(selectedMonth);
2303
2351
  const monthHasChanged = prevSelectedMonth && !isSameDay(selectedMonth, prevSelectedMonth);
2304
2352
 
@@ -2307,6 +2355,7 @@ const DatePickerControlled = /*#__PURE__*/forwardRef((props, ref) => {
2307
2355
  React.useEffect(() => {
2308
2356
  if (monthHasChanged) setLabelAriaLive('polite');
2309
2357
  }, [monthHasChanged]);
2358
+ const label = getYearDisplayName(locale);
2310
2359
  return /*#__PURE__*/React.createElement("div", {
2311
2360
  ref: ref,
2312
2361
  className: `${CLASSNAME$c}`
@@ -2322,13 +2371,32 @@ const DatePickerControlled = /*#__PURE__*/forwardRef((props, ref) => {
2322
2371
  icon: mdiChevronLeft,
2323
2372
  onClick: onPrevMonthChange
2324
2373
  })),
2325
- label: /*#__PURE__*/React.createElement("span", {
2374
+ label: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
2375
+ "aria-live": labelAriaLive,
2376
+ className: onMonthChange ? 'visually-hidden' : '',
2377
+ dir: "auto"
2378
+ }, monthYear), onMonthChange && /*#__PURE__*/React.createElement(FlexBox, {
2326
2379
  className: `${CLASSNAME$c}__month`,
2327
- "aria-live": labelAriaLive
2328
- }, selectedMonth.toLocaleDateString(locale, {
2329
- year: 'numeric',
2330
- month: 'long'
2331
- }))
2380
+ orientation: "horizontal",
2381
+ hAlign: "center",
2382
+ gap: "regular",
2383
+ vAlign: "center",
2384
+ dir: "auto"
2385
+ }, (_RegExp$exec = RegExp(`(.*)(${selectedYear})(.*)`).exec(monthYear)) === null || _RegExp$exec === void 0 ? void 0 : _RegExp$exec.slice(1).filter(part => part !== '').map(part => part === selectedYear ? /*#__PURE__*/React.createElement(TextField, {
2386
+ value: textFieldYearValue,
2387
+ "aria-label": label,
2388
+ onChange: setTextFieldYearValue,
2389
+ type: "number",
2390
+ max: 9999,
2391
+ min: 0,
2392
+ onBlur: updateMonthOffset,
2393
+ onKeyPress: handleKeyPress,
2394
+ key: "year",
2395
+ className: `${CLASSNAME$c}__year`
2396
+ }) : /*#__PURE__*/React.createElement(Text, {
2397
+ as: "p",
2398
+ key: part
2399
+ }, part))))
2332
2400
  }), /*#__PURE__*/React.createElement("div", {
2333
2401
  className: `${CLASSNAME$c}__calendar`
2334
2402
  }, /*#__PURE__*/React.createElement("div", {
@@ -2410,14 +2478,12 @@ const DatePicker = /*#__PURE__*/forwardRef((props, ref) => {
2410
2478
  console.warn(`[@lumx/react/DatePicker] Invalid date provided ${referenceDate}`);
2411
2479
  referenceDate = new Date();
2412
2480
  }
2413
- const [monthOffset, setMonthOffset] = useState(0);
2414
- const setPrevMonth = () => setMonthOffset(monthOffset - 1);
2415
- const setNextMonth = () => setMonthOffset(monthOffset + 1);
2481
+ const [selectedMonth, setSelectedMonth] = useState(referenceDate);
2482
+ const setPrevMonth = () => setSelectedMonth(current => addMonthResetDay(current, -1));
2483
+ const setNextMonth = () => setSelectedMonth(current => addMonthResetDay(current, +1));
2416
2484
  const onDatePickerChange = newDate => {
2417
2485
  onChange(newDate);
2418
- setMonthOffset(0);
2419
2486
  };
2420
- const selectedMonth = addMonthResetDay(referenceDate, monthOffset);
2421
2487
  return /*#__PURE__*/React.createElement(DatePickerControlled, _extends({
2422
2488
  ref: ref
2423
2489
  }, forwardedProps, {
@@ -2427,7 +2493,8 @@ const DatePicker = /*#__PURE__*/forwardRef((props, ref) => {
2427
2493
  onPrevMonthChange: setPrevMonth,
2428
2494
  onNextMonthChange: setNextMonth,
2429
2495
  selectedMonth: selectedMonth,
2430
- onChange: onDatePickerChange
2496
+ onChange: onDatePickerChange,
2497
+ onMonthChange: setSelectedMonth
2431
2498
  }));
2432
2499
  });
2433
2500
  DatePicker.displayName = COMPONENT_NAME$d;
@@ -6471,7 +6538,7 @@ function usePopoverStyle(_ref5) {
6471
6538
  };
6472
6539
  }
6473
6540
 
6474
- const _excluded$m = ["anchorRef", "as", "children", "className", "closeOnClickAway", "closeOnEscape", "elevation", "focusElement", "hasArrow", "isOpen", "onClose", "parentElement", "usePortal", "focusAnchorOnClose", "withFocusTrap", "boundaryRef", "fitToAnchorWidth", "fitWithinViewportHeight", "offset", "placement", "style", "theme", "zIndex"];
6541
+ const _excluded$m = ["anchorRef", "as", "children", "className", "closeOnClickAway", "closeOnEscape", "elevation", "focusElement", "hasArrow", "isOpen", "onClose", "parentElement", "usePortal", "focusAnchorOnClose", "withFocusTrap", "boundaryRef", "fitToAnchorWidth", "fitWithinViewportHeight", "focusTrapZone", "offset", "placement", "style", "theme", "zIndex"];
6475
6542
 
6476
6543
  /**
6477
6544
  * Defines the props of the component.
@@ -6524,6 +6591,7 @@ const _InnerPopover = /*#__PURE__*/forwardRef((props, ref) => {
6524
6591
  boundaryRef,
6525
6592
  fitToAnchorWidth,
6526
6593
  fitWithinViewportHeight,
6594
+ focusTrapZone,
6527
6595
  offset,
6528
6596
  placement,
6529
6597
  style,
@@ -6561,7 +6629,7 @@ const _InnerPopover = /*#__PURE__*/forwardRef((props, ref) => {
6561
6629
 
6562
6630
  /** Only set focus within if the focus trap is disabled as they interfere with one another. */
6563
6631
  useFocus(focusElement === null || focusElement === void 0 ? void 0 : focusElement.current, !withFocusTrap && isOpen && isPositioned);
6564
- useFocusTrap(withFocusTrap && isOpen && (popoverRef === null || popoverRef === void 0 ? void 0 : popoverRef.current), focusElement === null || focusElement === void 0 ? void 0 : focusElement.current);
6632
+ useFocusTrap(withFocusTrap && isOpen && (focusTrapZone === null || focusTrapZone === void 0 ? void 0 : focusTrapZone.current) || (popoverRef === null || popoverRef === void 0 ? void 0 : popoverRef.current), focusElement === null || focusElement === void 0 ? void 0 : focusElement.current);
6565
6633
  const clickAwayRefs = useRef([popoverRef, anchorRef]);
6566
6634
  const mergedRefs = useMergeRefs(setPopperElement, ref, popoverRef);
6567
6635
  return isOpen ? renderPopover( /*#__PURE__*/React.createElement(Component, _extends({}, forwardedProps, {
@@ -8233,7 +8301,7 @@ const ListSubheader = /*#__PURE__*/forwardRef((props, ref) => {
8233
8301
  ListSubheader.displayName = COMPONENT_NAME$F;
8234
8302
  ListSubheader.className = CLASSNAME$C;
8235
8303
 
8236
- const _excluded$H = ["children", "className", "hasBackground", "kind", "icon"];
8304
+ const _excluded$H = ["children", "className", "hasBackground", "kind", "icon", "closeButtonProps"];
8237
8305
 
8238
8306
  /**
8239
8307
  * Defines the props of the component.
@@ -8284,13 +8352,19 @@ const Message = /*#__PURE__*/forwardRef((props, ref) => {
8284
8352
  className,
8285
8353
  hasBackground,
8286
8354
  kind,
8287
- icon: customIcon
8355
+ icon: customIcon,
8356
+ closeButtonProps
8288
8357
  } = props,
8289
8358
  forwardedProps = _objectWithoutProperties(props, _excluded$H);
8290
8359
  const {
8291
8360
  color,
8292
8361
  icon
8293
8362
  } = CONFIG$1[kind] || {};
8363
+ const {
8364
+ onClick,
8365
+ label: closeButtonLabel
8366
+ } = closeButtonProps || {};
8367
+ const isCloseButtonDisplayed = hasBackground && kind === 'info' && onClick && closeButtonLabel;
8294
8368
  return /*#__PURE__*/React.createElement("div", _extends({
8295
8369
  ref: ref,
8296
8370
  className: classnames(className, handleBasicClasses({
@@ -8305,7 +8379,13 @@ const Message = /*#__PURE__*/forwardRef((props, ref) => {
8305
8379
  color: color
8306
8380
  }), /*#__PURE__*/React.createElement("div", {
8307
8381
  className: `${CLASSNAME$D}__text`
8308
- }, children));
8382
+ }, children), isCloseButtonDisplayed && /*#__PURE__*/React.createElement(IconButton, {
8383
+ className: `${CLASSNAME$D}__close-button`,
8384
+ icon: mdiClose,
8385
+ onClick: onClick,
8386
+ label: closeButtonLabel,
8387
+ emphasis: Emphasis.low
8388
+ }));
8309
8389
  });
8310
8390
  Message.displayName = COMPONENT_NAME$G;
8311
8391
  Message.className = CLASSNAME$D;