@itwin/itwinui-react 3.3.4 → 3.4.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 (55) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/cjs/core/Carousel/CarouselSlider.js +1 -1
  3. package/cjs/core/ComboBox/ComboBox.js +1 -1
  4. package/cjs/core/DatePicker/DatePicker.d.ts +8 -0
  5. package/cjs/core/DatePicker/DatePicker.js +9 -4
  6. package/cjs/core/Dialog/DialogMain.js +1 -1
  7. package/cjs/core/FileUpload/FileUploadTemplate.d.ts +2 -1
  8. package/cjs/core/FileUpload/FileUploadTemplate.js +2 -1
  9. package/cjs/core/LinkAction/LinkAction.d.ts +3 -2
  10. package/cjs/core/LinkAction/LinkAction.js +33 -1
  11. package/cjs/core/Modal/Modal.d.ts +12 -0
  12. package/cjs/core/Modal/Modal.js +4 -4
  13. package/cjs/core/Popover/Popover.js +1 -1
  14. package/cjs/core/ProgressIndicators/ProgressLinear.d.ts +15 -7
  15. package/cjs/core/ProgressIndicators/ProgressLinear.js +14 -6
  16. package/cjs/core/ProgressIndicators/ProgressRadial.d.ts +3 -2
  17. package/cjs/core/ProgressIndicators/ProgressRadial.js +3 -1
  18. package/cjs/core/Table/Table.js +1 -1
  19. package/cjs/core/Tabs/Tabs.js +17 -16
  20. package/cjs/core/Tag/Tag.d.ts +29 -7
  21. package/cjs/core/Tag/Tag.js +12 -5
  22. package/cjs/core/ThemeProvider/ThemeProvider.js +2 -2
  23. package/cjs/core/utils/components/VirtualScroll.js +7 -7
  24. package/cjs/core/utils/hooks/useIsomorphicLayoutEffect.d.ts +4 -1
  25. package/cjs/core/utils/hooks/useIsomorphicLayoutEffect.js +5 -2
  26. package/cjs/core/utils/hooks/useMediaQuery.js +1 -1
  27. package/cjs/core/utils/hooks/useOverflow.js +3 -3
  28. package/esm/core/Carousel/CarouselSlider.js +2 -2
  29. package/esm/core/ComboBox/ComboBox.js +2 -2
  30. package/esm/core/DatePicker/DatePicker.d.ts +8 -0
  31. package/esm/core/DatePicker/DatePicker.js +9 -4
  32. package/esm/core/Dialog/DialogMain.js +2 -2
  33. package/esm/core/FileUpload/FileUploadTemplate.d.ts +2 -1
  34. package/esm/core/FileUpload/FileUploadTemplate.js +2 -1
  35. package/esm/core/LinkAction/LinkAction.d.ts +3 -2
  36. package/esm/core/LinkAction/LinkAction.js +7 -1
  37. package/esm/core/Modal/Modal.d.ts +12 -0
  38. package/esm/core/Modal/Modal.js +4 -4
  39. package/esm/core/Popover/Popover.js +2 -2
  40. package/esm/core/ProgressIndicators/ProgressLinear.d.ts +15 -7
  41. package/esm/core/ProgressIndicators/ProgressLinear.js +14 -6
  42. package/esm/core/ProgressIndicators/ProgressRadial.d.ts +3 -2
  43. package/esm/core/ProgressIndicators/ProgressRadial.js +3 -1
  44. package/esm/core/Table/Table.js +2 -2
  45. package/esm/core/Tabs/Tabs.js +18 -17
  46. package/esm/core/Tag/Tag.d.ts +29 -7
  47. package/esm/core/Tag/Tag.js +13 -6
  48. package/esm/core/ThemeProvider/ThemeProvider.js +3 -3
  49. package/esm/core/utils/components/VirtualScroll.js +8 -8
  50. package/esm/core/utils/hooks/useIsomorphicLayoutEffect.d.ts +4 -1
  51. package/esm/core/utils/hooks/useIsomorphicLayoutEffect.js +4 -1
  52. package/esm/core/utils/hooks/useMediaQuery.js +2 -2
  53. package/esm/core/utils/hooks/useOverflow.js +4 -4
  54. package/package.json +2 -2
  55. package/styles.css +8 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1824](https://github.com/iTwin/iTwinUI/pull/1824): `LinkAction` will now automatically render a `<button>` by default if no `href` is passed.
8
+ - [#1815](https://github.com/iTwin/iTwinUI/pull/1815): Removed unnecessary `Tag` styles, and added support for tags to be used as `<button>` elements.
9
+ - [#1813](https://github.com/iTwin/iTwinUI/pull/1813): Added warning status for `ProgressRadial`/`ProgressLinear`
10
+ - [#1815](https://github.com/iTwin/iTwinUI/pull/1815): Added `labelProps` and `removeButtonProps` to `Tag` to allow customizing internal parts.
11
+ - [#1821](https://github.com/iTwin/iTwinUI/pull/1821): Added new props to `Modal` to allow customizing inner DOM elements: `wrapperProps`, `backdropProps`, `titleBarProps`.
12
+ - [#1822](https://github.com/iTwin/iTwinUI/pull/1822): Added new `showDatesOutsideMonth` prop to `DatePicker`. Currently defaults to `true` but is recommended to be set to `false`.
13
+
3
14
  ## 3.3.4
4
15
 
5
16
  ### Patch Changes
@@ -52,7 +52,7 @@ exports.CarouselSlider = React.forwardRef((props, ref) => {
52
52
  index,
53
53
  })
54
54
  : child) ?? [], [children, idPrefix]);
55
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
55
+ (0, index_js_1.useLayoutEffect)(() => {
56
56
  setSlideCount(items.length);
57
57
  }, [items.length, setSlideCount]);
58
58
  const sliderRef = React.useRef(null);
@@ -120,7 +120,7 @@ exports.ComboBox = React.forwardRef((props, forwardedRef) => {
120
120
  dispatch({ type: 'close' });
121
121
  onHideRef.current?.();
122
122
  }, [onHideRef]);
123
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
123
+ (0, index_js_1.useLayoutEffect)(() => {
124
124
  // When the dropdown opens
125
125
  if (isOpen) {
126
126
  inputRef.current?.focus(); // Focus the input
@@ -96,6 +96,14 @@ type DatePickerProps = {
96
96
  * @default true
97
97
  */
98
98
  applyBackground?: boolean;
99
+ /**
100
+ * Whether dates outside the current month should be displayed (in a muted text style).
101
+ *
102
+ * It is recommended to set this to false. Currently it defaults to true for backward compatibility.
103
+ *
104
+ * @default true
105
+ */
106
+ showDatesOutsideMonth?: boolean;
99
107
  } & DateRangePickerProps & Omit<TimePickerProps, 'date' | 'onChange' | 'setFocusHour'>;
100
108
  /**
101
109
  * Date picker component
@@ -140,7 +140,7 @@ exports.generateLocalizedStrings = generateLocalizedStrings;
140
140
  * <DatePicker date={new Date()} onChange={(e) => console.log('New date value: ' + e)} />
141
141
  */
142
142
  exports.DatePicker = React.forwardRef((props, forwardedRef) => {
143
- const { date, onChange, localizedNames, className, setFocus = false, showTime = false, use12Hours = false, precision, hourStep, minuteStep, secondStep, useCombinedRenderer, combinedRenderer, hourRenderer, minuteRenderer, secondRenderer, meridiemRenderer, showYearSelection = false, enableRangeSelect = false, startDate, endDate, monthYearProps, calendarProps, monthProps, weekDayProps, dayProps, weekProps, isDateDisabled, applyBackground = true, ...rest } = props;
143
+ const { date, onChange, localizedNames, className, setFocus = false, showTime = false, use12Hours = false, precision, hourStep, minuteStep, secondStep, useCombinedRenderer, combinedRenderer, hourRenderer, minuteRenderer, secondRenderer, meridiemRenderer, showYearSelection = false, enableRangeSelect = false, startDate, endDate, monthYearProps, calendarProps, monthProps, weekDayProps, dayProps, weekProps, isDateDisabled, applyBackground = true, showDatesOutsideMonth = true, ...rest } = props;
144
144
  const monthNames = localizedNames?.months ?? defaultMonths;
145
145
  const shortDays = localizedNames?.shortDays ?? defaultShortDays;
146
146
  const longDays = localizedNames?.days ?? defaultLongDays;
@@ -182,8 +182,9 @@ exports.DatePicker = React.forwardRef((props, forwardedRef) => {
182
182
  }, [date, setMonthAndYear, startDate, endDate, enableRangeSelect]);
183
183
  const days = React.useMemo(() => {
184
184
  let offsetToFirst = new Date(displayedYear, displayedMonthIndex, 1).getDay();
185
- // if its sunday, show one week before
186
- if (0 === offsetToFirst) {
185
+ // If it's sunday, show one week before, but only if dates outside month are shown.
186
+ // (We do not want empty space at the top if dates outside month are not shown.)
187
+ if (0 === offsetToFirst && showDatesOutsideMonth) {
187
188
  offsetToFirst = 7;
188
189
  }
189
190
  const daysInMonth = [];
@@ -193,7 +194,7 @@ exports.DatePicker = React.forwardRef((props, forwardedRef) => {
193
194
  daysInMonth.push(new Date(displayedYear, displayedMonthIndex, adjustedDay));
194
195
  }
195
196
  return daysInMonth;
196
- }, [displayedMonthIndex, displayedYear]);
197
+ }, [displayedMonthIndex, displayedYear, showDatesOutsideMonth]);
197
198
  const weeks = React.useMemo(() => {
198
199
  const weeksInMonth = [];
199
200
  const weekCount = Math.ceil(days.length / 7);
@@ -383,6 +384,10 @@ exports.DatePicker = React.forwardRef((props, forwardedRef) => {
383
384
  return (React.createElement(index_js_1.Box, { as: 'div', key: `week-${displayedMonthIndex}-${weekIndex}`, ...weekProps, className: (0, classnames_1.default)('iui-calendar-week', weekProps?.className) }, weekDays.map((weekDay, dayIndex) => {
384
385
  const dateValue = weekDay.getDate();
385
386
  const isDisabled = isDateDisabled?.(weekDay);
387
+ const isOutsideMonth = weekDay.getMonth() !== displayedMonthIndex;
388
+ if (isOutsideMonth && !showDatesOutsideMonth) {
389
+ return (React.createElement(index_js_1.Box, { key: `day-${displayedMonthIndex}-${dayIndex}`, className: (0, classnames_1.default)(getDayClass(weekDay), dayProps?.className), "aria-hidden": true }));
390
+ }
386
391
  return (React.createElement(index_js_1.Box, { as: 'div', key: `day-${displayedMonthIndex}-${dayIndex}`, onClick: () => !isDisabled && onDayClick(weekDay), role: 'option', tabIndex: isSameDay(weekDay, focusedDay) ? 0 : -1, "aria-disabled": isDisabled ? 'true' : undefined, ref: (element) => isSameDay(weekDay, focusedDay) &&
387
392
  needFocus.current &&
388
393
  element?.focus(), ...dayProps, className: (0, classnames_1.default)(getDayClass(weekDay), dayProps?.className) }, dateValue));
@@ -110,7 +110,7 @@ exports.DialogMain = React.forwardRef((props, ref) => {
110
110
  }
111
111
  }, [isDraggable, onPointerDown]);
112
112
  // Prevents dialog from moving when window is being resized
113
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
113
+ (0, index_js_1.useLayoutEffect)(() => {
114
114
  if (!isDraggable || !isOpen) {
115
115
  return;
116
116
  }
@@ -32,7 +32,8 @@ type FileUploadTemplateProps = {
32
32
  children?: React.ReactNode;
33
33
  };
34
34
  /**
35
- * @deprecated Use `FileUploadCard` instead.
35
+ * @deprecated Use [`FileUploadCard`](https://itwinui.bentley.com/docs/fileupload#fileuploadcard) instead.
36
+ *
36
37
  * Default template to be used with the `FileUpload` wrapper component.
37
38
  * Contains a hidden input with styled labels (customizable).
38
39
  * @example
@@ -36,7 +36,8 @@ const classnames_1 = __importDefault(require("classnames"));
36
36
  const index_js_1 = require("../utils/index.js");
37
37
  const Anchor_js_1 = require("../Typography/Anchor.js");
38
38
  /**
39
- * @deprecated Use `FileUploadCard` instead.
39
+ * @deprecated Use [`FileUploadCard`](https://itwinui.bentley.com/docs/fileupload#fileuploadcard) instead.
40
+ *
40
41
  * Default template to be used with the `FileUpload` wrapper component.
41
42
  * Contains a hidden input with styled labels (customizable).
42
43
  * @example
@@ -1,3 +1,4 @@
1
+ import type { PolymorphicForwardRefComponent } from '../utils/props.js';
1
2
  /**
2
3
  * Polymorphic link action component.
3
4
  * It is rendered as `a` by default.
@@ -8,7 +9,7 @@
8
9
  * </Surface>
9
10
  * </LinkBox>
10
11
  */
11
- export declare const LinkAction: import("../utils/props.js").PolymorphicForwardRefComponent<"a", {}>;
12
+ export declare const LinkAction: PolymorphicForwardRefComponent<"a", {}>;
12
13
  /**
13
14
  * Polymorphic link box component.
14
15
  * Used to wrap around your component to use LinkAction.
@@ -20,4 +21,4 @@ export declare const LinkAction: import("../utils/props.js").PolymorphicForwardR
20
21
  * </Surface>
21
22
  * </LinkBox>
22
23
  */
23
- export declare const LinkBox: import("../utils/props.js").PolymorphicForwardRefComponent<"div", {}>;
24
+ export declare const LinkBox: PolymorphicForwardRefComponent<"div", {}>;
@@ -1,11 +1,40 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
2
28
  Object.defineProperty(exports, "__esModule", { value: true });
3
29
  exports.LinkBox = exports.LinkAction = void 0;
4
30
  /*---------------------------------------------------------------------------------------------
5
31
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
6
32
  * See LICENSE.md in the project root for license terms and full copyright notice.
7
33
  *--------------------------------------------------------------------------------------------*/
34
+ const React = __importStar(require("react"));
35
+ const classnames_1 = __importDefault(require("classnames"));
8
36
  const polymorphic_js_1 = require("../utils/functions/polymorphic.js");
37
+ const index_js_1 = require("../utils/components/index.js");
9
38
  /**
10
39
  * Polymorphic link action component.
11
40
  * It is rendered as `a` by default.
@@ -16,7 +45,10 @@ const polymorphic_js_1 = require("../utils/functions/polymorphic.js");
16
45
  * </Surface>
17
46
  * </LinkBox>
18
47
  */
19
- exports.LinkAction = polymorphic_js_1.polymorphic.a('iui-link-action');
48
+ exports.LinkAction = React.forwardRef((props, forwardedRef) => {
49
+ const { as: asProp = (!!props.href ? 'a' : 'button') } = props;
50
+ return (React.createElement(index_js_1.Box, { ...props, as: asProp, className: (0, classnames_1.default)('iui-link-action', props.className), ref: forwardedRef }));
51
+ });
20
52
  exports.LinkAction.displayName = 'LinkAction';
21
53
  /**
22
54
  * Polymorphic link box component.
@@ -42,6 +42,18 @@ type ModalProps = {
42
42
  * Content of the modal.
43
43
  */
44
44
  children: React.ReactNode;
45
+ /**
46
+ * Props for customizing the title bar element.
47
+ */
48
+ titleBarProps?: React.ComponentPropsWithRef<'div'>;
49
+ /**
50
+ * Props for customizing the dialog-wrapper element.
51
+ */
52
+ wrapperProps?: React.ComponentPropsWithoutRef<'div'>;
53
+ /**
54
+ * Props for customizing the backdrop element.
55
+ */
56
+ backdropProps?: React.ComponentPropsWithRef<'div'>;
45
57
  } & Pick<DialogMainProps, 'isOpen' | 'styleType'>;
46
58
  /**
47
59
  * Modal component which can wrap any content.
@@ -52,11 +52,11 @@ const Dialog_js_1 = require("../Dialog/Dialog.js");
52
52
  * </Modal>
53
53
  */
54
54
  exports.Modal = React.forwardRef((props, forwardedRef) => {
55
- const { isOpen = false, isDismissible = true, closeOnEsc = true, closeOnExternalClick = true, onClose, title, children, portal = true, ...rest } = props;
56
- return (React.createElement(Dialog_js_1.Dialog, { isOpen: isOpen, closeOnEsc: closeOnEsc, closeOnExternalClick: closeOnExternalClick, isDismissible: isDismissible, onClose: onClose, preventDocumentScroll: true, trapFocus: true, setFocus: true, ref: forwardedRef, portal: portal },
57
- React.createElement(Dialog_js_1.Dialog.Backdrop, null),
55
+ const { isOpen = false, isDismissible = true, closeOnEsc = true, closeOnExternalClick = true, onClose, title, children, portal = true, wrapperProps, backdropProps, titleBarProps, ...rest } = props;
56
+ return (React.createElement(Dialog_js_1.Dialog, { isOpen: isOpen, closeOnEsc: closeOnEsc, closeOnExternalClick: closeOnExternalClick, isDismissible: isDismissible, onClose: onClose, preventDocumentScroll: true, trapFocus: true, setFocus: true, ref: forwardedRef, portal: portal, ...wrapperProps },
57
+ React.createElement(Dialog_js_1.Dialog.Backdrop, { ...backdropProps }),
58
58
  React.createElement(Dialog_js_1.Dialog.Main, { "aria-modal": true, ...rest },
59
- React.createElement(Dialog_js_1.Dialog.TitleBar, { titleText: title }),
59
+ React.createElement(Dialog_js_1.Dialog.TitleBar, { titleText: title, ...titleBarProps }),
60
60
  children)));
61
61
  });
62
62
  exports.default = exports.Modal;
@@ -131,7 +131,7 @@ exports.Popover = React.forwardRef((props, forwardedRef) => {
131
131
  const popoverRef = (0, index_js_1.useMergedRefs)(popover.refs.setFloating, forwardedRef, setPopoverElement);
132
132
  const triggerId = `${(0, index_js_1.useId)()}-trigger`;
133
133
  const hasAriaLabel = !!props['aria-labelledby'] || !!props['aria-label'];
134
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
134
+ (0, index_js_1.useLayoutEffect)(() => {
135
135
  if (!positionReference) {
136
136
  return;
137
137
  }
@@ -23,7 +23,7 @@ type ProgressLinearProps = {
23
23
  /**
24
24
  * Status of progress.
25
25
  */
26
- status?: 'positive' | 'negative';
26
+ status?: 'positive' | 'negative' | 'warning';
27
27
  /**
28
28
  * Pass props to ProgressLinear label group.
29
29
  */
@@ -32,16 +32,24 @@ type ProgressLinearProps = {
32
32
  /**
33
33
  * Shows progress on a linear bar
34
34
  * @example
35
- * Determinate
35
+ * // Determinate
36
36
  * <ProgressLinear value={25}/>
37
- * Indeterminate
37
+ * // Indeterminate
38
38
  * <ProgressLinear indeterminate={true}/>
39
- * Labeled - Center
39
+ * // Labeled - Center
40
40
  * <ProgressLinear value={50} labels={['Centered Label']} />
41
- * Labeled - Left & Right
41
+ * // Labeled - Left & Right
42
42
  * <ProgressLinear value={50} labels={['Loading...', '50%']} />
43
- * Positive / Negative.
44
- * <ProgressLinear status='positive' labels={['Upload done!', <SvgStatusSuccessHollow />]} />
43
+ * // Status
44
+ * <ProgressLinear
45
+ * status='positive'
46
+ * labels={[
47
+ * 'Upload done!',
48
+ * <Icon key='icon'>
49
+ * <SvgStatusSuccess />{' '}
50
+ * </Icon>
51
+ * ]}
52
+ * />
45
53
  * <ProgressLinear status='negative' />
46
54
  */
47
55
  export declare const ProgressLinear: PolymorphicForwardRefComponent<"div", ProgressLinearProps>;
@@ -37,16 +37,24 @@ const index_js_1 = require("../utils/index.js");
37
37
  /**
38
38
  * Shows progress on a linear bar
39
39
  * @example
40
- * Determinate
40
+ * // Determinate
41
41
  * <ProgressLinear value={25}/>
42
- * Indeterminate
42
+ * // Indeterminate
43
43
  * <ProgressLinear indeterminate={true}/>
44
- * Labeled - Center
44
+ * // Labeled - Center
45
45
  * <ProgressLinear value={50} labels={['Centered Label']} />
46
- * Labeled - Left & Right
46
+ * // Labeled - Left & Right
47
47
  * <ProgressLinear value={50} labels={['Loading...', '50%']} />
48
- * Positive / Negative.
49
- * <ProgressLinear status='positive' labels={['Upload done!', <SvgStatusSuccessHollow />]} />
48
+ * // Status
49
+ * <ProgressLinear
50
+ * status='positive'
51
+ * labels={[
52
+ * 'Upload done!',
53
+ * <Icon key='icon'>
54
+ * <SvgStatusSuccess />{' '}
55
+ * </Icon>
56
+ * ]}
57
+ * />
50
58
  * <ProgressLinear status='negative' />
51
59
  */
52
60
  exports.ProgressLinear = React.forwardRef((props, forwardedRef) => {
@@ -14,7 +14,7 @@ type ProgressRadialProps = {
14
14
  /**
15
15
  * Status of Progress. Positive status always has 100% value.
16
16
  */
17
- status?: 'positive' | 'negative';
17
+ status?: 'positive' | 'negative' | 'warning';
18
18
  /**
19
19
  * Size of the progress indicator. Defaults to medium size.
20
20
  * @default ''
@@ -32,9 +32,10 @@ type ProgressRadialProps = {
32
32
  * <ProgressRadial value={50} />
33
33
  * Indeterminate
34
34
  * <ProgressRadial indeterminate />
35
- * Positive / Negative
35
+ * // Positive / Negative / Warning
36
36
  * <ProgressRadial status='positive' />
37
37
  * <ProgressRadial status='negative' />
38
+ * <ProgressRadial status='warning' />
38
39
  * Centered Content
39
40
  * <ProgressRadial value={63}>63</ProgressRadial>
40
41
  * Small
@@ -41,9 +41,10 @@ const index_js_1 = require("../utils/index.js");
41
41
  * <ProgressRadial value={50} />
42
42
  * Indeterminate
43
43
  * <ProgressRadial indeterminate />
44
- * Positive / Negative
44
+ * // Positive / Negative / Warning
45
45
  * <ProgressRadial status='positive' />
46
46
  * <ProgressRadial status='negative' />
47
+ * <ProgressRadial status='warning' />
47
48
  * Centered Content
48
49
  * <ProgressRadial value={63}>63</ProgressRadial>
49
50
  * Small
@@ -54,6 +55,7 @@ exports.ProgressRadial = React.forwardRef((props, forwardedRef) => {
54
55
  const statusMap = {
55
56
  negative: React.createElement(index_js_1.SvgImportantSmall, { "aria-hidden": true }),
56
57
  positive: React.createElement(index_js_1.SvgCheckmarkSmall, { "aria-hidden": true }),
58
+ warning: React.createElement(index_js_1.SvgImportantSmall, { "aria-hidden": true }),
57
59
  };
58
60
  return (React.createElement(index_js_1.Box, { className: (0, classnames_1.default)('iui-progress-indicator-radial', className), "data-iui-size": size, "data-iui-status": status, "data-iui-indeterminate": indeterminate ? 'true' : undefined, ref: forwardedRef, style: {
59
61
  ...(value !== undefined && {
@@ -357,7 +357,7 @@ const Table = (props) => {
357
357
  const [headerScrollWidth, setHeaderScrollWidth] = React.useState(0);
358
358
  const [headerClientWidth, setHeaderClientWidth] = React.useState(0);
359
359
  // Flexbox handles columns resize so we take new column widths before browser repaints.
360
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
360
+ (0, index_js_1.useLayoutEffect)(() => {
361
361
  if (state.isTableResizing) {
362
362
  const newColumnWidths = {};
363
363
  flatHeaders.forEach((column) => {
@@ -82,27 +82,27 @@ const Tab = React.forwardRef((props, forwardedRef) => {
82
82
  const isActive = activeValue === value;
83
83
  const isActiveRef = (0, index_js_1.useLatestRef)(isActive);
84
84
  // Scroll to active tab only on initial render
85
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
85
+ (0, index_js_1.useLayoutEffect)(() => {
86
86
  if (isActiveRef.current) {
87
87
  tabRef.current?.parentElement?.scrollTo({
88
88
  [orientation === 'horizontal' ? 'left' : 'top']: tabRef.current?.[orientation === 'horizontal' ? 'offsetLeft' : 'offsetTop'] - 4,
89
89
  behavior: 'instant', // not using 'smooth' to reduce layout shift on page load
90
90
  });
91
91
  }
92
- }, []);
93
- const updateStripe = () => {
94
- const currentTabRect = tabRef.current?.getBoundingClientRect();
95
- setStripeProperties({
96
- '--iui-tabs-stripe-size': orientation === 'horizontal'
97
- ? `${currentTabRect?.width}px`
98
- : `${currentTabRect?.height}px`,
99
- '--iui-tabs-stripe-position': orientation === 'horizontal'
100
- ? `${tabRef.current?.offsetLeft}px`
101
- : `${tabRef.current?.offsetTop}px`,
102
- });
103
- };
92
+ }, [isActiveRef, orientation]);
104
93
  // CSS custom properties to place the active stripe
105
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
94
+ (0, index_js_1.useLayoutEffect)(() => {
95
+ const updateStripe = () => {
96
+ const currentTabRect = tabRef.current?.getBoundingClientRect();
97
+ setStripeProperties({
98
+ '--iui-tabs-stripe-size': orientation === 'horizontal'
99
+ ? `${currentTabRect?.width}px`
100
+ : `${currentTabRect?.height}px`,
101
+ '--iui-tabs-stripe-position': orientation === 'horizontal'
102
+ ? `${tabRef.current?.offsetLeft}px`
103
+ : `${tabRef.current?.offsetTop}px`,
104
+ });
105
+ };
106
106
  if (type !== 'default' && isActive) {
107
107
  updateStripe();
108
108
  }
@@ -110,7 +110,8 @@ const Tab = React.forwardRef((props, forwardedRef) => {
110
110
  type,
111
111
  orientation,
112
112
  isActive,
113
- tabsWidth, // to fix visual artifact on initial render
113
+ tabsWidth,
114
+ setStripeProperties,
114
115
  ]);
115
116
  const onKeyDown = (event) => {
116
117
  if (event.altKey) {
@@ -186,7 +187,7 @@ TabLabel.displayName = 'Tabs.TabLabel';
186
187
  const TabDescription = React.forwardRef((props, ref) => {
187
188
  const { className, children, ...rest } = props;
188
189
  const { hasSublabel, setHasSublabel } = (0, index_js_1.useSafeContext)(TabsContext);
189
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
190
+ (0, index_js_1.useLayoutEffect)(() => {
190
191
  if (!hasSublabel) {
191
192
  setHasSublabel(true);
192
193
  }
@@ -2,22 +2,44 @@ import * as React from 'react';
2
2
  import type { PolymorphicForwardRefComponent } from '../utils/index.js';
3
3
  type TagProps = {
4
4
  /**
5
- * Callback function that handles click on close icon.
6
- * Close icon is shown only when this function is passed.
7
- * Use only with 'default' Tag.
5
+ * Text inside the tag.
6
+ */
7
+ children: React.ReactNode;
8
+ /**
9
+ * Callback invoked when the tag is clicked.
10
+ *
11
+ * When this prop is passed, the tag will be rendered as a button.
12
+ */
13
+ onClick?: React.MouseEventHandler;
14
+ /**
15
+ * Callback function that handles click on the remove ("❌") button.
16
+ * If not passed, the remove button will not be shown.
17
+ *
18
+ * If both `onClick` and `onRemove` are passed, then the tag label (rather than the tag itself)
19
+ * will be rendered as a button, to avoid invalid markup (nested buttons).
8
20
  */
9
21
  onRemove?: React.MouseEventHandler;
10
22
  /**
11
- * Text inside the tag.
23
+ * Props for customizing the remove ("❌") button.
12
24
  */
13
- children: React.ReactNode;
25
+ removeButtonProps?: React.ComponentPropsWithRef<'button'>;
26
+ } & ({
14
27
  /**
15
28
  * Variant of tag.
16
29
  * Basic tags don't have an outline.
17
30
  * @default 'default'
18
31
  */
19
- variant?: 'default' | 'basic';
20
- };
32
+ variant?: 'default';
33
+ /**
34
+ * Props for customizing the label.
35
+ *
36
+ * Only relevant for the 'default' Tag.
37
+ */
38
+ labelProps?: React.ComponentPropsWithRef<'span'>;
39
+ } | {
40
+ variant?: 'basic';
41
+ labelProps?: never;
42
+ });
21
43
  /**
22
44
  * Tag for showing categories, filters etc.
23
45
  * @example
@@ -35,6 +35,7 @@ const classnames_1 = __importDefault(require("classnames"));
35
35
  const React = __importStar(require("react"));
36
36
  const index_js_1 = require("../utils/index.js");
37
37
  const IconButton_js_1 = require("../Buttons/IconButton.js");
38
+ const LinkAction_js_1 = require("../LinkAction/LinkAction.js");
38
39
  /**
39
40
  * Tag for showing categories, filters etc.
40
41
  * @example
@@ -42,13 +43,19 @@ const IconButton_js_1 = require("../Buttons/IconButton.js");
42
43
  * <Tag variant='basic'>Basic tag</Tag>
43
44
  */
44
45
  exports.Tag = React.forwardRef((props, forwardedRef) => {
45
- const { className, variant = 'default', children, onRemove, ...rest } = props;
46
- return (React.createElement(index_js_1.Box, { as: 'span', className: (0, classnames_1.default)({
46
+ const { className, variant = 'default', children, onRemove, onClick, labelProps, removeButtonProps, ...rest } = props;
47
+ // If both onClick and onRemove are passed, we want to render the label as a button
48
+ // to avoid invalid markup (nested buttons). LinkAction ensures that clicking anywhere outside
49
+ // the remove button (including padding) will still trigger the main onClick callback.
50
+ const shouldUseLinkAction = !!onClick && !!onRemove;
51
+ return (React.createElement(index_js_1.Box, { as: shouldUseLinkAction ? LinkAction_js_1.LinkBox : !!onClick ? index_js_1.ButtonBase : 'span', className: (0, classnames_1.default)({
47
52
  'iui-tag-basic': variant === 'basic',
48
53
  'iui-tag': variant === 'default',
49
- }, className), ref: forwardedRef, ...rest },
50
- variant === 'default' ? (React.createElement(index_js_1.Box, { as: 'span', className: 'iui-tag-label' }, children)) : (children),
51
- onRemove && (React.createElement(IconButton_js_1.IconButton, { styleType: 'borderless', size: 'small', onClick: onRemove, "aria-label": 'Delete tag', className: 'iui-tag-button' },
54
+ }, className),
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ref's type doesn't matter internally
56
+ ref: forwardedRef, onClick: !shouldUseLinkAction ? onClick : undefined, ...rest },
57
+ variant === 'default' ? (React.createElement(index_js_1.Box, { as: (shouldUseLinkAction ? LinkAction_js_1.LinkAction : 'span'), onClick: shouldUseLinkAction ? onClick : undefined, ...labelProps, className: (0, classnames_1.default)('iui-tag-label', labelProps?.className) }, children)) : (children),
58
+ onRemove && (React.createElement(IconButton_js_1.IconButton, { styleType: 'borderless', size: 'small', onClick: onRemove, "aria-label": 'Delete tag', ...removeButtonProps, className: (0, classnames_1.default)('iui-tag-button', removeButtonProps?.className) },
52
59
  React.createElement(index_js_1.SvgCloseSmall, { "aria-hidden": true })))));
53
60
  });
54
61
  exports.default = exports.Tag;
@@ -116,7 +116,7 @@ const useParentThemeAndContext = (rootElement) => {
116
116
  const [parentThemeState, setParentTheme] = React.useState(parentContext?.theme);
117
117
  const [parentHighContrastState, setParentHighContrastState] = React.useState(parentContext?.themeOptions?.highContrast);
118
118
  const parentThemeRef = (0, index_js_1.useLatestRef)(parentContext?.theme);
119
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
119
+ (0, index_js_1.useLayoutEffect)(() => {
120
120
  // bail if we already have theme from context
121
121
  if (parentThemeRef.current) {
122
122
  return;
@@ -155,7 +155,7 @@ const useParentThemeAndContext = (rootElement) => {
155
155
  * dynamically import it (if possible) and fallback to loading it from a CDN.
156
156
  */
157
157
  const FallbackStyles = ({ root }) => {
158
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
158
+ (0, index_js_1.useLayoutEffect)(() => {
159
159
  // bail if styles are already loaded
160
160
  if (getComputedStyle(root).getPropertyValue('--_iui-v3-loaded') === 'yes') {
161
161
  return;
@@ -172,17 +172,17 @@ const useVirtualization = (props) => {
172
172
  const [resizeRef, resizeObserver] = (0, index_js_1.useResizeObserver)(onResize);
173
173
  // Find scrollable parent
174
174
  // Needed only on init
175
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
175
+ (0, index_js_1.useLayoutEffect)(() => {
176
176
  const scrollableParent = getScrollableParent(parentRef.current, parentRef.current?.ownerDocument);
177
177
  scrollContainer.current = scrollableParent;
178
178
  resizeRef(scrollableParent);
179
179
  }, [resizeRef]);
180
180
  // Stop watching resize, when virtual scroll is unmounted
181
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
181
+ (0, index_js_1.useLayoutEffect)(() => {
182
182
  return () => resizeObserver?.disconnect();
183
183
  }, [resizeObserver]);
184
184
  // Get child height when children available
185
- (0, index_js_1.useIsomorphicLayoutEffect)(() => updateChildHeight(), [updateChildHeight]);
185
+ (0, index_js_1.useLayoutEffect)(() => updateChildHeight(), [updateChildHeight]);
186
186
  const updateVirtualScroll = React.useCallback(() => {
187
187
  const scrollableContainer = getScrollableContainer();
188
188
  if (!scrollableContainer) {
@@ -215,7 +215,7 @@ const useVirtualization = (props) => {
215
215
  : scrollContainer.current.removeEventListener('scroll', onScrollRef.current);
216
216
  }, []);
217
217
  // Add event listener to the scrollable container.
218
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
218
+ (0, index_js_1.useLayoutEffect)(() => {
219
219
  removeScrollListener();
220
220
  onScrollRef.current = onScroll;
221
221
  if (!scrollContainer.current ||
@@ -227,7 +227,7 @@ const useVirtualization = (props) => {
227
227
  }
228
228
  return removeScrollListener;
229
229
  }, [onScroll, removeScrollListener]);
230
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
230
+ (0, index_js_1.useLayoutEffect)(() => {
231
231
  if (!isMounted) {
232
232
  return;
233
233
  }
@@ -282,8 +282,8 @@ const useVirtualization = (props) => {
282
282
  });
283
283
  }
284
284
  }
285
- }, [scrollToIndex, isMounted]);
286
- (0, index_js_1.useIsomorphicLayoutEffect)(() => {
285
+ }, [scrollToIndex, isMounted, itemsLength]);
286
+ (0, index_js_1.useLayoutEffect)(() => {
287
287
  if (!scrollContainerHeight) {
288
288
  return;
289
289
  }
@@ -2,6 +2,9 @@ import * as React from 'react';
2
2
  /**
3
3
  * SSR-safe version of `useLayoutEffect` that replaces it with `useEffect` on the server.
4
4
  *
5
+ * Exported as `useLayoutEffect` so that the react hooks linter correctly identifies the necessary dependencies.
6
+ *
5
7
  * @see https://fb.me/react-uselayouteffect-ssr
6
8
  */
7
- export declare const useIsomorphicLayoutEffect: typeof React.useEffect;
9
+ declare const useIsomorphicLayoutEffect: typeof React.useEffect;
10
+ export { useIsomorphicLayoutEffect as useLayoutEffect };
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.useIsomorphicLayoutEffect = void 0;
26
+ exports.useLayoutEffect = void 0;
27
27
  /*---------------------------------------------------------------------------------------------
28
28
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
29
29
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -32,6 +32,9 @@ const React = __importStar(require("react"));
32
32
  /**
33
33
  * SSR-safe version of `useLayoutEffect` that replaces it with `useEffect` on the server.
34
34
  *
35
+ * Exported as `useLayoutEffect` so that the react hooks linter correctly identifies the necessary dependencies.
36
+ *
35
37
  * @see https://fb.me/react-uselayouteffect-ssr
36
38
  */
37
- exports.useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
39
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
40
+ exports.useLayoutEffect = useIsomorphicLayoutEffect;
@@ -33,7 +33,7 @@ const index_js_1 = require("../functions/index.js");
33
33
  const useIsomorphicLayoutEffect_js_1 = require("./useIsomorphicLayoutEffect.js");
34
34
  const useMediaQuery = (queryString) => {
35
35
  const [matches, setMatches] = React.useState();
36
- (0, useIsomorphicLayoutEffect_js_1.useIsomorphicLayoutEffect)(() => {
36
+ (0, useIsomorphicLayoutEffect_js_1.useLayoutEffect)(() => {
37
37
  const mediaQueryList = (0, index_js_1.getWindow)()?.matchMedia?.(queryString);
38
38
  const handleChange = ({ matches }) => setMatches(matches);
39
39
  if (mediaQueryList != undefined) {