@carbon/react 1.84.0-rc.0 → 1.84.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.
Files changed (39) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +883 -883
  2. package/es/components/ComposedModal/ComposedModal.js +19 -2
  3. package/es/components/Modal/Modal.js +19 -2
  4. package/es/components/MultiSelect/FilterableMultiSelect.js +21 -0
  5. package/es/components/OverflowMenu/OverflowMenu.js +4 -5
  6. package/es/components/PageHeader/PageHeader.d.ts +10 -9
  7. package/es/components/PageHeader/PageHeader.js +92 -32
  8. package/es/components/PageHeader/index.d.ts +2 -2
  9. package/es/components/PageHeader/index.js +1 -1
  10. package/es/components/Search/Search.js +0 -1
  11. package/es/components/Slider/Slider.js +6 -0
  12. package/es/components/TextArea/TextArea.js +4 -4
  13. package/es/components/TileGroup/TileGroup.d.ts +4 -4
  14. package/es/components/TileGroup/TileGroup.js +45 -53
  15. package/es/components/TileGroup/index.d.ts +3 -3
  16. package/es/components/UIShell/HeaderMenuItem.js +2 -1
  17. package/es/index.js +1 -1
  18. package/es/internal/useOverflowItems.d.ts +29 -0
  19. package/es/internal/useOverflowItems.js +122 -0
  20. package/lib/components/ComposedModal/ComposedModal.js +19 -2
  21. package/lib/components/Modal/Modal.js +19 -2
  22. package/lib/components/MultiSelect/FilterableMultiSelect.js +21 -0
  23. package/lib/components/OverflowMenu/OverflowMenu.js +4 -5
  24. package/lib/components/PageHeader/PageHeader.d.ts +10 -9
  25. package/lib/components/PageHeader/PageHeader.js +90 -32
  26. package/lib/components/PageHeader/index.d.ts +2 -2
  27. package/lib/components/PageHeader/index.js +0 -2
  28. package/lib/components/Search/Search.js +0 -1
  29. package/lib/components/Slider/Slider.js +6 -0
  30. package/lib/components/TextArea/TextArea.js +4 -4
  31. package/lib/components/TileGroup/TileGroup.d.ts +4 -4
  32. package/lib/components/TileGroup/TileGroup.js +44 -52
  33. package/lib/components/TileGroup/index.d.ts +3 -3
  34. package/lib/components/UIShell/HeaderMenuItem.js +2 -1
  35. package/lib/index.js +1 -1
  36. package/lib/internal/useOverflowItems.d.ts +29 -0
  37. package/lib/internal/useOverflowItems.js +126 -0
  38. package/package.json +9 -9
  39. package/telemetry.yml +2 -0
@@ -176,7 +176,7 @@ const ComposedModal = /*#__PURE__*/React.forwardRef(function ComposedModal({
176
176
  target: oldActiveNode,
177
177
  relatedTarget: currentActiveNode
178
178
  }) {
179
- if (open && currentActiveNode && oldActiveNode && innerModal.current) {
179
+ if (!enableDialogElement && !focusTrapWithoutSentinels && open && currentActiveNode && oldActiveNode && innerModal.current) {
180
180
  const {
181
181
  current: bodyNode
182
182
  } = innerModal;
@@ -195,6 +195,23 @@ const ComposedModal = /*#__PURE__*/React.forwardRef(function ComposedModal({
195
195
  selectorsFloatingMenus: selectorsFloatingMenus?.filter(Boolean)
196
196
  });
197
197
  }
198
+
199
+ // Adjust scroll if needed so that element with focus is not obscured by gradient
200
+ const modalContent = document.querySelector(`.${prefix}--modal-content`);
201
+ if (!modalContent || !modalContent.classList.contains(`${prefix}--modal-scroll-content`) || !currentActiveNode || !modalContent.contains(currentActiveNode)) {
202
+ return;
203
+ }
204
+ const lastContent = modalContent.children[modalContent.children.length - 1];
205
+ const gradientSpacing = modalContent.scrollHeight - lastContent.offsetTop - lastContent.clientHeight;
206
+ for (let elem of modalContent.children) {
207
+ if (elem.contains(currentActiveNode)) {
208
+ const spaceBelow = modalContent.clientHeight - elem.offsetTop + modalContent.scrollTop - elem.clientHeight;
209
+ if (spaceBelow < gradientSpacing) {
210
+ modalContent.scrollTop = modalContent.scrollTop + (gradientSpacing - spaceBelow);
211
+ }
212
+ break;
213
+ }
214
+ }
198
215
  }
199
216
  function closeModal(evt) {
200
217
  if (!onClose || onClose(evt) !== false) {
@@ -309,7 +326,7 @@ const ComposedModal = /*#__PURE__*/React.forwardRef(function ComposedModal({
309
326
  role: "presentation",
310
327
  ref: ref,
311
328
  "aria-hidden": !open,
312
- onBlur: !enableDialogElement && !focusTrapWithoutSentinels ? handleBlur : () => {},
329
+ onBlur: handleBlur,
313
330
  onClick: composeEventHandlers([rest?.onClick, handleOnClick]),
314
331
  onMouseDown: composeEventHandlers([rest?.onMouseDown, handleOnMouseDown]),
315
332
  onKeyDown: handleKeyDown,
@@ -129,7 +129,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
129
129
  target: oldActiveNode,
130
130
  relatedTarget: currentActiveNode
131
131
  }) {
132
- if (open && oldActiveNode instanceof HTMLElement && currentActiveNode instanceof HTMLElement) {
132
+ if (!enableDialogElement && open && oldActiveNode instanceof HTMLElement && currentActiveNode instanceof HTMLElement) {
133
133
  const {
134
134
  current: bodyNode
135
135
  } = innerModal;
@@ -148,6 +148,23 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
148
148
  selectorsFloatingMenus
149
149
  });
150
150
  }
151
+
152
+ // Adjust scroll if needed so that element with focus is not obscured by gradient
153
+ const modalContent = document.querySelector(`.${prefix}--modal-content`);
154
+ if (!modalContent || !modalContent.classList.contains(`${prefix}--modal-scroll-content`) || !currentActiveNode || !modalContent.contains(currentActiveNode)) {
155
+ return;
156
+ }
157
+ const lastContent = modalContent.children[modalContent.children.length - 1];
158
+ const gradientSpacing = modalContent.scrollHeight - lastContent.offsetTop - lastContent.clientHeight;
159
+ for (let elem of modalContent.children) {
160
+ if (elem.contains(currentActiveNode)) {
161
+ const spaceBelow = modalContent.clientHeight - elem.offsetTop + modalContent.scrollTop - elem.clientHeight;
162
+ if (spaceBelow < gradientSpacing) {
163
+ modalContent.scrollTop = modalContent.scrollTop + (gradientSpacing - spaceBelow);
164
+ }
165
+ break;
166
+ }
167
+ }
151
168
  }
152
169
  const onSecondaryButtonClick = onSecondarySubmit ? onSecondarySubmit : onRequestClose;
153
170
  const modalClasses = cx(`${prefix}--modal`, {
@@ -404,7 +421,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
404
421
  level: 0,
405
422
  onKeyDown: handleKeyDown,
406
423
  onClick: composeEventHandlers([rest?.onClick, handleOnClick]),
407
- onBlur: !enableDialogElement ? handleBlur : () => {},
424
+ onBlur: handleBlur,
408
425
  className: modalClasses,
409
426
  role: "presentation",
410
427
  ref: ref
@@ -248,6 +248,27 @@ const FilterableMultiSelect = /*#__PURE__*/forwardRef(function FilterableMultiSe
248
248
  onMenuChange?.(isOpen);
249
249
  }
250
250
  }, [isOpen, onMenuChange, open]);
251
+ useEffect(() => {
252
+ const handleClickOutside = event => {
253
+ const target = event.target;
254
+ const wrapper = document.getElementById(id)?.closest(`.${prefix}--multi-select__wrapper`);
255
+
256
+ // If click is outside our component and menu is open or input is focused
257
+ if (wrapper && !wrapper.contains(target)) {
258
+ if (isOpen || inputFocused) {
259
+ setIsOpen(false);
260
+ setInputFocused(false);
261
+ setInputValue('');
262
+ }
263
+ }
264
+ };
265
+ if (inputFocused || isOpen) {
266
+ document.addEventListener('mousedown', handleClickOutside);
267
+ }
268
+ return () => {
269
+ document.removeEventListener('mousedown', handleClickOutside);
270
+ };
271
+ }, [isOpen, inputFocused]);
251
272
  const {
252
273
  getToggleButtonProps,
253
274
  getLabelProps,
@@ -143,14 +143,14 @@ const OverflowMenu = /*#__PURE__*/forwardRef(({
143
143
  setHasMountedTrigger(true);
144
144
  }
145
145
  }, []);
146
-
147
- // Call `onClose` when menu closes.
148
146
  useEffect(() => {
149
- if (!open && prevOpenState.current) {
147
+ if (open && !prevOpenState.current) {
148
+ onOpen();
149
+ } else if (!open && prevOpenState.current) {
150
150
  onClose();
151
151
  }
152
152
  prevOpenState.current = open;
153
- }, [open, onClose]);
153
+ }, [open, onClose, onOpen]);
154
154
  useOutsideClick(wrapperRef, ({
155
155
  target
156
156
  }) => {
@@ -259,7 +259,6 @@ const OverflowMenu = /*#__PURE__*/forwardRef(({
259
259
  }
260
260
  }
261
261
  }, !hasFocusin);
262
- onOpen();
263
262
  };
264
263
  const getTarget = () => {
265
264
  const triggerEl = triggerRef.current;
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import React, { type ComponentType, type FunctionComponent } from 'react';
8
8
  import PropTypes from 'prop-types';
9
- import { Tabs as BaseTabs } from '../Tabs/Tabs';
9
+ import { TYPES } from '../Tag/Tag';
10
10
  /**
11
11
  * ----------
12
12
  * PageHeader
@@ -199,16 +199,18 @@ declare const PageHeaderHeroImage: {
199
199
  * PageHeaderTabBar
200
200
  * ----------------
201
201
  */
202
+ interface TagItem {
203
+ type: keyof typeof TYPES;
204
+ text: string;
205
+ size?: 'sm' | 'md' | 'lg';
206
+ id: string;
207
+ }
202
208
  interface PageHeaderTabBarProps {
203
209
  children?: React.ReactNode;
204
210
  className?: string;
211
+ tags?: TagItem[];
205
212
  }
206
213
  declare const PageHeaderTabBar: React.ForwardRefExoticComponent<PageHeaderTabBarProps & React.RefAttributes<HTMLDivElement>>;
207
- interface PageHeaderTabsProps extends React.ComponentProps<typeof BaseTabs> {
208
- children?: React.ReactNode;
209
- className?: string;
210
- }
211
- declare const PageHeaderTabs: React.ForwardRefExoticComponent<PageHeaderTabsProps & React.RefAttributes<HTMLDivElement>>;
212
214
  /**
213
215
  * -------
214
216
  * Exports
@@ -272,6 +274,5 @@ declare const HeroImage: {
272
274
  };
273
275
  };
274
276
  declare const TabBar: React.ForwardRefExoticComponent<PageHeaderTabBarProps & React.RefAttributes<HTMLDivElement>>;
275
- declare const Tabs: React.ForwardRefExoticComponent<PageHeaderTabsProps & React.RefAttributes<HTMLDivElement>>;
276
- export { PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderHeroImage, PageHeaderTabBar, PageHeaderTabs, Root, BreadcrumbBar, Content, ContentPageActions, ContentText, HeroImage, TabBar, Tabs, };
277
- export type { PageHeaderProps, PageHeaderBreadcrumbBarProps, PageHeaderContentProps, PageHeaderContentPageActionsProps, PageHeaderContentTextProps, PageHeaderHeroImageProps, PageHeaderTabBarProps, PageHeaderTabsProps, };
277
+ export { PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderHeroImage, PageHeaderTabBar, Root, BreadcrumbBar, Content, ContentPageActions, ContentText, HeroImage, TabBar, };
278
+ export type { PageHeaderProps, PageHeaderBreadcrumbBarProps, PageHeaderContentProps, PageHeaderContentPageActionsProps, PageHeaderContentTextProps, PageHeaderHeroImageProps, PageHeaderTabBarProps, };
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
- import React, { useRef, useState, useLayoutEffect, useEffect } from 'react';
9
+ import React, { useRef, useState, useLayoutEffect, useEffect, useMemo, useCallback } from 'react';
10
10
  import PropTypes from 'prop-types';
11
11
  import cx from 'classnames';
12
12
  import { usePrefix } from '../../internal/usePrefix.js';
@@ -20,7 +20,14 @@ import { DefinitionTooltip } from '../Tooltip/DefinitionTooltip.js';
20
20
  import '../Tooltip/Tooltip.js';
21
21
  import AspectRatio from '../AspectRatio/AspectRatio.js';
22
22
  import { createOverflowHandler } from '@carbon/utilities';
23
- import { Tabs as Tabs$1 } from '../Tabs/Tabs.js';
23
+ import Tag from '../Tag/Tag.js';
24
+ import '../Tag/DismissibleTag.js';
25
+ import OperationalTag from '../Tag/OperationalTag.js';
26
+ import '../Tag/SelectableTag.js';
27
+ import '../Tag/Tag.Skeleton.js';
28
+ import useOverflowItems from '../../internal/useOverflowItems.js';
29
+ import { Popover, PopoverContent } from '../Popover/index.js';
30
+ import { useId } from '../../internal/useId.js';
24
31
  import '../Grid/FlexGrid.js';
25
32
  import { Grid as GridAsGridComponent } from '../Grid/Grid.js';
26
33
  import '../Grid/Row.js';
@@ -355,46 +362,101 @@ PageHeaderHeroImage.propTypes = {
355
362
  const PageHeaderTabBar = /*#__PURE__*/React.forwardRef(function PageHeaderTabBar({
356
363
  className,
357
364
  children,
365
+ tags = [],
358
366
  ...other
359
367
  }, ref) {
360
368
  const prefix = usePrefix();
361
369
  const classNames = cx({
362
370
  [`${prefix}--page-header__tab-bar`]: true
363
371
  }, className);
372
+ // Early return if no tags are provided
373
+ if (!tags.length) {
374
+ return /*#__PURE__*/React.createElement("div", _extends({
375
+ className: classNames,
376
+ ref: ref
377
+ }, other), /*#__PURE__*/React.createElement(GridAsGridComponent, null, /*#__PURE__*/React.createElement(Column, {
378
+ lg: 16,
379
+ md: 8,
380
+ sm: 4
381
+ }, children)));
382
+ }
383
+ const [openPopover, setOpenPopover] = useState(false);
384
+ const tagSize = tags[0]?.size || 'md';
385
+ const instanceId = useId('PageHeaderTabBar');
386
+ const tagsWithIds = useMemo(() => {
387
+ return tags.map((tag, index) => ({
388
+ ...tag,
389
+ id: tag.id || `tag-${index}-${instanceId}`
390
+ }));
391
+ }, [tags]);
392
+ const tagsContainerRef = useRef(null);
393
+ const offsetRef = useRef(null);
394
+ // To close popover when window resizes
395
+ useEffect(() => {
396
+ const handleResize = () => {
397
+ // Close the popover when window resizes to prevent unwanted opens
398
+ setOpenPopover(false);
399
+ };
400
+ window.addEventListener('resize', handleResize);
401
+ return () => {
402
+ window.removeEventListener('resize', handleResize);
403
+ };
404
+ }, []);
405
+
406
+ // overflow items hook
407
+ const {
408
+ visibleItems = [],
409
+ hiddenItems = [],
410
+ itemRefHandler = () => {}
411
+ } = useOverflowItems(tagsWithIds, tagsContainerRef, offsetRef) || {
412
+ visibleItems: [],
413
+ hiddenItems: [],
414
+ itemRefHandler: () => {}
415
+ };
416
+ const handleOverflowClick = useCallback(event => {
417
+ event.stopPropagation();
418
+ setOpenPopover(prev => !prev);
419
+ }, []);
420
+
421
+ // Function to render tags
422
+ const renderTags = () => /*#__PURE__*/React.createElement("div", {
423
+ className: `${prefix}--page-header__tags`,
424
+ ref: tagsContainerRef
425
+ }, visibleItems.map(tag => /*#__PURE__*/React.createElement(Tag, {
426
+ key: tag.id,
427
+ ref: node => itemRefHandler(tag.id, node),
428
+ type: tag.type,
429
+ size: tag.size,
430
+ className: `${prefix}--page-header__tag-item`
431
+ }, tag.text)), hiddenItems.length > 0 && /*#__PURE__*/React.createElement(Popover, {
432
+ open: openPopover,
433
+ onRequestClose: () => setOpenPopover(false)
434
+ }, /*#__PURE__*/React.createElement(OperationalTag, {
435
+ onClick: handleOverflowClick,
436
+ "aria-expanded": openPopover,
437
+ text: `+${hiddenItems.length}`,
438
+ size: tagSize
439
+ }), /*#__PURE__*/React.createElement(PopoverContent, {
440
+ className: "tag-popover-content"
441
+ }, /*#__PURE__*/React.createElement("div", {
442
+ className: `${prefix}--page-header__tags-popover-list`
443
+ }, hiddenItems.map(tag => /*#__PURE__*/React.createElement(Tag, {
444
+ key: tag.id,
445
+ type: tag.type,
446
+ size: tag.size
447
+ }, tag.text))))));
364
448
  return /*#__PURE__*/React.createElement("div", _extends({
365
449
  className: classNames,
366
450
  ref: ref
367
- }, other), children);
368
- });
369
- PageHeaderTabBar.displayName = 'PageHeaderTabBar';
370
- const PageHeaderTabs = /*#__PURE__*/React.forwardRef(function PageHeaderTabs({
371
- className,
372
- children,
373
- ...other
374
- }, ref) {
375
- const prefix = usePrefix();
376
- const childrenArray = React.Children.toArray(children);
377
- let tabListElement = null;
378
- const otherChildren = [];
379
-
380
- // extract the TabList component so we can wrap a needed div around for
381
- // layout purposes
382
- childrenArray.forEach(child => {
383
- if (child?.type?.displayName === 'TabList' || child?.type?.name === 'TabList') {
384
- tabListElement = child;
385
- } else {
386
- otherChildren.push(child);
387
- }
388
- });
389
- return /*#__PURE__*/React.createElement(Tabs$1, other, tabListElement && /*#__PURE__*/React.createElement("div", {
390
- className: `${prefix}--page-header__tablist-wrapper`
391
- }, /*#__PURE__*/React.createElement(GridAsGridComponent, null, /*#__PURE__*/React.createElement(Column, {
451
+ }, other), /*#__PURE__*/React.createElement(GridAsGridComponent, null, /*#__PURE__*/React.createElement(Column, {
392
452
  lg: 16,
393
453
  md: 8,
394
454
  sm: 4
395
- }, tabListElement))), otherChildren);
455
+ }, /*#__PURE__*/React.createElement("div", {
456
+ className: `${prefix}--page-header__tab-bar--tablist`
457
+ }, children, tags.length > 0 && renderTags()))));
396
458
  });
397
- PageHeaderTabs.displayName = 'PageHeaderTabs';
459
+ PageHeaderTabBar.displayName = 'PageHeaderTabBar';
398
460
 
399
461
  /**
400
462
  * -------
@@ -415,7 +477,5 @@ const HeroImage = PageHeaderHeroImage;
415
477
  HeroImage.displayName = 'PageHeaderHeroImage';
416
478
  const TabBar = PageHeaderTabBar;
417
479
  TabBar.displayName = 'PageHeaderTabBar';
418
- const Tabs = PageHeaderTabs;
419
- Tabs.displayName = 'PageHeader.Tabs';
420
480
 
421
- export { BreadcrumbBar, Content, ContentPageActions, ContentText, HeroImage, PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderHeroImage, PageHeaderTabBar, PageHeaderTabs, Root, TabBar, Tabs };
481
+ export { BreadcrumbBar, Content, ContentPageActions, ContentText, HeroImage, PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderHeroImage, PageHeaderTabBar, Root, TabBar };
@@ -4,5 +4,5 @@
4
4
  * This source code is licensed under the Apache-2.0 license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- export { PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderTabBar, PageHeaderHeroImage, PageHeaderTabs, Root, BreadcrumbBar, Content, ContentPageActions, ContentText, TabBar, HeroImage, Tabs, } from './PageHeader';
8
- export type { PageHeaderProps, PageHeaderBreadcrumbBarProps, PageHeaderContentProps, PageHeaderContentPageActionsProps, PageHeaderContentTextProps, PageHeaderTabBarProps, PageHeaderHeroImageProps, PageHeaderTabsProps, } from './PageHeader';
7
+ export { PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderTabBar, PageHeaderHeroImage, Root, BreadcrumbBar, Content, ContentPageActions, ContentText, TabBar, HeroImage, } from './PageHeader';
8
+ export type { PageHeaderProps, PageHeaderBreadcrumbBarProps, PageHeaderContentProps, PageHeaderContentPageActionsProps, PageHeaderContentTextProps, PageHeaderTabBarProps, PageHeaderHeroImageProps, } from './PageHeader';
@@ -5,4 +5,4 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- export { BreadcrumbBar, Content, ContentPageActions, ContentText, HeroImage, PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderHeroImage, PageHeaderTabBar, PageHeaderTabs, Root, TabBar, Tabs } from './PageHeader.js';
8
+ export { BreadcrumbBar, Content, ContentPageActions, ContentText, HeroImage, PageHeader, PageHeaderBreadcrumbBar, PageHeaderContent, PageHeaderContentPageActions, PageHeaderContentText, PageHeaderHeroImage, PageHeaderTabBar, Root, TabBar } from './PageHeader.js';
@@ -134,7 +134,6 @@ const Search = /*#__PURE__*/React.forwardRef(function Search({
134
134
  "aria-label": placeholder,
135
135
  className: searchClasses
136
136
  }, /*#__PURE__*/React.createElement("div", {
137
- "aria-label": onExpand ? 'button' : undefined,
138
137
  "aria-labelledby": onExpand ? searchId : undefined,
139
138
  role: onExpand ? 'button' : undefined,
140
139
  className: `${prefix}--search-magnifier`,
@@ -758,6 +758,9 @@ class Slider extends PureComponent {
758
758
  valueUpper,
759
759
  leftUpper
760
760
  });
761
+ if (this.filledTrackRef.current) {
762
+ this.filledTrackRef.current.style.transform = this.state.isRtl ? `translate(${100 - this.state.leftUpper}%, -50%) scaleX(${(this.state.leftUpper - this.state.left) / 100})` : `translate(${this.state.left}%, -50%) scaleX(${(this.state.leftUpper - this.state.left) / 100})`;
763
+ }
761
764
  } else {
762
765
  const {
763
766
  value,
@@ -771,6 +774,9 @@ class Slider extends PureComponent {
771
774
  value,
772
775
  left
773
776
  });
777
+ if (this.filledTrackRef.current) {
778
+ this.filledTrackRef.current.style.transform = this.state.isRtl ? `translate(100%, -50%) scaleX(-${this.state.left / 100})` : `translate(0%, -50%) scaleX(${this.state.left / 100})`;
779
+ }
774
780
  }
775
781
  }
776
782
  }
@@ -65,7 +65,7 @@ const TextArea = frFn((props, forwardRef) => {
65
65
  if (counterMode === 'character') {
66
66
  return strValue.length;
67
67
  } else {
68
- return strValue.match(/\w+/g)?.length || 0;
68
+ return strValue.match(/\p{L}+/gu)?.length || 0;
69
69
  }
70
70
  }
71
71
  const [textCount, setTextCount] = useState(getInitialTextCount());
@@ -97,8 +97,8 @@ const TextArea = frFn((props, forwardRef) => {
97
97
  onPaste: evt => {
98
98
  if (!disabled) {
99
99
  if (counterMode === 'word' && enableCounter && typeof maxCount !== 'undefined' && textareaRef.current !== null) {
100
- const existingWords = textareaRef.current.value.match(/\w+/g) || [];
101
- const pastedWords = evt.clipboardData.getData('Text').match(/\w+/g) || [];
100
+ const existingWords = textareaRef.current.value.match(/\p{L}+/gu) || [];
101
+ const pastedWords = evt.clipboardData.getData('Text').match(/\p{L}+/gu) || [];
102
102
  const totalWords = existingWords.length + pastedWords.length;
103
103
  if (totalWords > maxCount) {
104
104
  evt.preventDefault();
@@ -127,7 +127,7 @@ const TextArea = frFn((props, forwardRef) => {
127
127
  return;
128
128
  }
129
129
  if (enableCounter && typeof maxCount !== 'undefined' && textareaRef.current !== null) {
130
- const matchedWords = evt.target?.value?.match(/\w+/g);
130
+ const matchedWords = evt.target?.value?.match(/\p{L}+/gu);
131
131
  if (matchedWords && matchedWords.length <= maxCount) {
132
132
  textareaRef.current.removeAttribute('maxLength');
133
133
  setTimeout(() => {
@@ -45,8 +45,9 @@ export interface TileGroupProps extends Omit<HTMLAttributes<HTMLFieldSetElement>
45
45
  */
46
46
  required?: boolean;
47
47
  }
48
- declare const TileGroup: {
49
- (props: any): import("react/jsx-runtime").JSX.Element;
48
+ export declare const TileGroup: {
49
+ ({ children, className, defaultSelected, disabled, legend, name, onChange, valueSelected, required, }: TileGroupProps): import("react/jsx-runtime").JSX.Element;
50
+ displayName: string;
50
51
  propTypes: {
51
52
  /**
52
53
  * Provide a collection of <RadioTile> components to render in the group
@@ -86,6 +87,5 @@ declare const TileGroup: {
86
87
  */
87
88
  valueSelected: PropTypes.Requireable<NonNullable<string | number | null | undefined>>;
88
89
  };
89
- displayName: string;
90
90
  };
91
- export default TileGroup;
91
+ export {};
@@ -7,40 +7,42 @@
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
9
  import PropTypes from 'prop-types';
10
- import React, { useState } from 'react';
10
+ import React, { useState, useEffect, Children, isValidElement, cloneElement } from 'react';
11
11
  import RadioTile from '../RadioTile/RadioTile.js';
12
12
  import { usePrefix } from '../../internal/usePrefix.js';
13
13
  import { noopFn } from '../../internal/noopFn.js';
14
14
 
15
- const TileGroup = props => {
16
- const {
17
- children,
18
- className,
19
- defaultSelected,
20
- disabled,
21
- legend,
22
- name,
23
- onChange = noopFn,
24
- valueSelected,
25
- required
26
- } = props;
15
+ const TileGroup = ({
16
+ children,
17
+ className,
18
+ defaultSelected,
19
+ disabled,
20
+ legend,
21
+ name,
22
+ onChange = noopFn,
23
+ valueSelected,
24
+ required
25
+ }) => {
27
26
  const prefix = usePrefix();
28
27
  const [selected, setSelected] = useState(valueSelected ?? defaultSelected);
29
- const [prevValueSelected, setPrevValueSelected] = useState(valueSelected);
28
+ useEffect(() => {
29
+ if (typeof valueSelected !== 'undefined' && valueSelected !== selected) {
30
+ setSelected(valueSelected);
31
+ }
32
+ }, [valueSelected, selected]);
33
+ const handleChange = (value, name, evt) => {
34
+ if (value !== selected) {
35
+ setSelected(value);
36
+ onChange(value, name ?? '', evt);
37
+ }
38
+ };
39
+ const getRadioTilesWithWrappers = elements => {
40
+ const traverseAndModifyChildren = elements => {
41
+ return Children.map(elements, child => {
42
+ if (! /*#__PURE__*/isValidElement(child)) return child;
30
43
 
31
- /**
32
- * prop + state alignment - getDerivedStateFromProps
33
- * only update if selected prop changes
34
- */
35
- if (valueSelected !== prevValueSelected) {
36
- setSelected(valueSelected);
37
- setPrevValueSelected(valueSelected);
38
- }
39
- const getRadioTilesWithWrappers = children => {
40
- const traverseAndModifyChildren = children => {
41
- return React.Children.map(children, child => {
42
- // If RadioTile found, return it with necessary props
43
- if (child?.type === RadioTile) {
44
+ // If a `RadioTile` is found, return it with necessary props,
45
+ if (/*#__PURE__*/isValidElement(child) && child.type === RadioTile) {
44
46
  const {
45
47
  value,
46
48
  ...otherProps
@@ -53,38 +55,29 @@ const TileGroup = props => {
53
55
  onChange: handleChange,
54
56
  checked: value === selected
55
57
  }));
56
- } else if (child?.props?.children) {
57
- // If the child is not RadioTile and has children, recheck the children
58
- return /*#__PURE__*/React.cloneElement(child, {
59
- ...child.props,
60
- children: traverseAndModifyChildren(child.props.children)
61
- });
62
- } else {
63
- // If the child is neither a RadioTile nor has children, return it as is
64
- return child;
65
58
  }
59
+
60
+ // If the child is not RadioTile and has children, recheck the children
61
+ const children = child.props.children;
62
+ const hasChildren = Children.count(children) > 0;
63
+ if (hasChildren) {
64
+ return /*#__PURE__*/cloneElement(child, undefined, traverseAndModifyChildren(children));
65
+ }
66
+
67
+ // If the child is neither a RadioTile nor has children, return it as is
68
+ return child;
66
69
  });
67
70
  };
68
- return /*#__PURE__*/React.createElement(React.Fragment, null, traverseAndModifyChildren(children));
69
- };
70
- const handleChange = (newSelection, value, evt) => {
71
- if (newSelection !== selected) {
72
- setSelected(newSelection);
73
- onChange(newSelection, name, evt);
74
- }
75
- };
76
- const renderLegend = legend => {
77
- if (legend) {
78
- return /*#__PURE__*/React.createElement("legend", {
79
- className: `${prefix}--label`
80
- }, legend);
81
- }
71
+ return /*#__PURE__*/React.createElement(React.Fragment, null, traverseAndModifyChildren(elements));
82
72
  };
83
73
  return /*#__PURE__*/React.createElement("fieldset", {
84
74
  className: className ?? `${prefix}--tile-group`,
85
75
  disabled: disabled
86
- }, renderLegend(legend), /*#__PURE__*/React.createElement("div", null, getRadioTilesWithWrappers(children)));
76
+ }, legend && /*#__PURE__*/React.createElement("legend", {
77
+ className: `${prefix}--label`
78
+ }, legend), /*#__PURE__*/React.createElement("div", null, getRadioTilesWithWrappers(children)));
87
79
  };
80
+ TileGroup.displayName = 'TileGroup';
88
81
  TileGroup.propTypes = {
89
82
  /**
90
83
  * Provide a collection of <RadioTile> components to render in the group
@@ -124,6 +117,5 @@ TileGroup.propTypes = {
124
117
  */
125
118
  valueSelected: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
126
119
  };
127
- TileGroup.displayName = 'TileGroup';
128
120
 
129
- export { TileGroup as default };
121
+ export { TileGroup };
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Copyright IBM Corp. 2016, 2023
2
+ * Copyright IBM Corp. 2016, 2025
3
3
  *
4
4
  * This source code is licensed under the Apache-2.0 license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import TileGroup, { type TileGroupProps } from './TileGroup';
7
+ import { TileGroup } from './TileGroup';
8
8
  export default TileGroup;
9
- export { TileGroup, type TileGroupProps };
9
+ export * from './TileGroup';
@@ -24,6 +24,7 @@ const HeaderMenuItem = /*#__PURE__*/forwardRef(function HeaderMenuItem({
24
24
  ...rest
25
25
  }, ref) {
26
26
  const prefix = usePrefix();
27
+ const resolvedTabIndex = tabIndex ?? 0;
27
28
  if (isCurrentPage) {
28
29
  isActive = isCurrentPage;
29
30
  }
@@ -42,7 +43,7 @@ const HeaderMenuItem = /*#__PURE__*/forwardRef(function HeaderMenuItem({
42
43
  "aria-current": hasCurrentClass ? true : ariaCurrent,
43
44
  className: linkClassName,
44
45
  ref: ref,
45
- tabIndex: tabIndex
46
+ tabIndex: resolvedTabIndex
46
47
  }), /*#__PURE__*/React.createElement("span", {
47
48
  className: `${prefix}--text-truncate--end`
48
49
  }, children)));
package/es/index.js CHANGED
@@ -148,7 +148,7 @@ export { default as TextAreaSkeleton } from './components/TextArea/TextArea.Skel
148
148
  export { default as TextInput } from './components/TextInput/TextInput.js';
149
149
  export { default as TextInputSkeleton } from './components/TextInput/TextInput.Skeleton.js';
150
150
  export { ClickableTile, ExpandableTile, SelectableTile, Tile, TileAboveTheFoldContent, TileBelowTheFoldContent } from './components/Tile/Tile.js';
151
- export { default as TileGroup } from './components/TileGroup/TileGroup.js';
151
+ export { TileGroup } from './components/TileGroup/TileGroup.js';
152
152
  export { default as TimePicker } from './components/TimePicker/TimePicker.js';
153
153
  export { default as TimePickerSelect } from './components/TimePickerSelect/TimePickerSelect.js';
154
154
  export { Toggle } from './components/Toggle/Toggle.js';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Copyright IBM Corp. 2025, 2025
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { ReactNode, RefObject } from 'react';
8
+ type Item = {
9
+ id: string;
10
+ };
11
+ /**
12
+ * Manages overflow items in a container by automatically hiding items that don't fit.
13
+ * @param items - Array of items to manage for overflow, each must have an `id` property.
14
+ * @param containerRef - React ref to the container element that holds the items.
15
+ * @param offsetRef - Optional ref to an offset element (like a "more" button) whose width is reserved when calculating available space.
16
+ * @param maxItems - Optional maximum number of visible items. If undefined, only container space constrains visibility.
17
+ * @param onChange - Optional callback called when hidden items change. Receives array of currently hidden items.
18
+ * @returns Object with `visibleItems` (items to display), `hiddenItems` (items that don't fit), and `itemRefHandler` (function to attach refs to items for width measurement).
19
+ */
20
+ declare const useOverflowItems: <T extends Item>(items: T[] | ReactNode, containerRef: RefObject<HTMLDivElement>, offsetRef?: RefObject<HTMLDivElement>, maxItems?: number, onChange?: (hiddenItems: T[]) => void) => {
21
+ visibleItems: T[];
22
+ hiddenItems: T[];
23
+ itemRefHandler: () => void;
24
+ } | {
25
+ visibleItems: T[];
26
+ itemRefHandler: (id: string, node: HTMLDivElement | null) => () => void;
27
+ hiddenItems: T[];
28
+ };
29
+ export default useOverflowItems;