@itwin/itwinui-react 2.6.0 → 2.7.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 (46) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/cjs/core/Badge/Badge.d.ts +1 -1
  3. package/cjs/core/Badge/Badge.js +14 -15
  4. package/cjs/core/Carousel/Carousel.js +3 -2
  5. package/cjs/core/DatePicker/DatePicker.d.ts +5 -0
  6. package/cjs/core/DatePicker/DatePicker.js +26 -7
  7. package/cjs/core/Slider/Thumb.js +2 -4
  8. package/cjs/core/Tabs/Tabs.js +3 -1
  9. package/cjs/core/Tile/Tile.d.ts +24 -2
  10. package/cjs/core/Tile/Tile.js +85 -50
  11. package/cjs/core/ToggleSwitch/ToggleSwitch.d.ts +11 -31
  12. package/cjs/core/ToggleSwitch/ToggleSwitch.js +8 -1
  13. package/cjs/core/index.d.ts +2 -2
  14. package/cjs/core/index.js +3 -1
  15. package/cjs/core/utils/components/LinkAction.d.ts +30 -0
  16. package/cjs/core/utils/components/LinkAction.js +44 -0
  17. package/cjs/core/utils/components/index.d.ts +1 -0
  18. package/cjs/core/utils/components/index.js +1 -0
  19. package/cjs/core/utils/hooks/index.d.ts +1 -0
  20. package/cjs/core/utils/hooks/index.js +1 -0
  21. package/cjs/core/utils/hooks/useIsClient.d.ts +1 -0
  22. package/cjs/core/utils/hooks/useIsClient.js +19 -0
  23. package/cjs/core/utils/hooks/useTheme.js +5 -0
  24. package/esm/core/Badge/Badge.d.ts +1 -1
  25. package/esm/core/Badge/Badge.js +14 -15
  26. package/esm/core/Carousel/Carousel.js +3 -2
  27. package/esm/core/DatePicker/DatePicker.d.ts +5 -0
  28. package/esm/core/DatePicker/DatePicker.js +26 -7
  29. package/esm/core/Slider/Thumb.js +2 -4
  30. package/esm/core/Tabs/Tabs.js +4 -2
  31. package/esm/core/Tile/Tile.d.ts +24 -2
  32. package/esm/core/Tile/Tile.js +84 -49
  33. package/esm/core/ToggleSwitch/ToggleSwitch.d.ts +11 -31
  34. package/esm/core/ToggleSwitch/ToggleSwitch.js +8 -1
  35. package/esm/core/index.d.ts +2 -2
  36. package/esm/core/index.js +1 -1
  37. package/esm/core/utils/components/LinkAction.d.ts +30 -0
  38. package/esm/core/utils/components/LinkAction.js +38 -0
  39. package/esm/core/utils/components/index.d.ts +1 -0
  40. package/esm/core/utils/components/index.js +1 -0
  41. package/esm/core/utils/hooks/index.d.ts +1 -0
  42. package/esm/core/utils/hooks/index.js +1 -0
  43. package/esm/core/utils/hooks/useIsClient.d.ts +1 -0
  44. package/esm/core/utils/hooks/useIsClient.js +12 -0
  45. package/esm/core/utils/hooks/useTheme.js +5 -0
  46. package/package.json +2 -2
@@ -0,0 +1,30 @@
1
+ import { PolymorphicComponentProps, PolymorphicForwardRefComponent } from '../props';
2
+ import '@itwin/itwinui-css/css/utils.css';
3
+ declare type LinkActionOwnProps = {};
4
+ declare type LinkBoxOwnProps = {};
5
+ export declare type LinkActionProps = PolymorphicComponentProps<'a', LinkActionOwnProps>;
6
+ export declare type LinkBoxProps = PolymorphicComponentProps<'div', LinkBoxOwnProps>;
7
+ /**
8
+ * Polymorphic link action component.
9
+ * It is rendered as `a` by default.
10
+ * @example
11
+ * <LinkBox>
12
+ * <Surface>
13
+ * <LinkAction href='/new-page'>Whole card is clickable</LinkAction>
14
+ * </Surface>
15
+ * </LinkBox>
16
+ */
17
+ export declare const LinkAction: PolymorphicForwardRefComponent<"a", LinkActionOwnProps>;
18
+ /**
19
+ * Polymorphic link box component.
20
+ * Used to wrap around your component to use LinkAction.
21
+ * Rendered as `div` by default
22
+ * @example
23
+ * <LinkBox>
24
+ * <Surface>
25
+ * <LinkAction href='/new-page'>Whole card is clickable</LinkAction>
26
+ * </Surface>
27
+ * </LinkBox>
28
+ */
29
+ export declare const LinkBox: PolymorphicForwardRefComponent<"div", LinkBoxOwnProps>;
30
+ export {};
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LinkBox = exports.LinkAction = void 0;
7
+ /*---------------------------------------------------------------------------------------------
8
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
9
+ * See LICENSE.md in the project root for license terms and full copyright notice.
10
+ *--------------------------------------------------------------------------------------------*/
11
+ const react_1 = __importDefault(require("react"));
12
+ const classnames_1 = __importDefault(require("classnames"));
13
+ require("@itwin/itwinui-css/css/utils.css");
14
+ /**
15
+ * Polymorphic link action component.
16
+ * It is rendered as `a` by default.
17
+ * @example
18
+ * <LinkBox>
19
+ * <Surface>
20
+ * <LinkAction href='/new-page'>Whole card is clickable</LinkAction>
21
+ * </Surface>
22
+ * </LinkBox>
23
+ */
24
+ exports.LinkAction = react_1.default.forwardRef((props, ref) => {
25
+ const { as: Element = 'a', className, ...rest } = props;
26
+ return (react_1.default.createElement(Element, { ref: ref, className: (0, classnames_1.default)('iui-link-action', className), ...rest }));
27
+ });
28
+ exports.LinkAction.displayName = 'LinkAction';
29
+ /**
30
+ * Polymorphic link box component.
31
+ * Used to wrap around your component to use LinkAction.
32
+ * Rendered as `div` by default
33
+ * @example
34
+ * <LinkBox>
35
+ * <Surface>
36
+ * <LinkAction href='/new-page'>Whole card is clickable</LinkAction>
37
+ * </Surface>
38
+ * </LinkBox>
39
+ */
40
+ exports.LinkBox = react_1.default.forwardRef((props, ref) => {
41
+ const { as: Element = 'div', className, ...rest } = props;
42
+ return (react_1.default.createElement(Element, { ref: ref, className: (0, classnames_1.default)('iui-link-box', className), ...rest }));
43
+ });
44
+ exports.LinkBox.displayName = 'LinkBox';
@@ -8,3 +8,4 @@ export * from './VirtualScroll';
8
8
  export * from './VisuallyHidden';
9
9
  export * from './Icon';
10
10
  export * from './Flex';
11
+ export * from './LinkAction';
@@ -28,3 +28,4 @@ __exportStar(require("./VirtualScroll"), exports);
28
28
  __exportStar(require("./VisuallyHidden"), exports);
29
29
  __exportStar(require("./Icon"), exports);
30
30
  __exportStar(require("./Flex"), exports);
31
+ __exportStar(require("./LinkAction"), exports);
@@ -10,4 +10,5 @@ export * from './useSafeContext';
10
10
  export * from './useLatestRef';
11
11
  export * from './useIsomorphicLayoutEffect';
12
12
  export * from './useIsThemeAlreadySet';
13
+ export * from './useIsClient';
13
14
  export * from './useId';
@@ -30,4 +30,5 @@ __exportStar(require("./useSafeContext"), exports);
30
30
  __exportStar(require("./useLatestRef"), exports);
31
31
  __exportStar(require("./useIsomorphicLayoutEffect"), exports);
32
32
  __exportStar(require("./useIsThemeAlreadySet"), exports);
33
+ __exportStar(require("./useIsClient"), exports);
33
34
  __exportStar(require("./useId"), exports);
@@ -0,0 +1 @@
1
+ export declare const useIsClient: () => boolean;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.useIsClient = void 0;
7
+ /*---------------------------------------------------------------------------------------------
8
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
9
+ * See LICENSE.md in the project root for license terms and full copyright notice.
10
+ *--------------------------------------------------------------------------------------------*/
11
+ const react_1 = __importDefault(require("react"));
12
+ const useIsClient = () => {
13
+ const [isClient, setIsClient] = react_1.default.useState(false);
14
+ react_1.default.useEffect(() => {
15
+ setIsClient(true);
16
+ }, []);
17
+ return isClient;
18
+ };
19
+ exports.useIsClient = useIsClient;
@@ -27,6 +27,11 @@ const useTheme = (theme, themeOptions) => {
27
27
  if (!ownerDocument || isThemeAlreadySet.current) {
28
28
  return;
29
29
  }
30
+ // do nothing if using v1 for the main page (incremental migration)
31
+ if (ownerDocument.documentElement.className.includes('iui-theme-') ||
32
+ ownerDocument.body.classList.contains('iui-body')) {
33
+ return;
34
+ }
30
35
  ownerDocument.body.classList.toggle('iui-root', true);
31
36
  switch (theme) {
32
37
  case 'light':
@@ -10,7 +10,7 @@ export declare type BadgeProps = {
10
10
  *
11
11
  * If not specified, a default neutral background will be used.
12
12
  */
13
- backgroundColor?: 'primary' | 'positive' | 'negative' | 'warning' | keyof typeof SoftBackgrounds | AnyString;
13
+ backgroundColor?: 'primary' | 'informational' | 'positive' | 'negative' | 'warning' | keyof typeof SoftBackgrounds | AnyString;
14
14
  /**
15
15
  * Badge label.
16
16
  * Always gets converted to uppercase, and truncated if too long.
@@ -13,18 +13,14 @@ const getBadgeColorValue = (color) => {
13
13
  if (!color) {
14
14
  return '';
15
15
  }
16
- switch (color) {
17
- case 'primary':
18
- return '#A5D7F5';
19
- case 'positive':
20
- return '#C3E1AF';
21
- case 'negative':
22
- return '#EFA9A9';
23
- case 'warning':
24
- return '#F9D7AB';
25
- default:
26
- return isSoftBackground(color) ? SoftBackgrounds[color] : color;
27
- }
16
+ return isSoftBackground(color) ? SoftBackgrounds[color] : color;
17
+ };
18
+ /**
19
+ * Helper function that returns one of the preset badge status values.
20
+ */
21
+ const getStatusValue = (color) => {
22
+ const statuses = ['positive', 'negative', 'warning', 'informational'];
23
+ return color && statuses.includes(color) ? color : undefined;
28
24
  };
29
25
  /**
30
26
  * A colorful visual indicator for categorizing items.
@@ -36,12 +32,15 @@ const getBadgeColorValue = (color) => {
36
32
  export const Badge = (props) => {
37
33
  const { backgroundColor, style, className, children, ...rest } = props;
38
34
  useTheme();
39
- const _style = backgroundColor
35
+ // choosing 'primary' status should result in data-iui-status equaling 'informational'
36
+ const reducedBackgroundColor = backgroundColor === 'primary' ? 'informational' : backgroundColor;
37
+ const statusValue = getStatusValue(reducedBackgroundColor);
38
+ const _style = reducedBackgroundColor && !statusValue
40
39
  ? {
41
- '--iui-badge-background-color': getBadgeColorValue(backgroundColor),
40
+ '--iui-badge-background-color': getBadgeColorValue(reducedBackgroundColor),
42
41
  ...style,
43
42
  }
44
43
  : { ...style };
45
- return (React.createElement("span", { className: cx('iui-badge', className), style: _style, ...rest }, children));
44
+ return (React.createElement("span", { className: cx('iui-badge', className), style: _style, "data-iui-status": statusValue, ...rest }, children));
46
45
  };
47
46
  export default Badge;
@@ -60,8 +60,9 @@ export const Carousel = Object.assign(React.forwardRef((props, ref) => {
60
60
  if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
61
61
  return;
62
62
  }
63
- if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
64
- setKeysPressed((old) => ({ ...old, [event.key]: true }));
63
+ const key = event.key;
64
+ if (key === 'ArrowLeft' || key === 'ArrowRight') {
65
+ setKeysPressed((old) => ({ ...old, [key]: true }));
65
66
  }
66
67
  };
67
68
  const handleKeyUp = (event) => {
@@ -61,6 +61,11 @@ export declare type DatePickerProps = {
61
61
  * @default false
62
62
  */
63
63
  showYearSelection?: boolean;
64
+ /**
65
+ * Will disable dates for which this function returns true.
66
+ * Disabled dates cannot be selected.
67
+ */
68
+ isDateDisabled?: (date: Date) => boolean;
64
69
  } & DateRangePickerProps & Omit<TimePickerProps, 'date' | 'onChange' | 'setFocusHour'>;
65
70
  /**
66
71
  * Date picker component
@@ -123,7 +123,7 @@ export const generateLocalizedStrings = (locale) => {
123
123
  */
124
124
  export const DatePicker = (props) => {
125
125
  var _a, _b, _c, _d, _e, _f, _g, _h;
126
- const { date, onChange, localizedNames, className, style, setFocus = false, showTime = false, use12Hours = false, precision, hourStep, minuteStep, secondStep, useCombinedRenderer, combinedRenderer, hourRenderer, minuteRenderer, secondRenderer, meridiemRenderer, showYearSelection = false, enableRangeSelect = false, startDate, endDate, ...rest } = props;
126
+ const { date, onChange, localizedNames, className, style, setFocus = false, showTime = false, use12Hours = false, precision, hourStep, minuteStep, secondStep, useCombinedRenderer, combinedRenderer, hourRenderer, minuteRenderer, secondRenderer, meridiemRenderer, showYearSelection = false, enableRangeSelect = false, startDate, endDate, isDateDisabled, ...rest } = props;
127
127
  useTheme();
128
128
  const monthNames = (_a = localizedNames === null || localizedNames === void 0 ? void 0 : localizedNames.months) !== null && _a !== void 0 ? _a : defaultMonths;
129
129
  const shortDays = (_b = localizedNames === null || localizedNames === void 0 ? void 0 : localizedNames.shortDays) !== null && _b !== void 0 ? _b : defaultShortDays;
@@ -260,6 +260,10 @@ export const DatePicker = (props) => {
260
260
  }
261
261
  }
262
262
  };
263
+ const isPreviousMonthDisabled = isDateDisabled === null || isDateDisabled === void 0 ? void 0 : isDateDisabled(new Date(displayedYear, displayedMonthIndex, 0));
264
+ const isNextMonthDisabled = isDateDisabled === null || isDateDisabled === void 0 ? void 0 : isDateDisabled(new Date(displayedYear, displayedMonthIndex + 1, 1));
265
+ const isPreviousYearDisabled = isDateDisabled === null || isDateDisabled === void 0 ? void 0 : isDateDisabled(new Date(displayedYear - 1, 11, 31));
266
+ const isNextYearDisabled = isDateDisabled === null || isDateDisabled === void 0 ? void 0 : isDateDisabled(new Date(displayedYear + 1, 0, 1));
263
267
  const handleCalendarKeyDown = (event) => {
264
268
  if (!focusedDay) {
265
269
  return;
@@ -269,6 +273,9 @@ export const DatePicker = (props) => {
269
273
  case 'ArrowDown':
270
274
  adjustedFocusedDay.setDate(focusedDay.getDate() + 7);
271
275
  if (adjustedFocusedDay.getMonth() !== displayedMonthIndex) {
276
+ if (isNextMonthDisabled) {
277
+ return;
278
+ }
272
279
  handleMoveToNextMonth();
273
280
  }
274
281
  setFocusedDay(adjustedFocusedDay);
@@ -278,6 +285,9 @@ export const DatePicker = (props) => {
278
285
  case 'ArrowUp':
279
286
  adjustedFocusedDay.setDate(focusedDay.getDate() - 7);
280
287
  if (adjustedFocusedDay.getMonth() !== displayedMonthIndex) {
288
+ if (isPreviousMonthDisabled) {
289
+ return;
290
+ }
281
291
  handleMoveToPreviousMonth();
282
292
  }
283
293
  setFocusedDay(adjustedFocusedDay);
@@ -287,6 +297,9 @@ export const DatePicker = (props) => {
287
297
  case 'ArrowLeft':
288
298
  adjustedFocusedDay.setDate(focusedDay.getDate() - 1);
289
299
  if (adjustedFocusedDay.getMonth() !== displayedMonthIndex) {
300
+ if (isPreviousMonthDisabled) {
301
+ return;
302
+ }
290
303
  handleMoveToPreviousMonth();
291
304
  }
292
305
  setFocusedDay(adjustedFocusedDay);
@@ -296,6 +309,9 @@ export const DatePicker = (props) => {
296
309
  case 'ArrowRight':
297
310
  adjustedFocusedDay.setDate(focusedDay.getDate() + 1);
298
311
  if (adjustedFocusedDay.getMonth() !== displayedMonthIndex) {
312
+ if (isNextMonthDisabled) {
313
+ return;
314
+ }
299
315
  handleMoveToNextMonth();
300
316
  }
301
317
  setFocusedDay(adjustedFocusedDay);
@@ -305,7 +321,9 @@ export const DatePicker = (props) => {
305
321
  case 'Enter':
306
322
  case ' ':
307
323
  case 'Spacebar':
308
- onDayClick(focusedDay);
324
+ if (!(isDateDisabled === null || isDateDisabled === void 0 ? void 0 : isDateDisabled(focusedDay))) {
325
+ onDayClick(focusedDay);
326
+ }
309
327
  event.preventDefault();
310
328
  break;
311
329
  }
@@ -340,23 +358,24 @@ export const DatePicker = (props) => {
340
358
  return (React.createElement("div", { className: cx('iui-date-picker', className), style: style, ...rest },
341
359
  React.createElement("div", null,
342
360
  React.createElement("div", { className: 'iui-calendar-month-year' },
343
- showYearSelection && (React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToPreviousYear, "aria-label": 'Previous year', size: 'small' },
361
+ showYearSelection && (React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToPreviousYear, "aria-label": 'Previous year', size: 'small', disabled: isPreviousYearDisabled },
344
362
  React.createElement(SvgChevronLeftDouble, null))),
345
- React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToPreviousMonth, "aria-label": 'Previous month', size: 'small' },
363
+ React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToPreviousMonth, "aria-label": 'Previous month', size: 'small', disabled: isPreviousMonthDisabled },
346
364
  React.createElement(SvgChevronLeft, null)),
347
365
  React.createElement("span", { "aria-live": 'polite' },
348
366
  React.createElement("span", { className: 'iui-calendar-month', title: monthNames[displayedMonthIndex] }, monthNames[displayedMonthIndex]),
349
367
  "\u00A0",
350
368
  displayedYear),
351
- React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToNextMonth, "aria-label": 'Next month', size: 'small' },
369
+ React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToNextMonth, "aria-label": 'Next month', size: 'small', disabled: isNextMonthDisabled },
352
370
  React.createElement(SvgChevronRight, null)),
353
- showYearSelection && (React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToNextYear, "aria-label": 'Next year', size: 'small' },
371
+ showYearSelection && (React.createElement(IconButton, { styleType: 'borderless', onClick: handleMoveToNextYear, "aria-label": 'Next year', size: 'small', disabled: isNextYearDisabled },
354
372
  React.createElement(SvgChevronRightDouble, null)))),
355
373
  React.createElement("div", { className: 'iui-calendar-weekdays' }, shortDays.map((day, index) => (React.createElement("div", { key: day, title: longDays[index] }, day)))),
356
374
  React.createElement("div", { onKeyDown: handleCalendarKeyDown, role: 'listbox' }, weeks.map((weekDays, weekIndex) => {
357
375
  return (React.createElement("div", { key: `week-${displayedMonthIndex}-${weekIndex}`, className: 'iui-calendar-week' }, weekDays.map((weekDay, dayIndex) => {
358
376
  const dateValue = weekDay.getDate();
359
- return (React.createElement("div", { key: `day-${displayedMonthIndex}-${dayIndex}`, className: getDayClass(weekDay), onClick: () => onDayClick(weekDay), role: 'option', tabIndex: isSameDay(weekDay, focusedDay) ? 0 : -1, ref: (element) => isSameDay(weekDay, focusedDay) &&
377
+ const isDisabled = isDateDisabled === null || isDateDisabled === void 0 ? void 0 : isDateDisabled(weekDay);
378
+ return (React.createElement("div", { key: `day-${displayedMonthIndex}-${dayIndex}`, className: getDayClass(weekDay), onClick: () => !isDisabled && onDayClick(weekDay), role: 'option', tabIndex: isSameDay(weekDay, focusedDay) ? 0 : -1, "aria-disabled": isDisabled ? 'true' : undefined, ref: (element) => isSameDay(weekDay, focusedDay) &&
360
379
  needFocus.current &&
361
380
  (element === null || element === void 0 ? void 0 : element.focus()) }, dateValue));
362
381
  })));
@@ -40,8 +40,6 @@ export const Thumb = (props) => {
40
40
  const handlePointerDownOnThumb = React.useCallback(() => {
41
41
  !disabled && onThumbActivated(index);
42
42
  }, [disabled, index, onThumbActivated]);
43
- const [hasFocus, setHasFocus] = React.useState(false);
44
- const [isHovered, setIsHovered] = React.useState(false);
45
43
  const adjustedValue = React.useMemo(() => {
46
44
  if (value < sliderMin) {
47
45
  return sliderMin;
@@ -58,11 +56,11 @@ export const Thumb = (props) => {
58
56
  return (100.0 * (adjustedValue - sliderMin)) / (sliderMax - sliderMin);
59
57
  }, [adjustedValue, sliderMax, sliderMin]);
60
58
  const { style, className, ...rest } = thumbProps || {};
61
- return (React.createElement(Tooltip, { visible: isActive || hasFocus || isHovered, placement: 'top', ...tooltipProps },
59
+ return (React.createElement(Tooltip, { placement: 'top', trigger: (tooltipProps === null || tooltipProps === void 0 ? void 0 : tooltipProps.visible) == null ? 'mouseenter click focus' : undefined, ...tooltipProps },
62
60
  React.createElement("div", { ...rest, "data-index": index, ref: thumbRef, style: {
63
61
  ...style,
64
62
  ...(orientation === 'horizontal'
65
63
  ? { left: `${lowPercent}%` }
66
64
  : { bottom: `${lowPercent}%` }),
67
- }, className: cx('iui-slider-thumb', { 'iui-active': isActive }, className), role: 'slider', tabIndex: disabled ? undefined : 0, "aria-valuemin": minVal, "aria-valuenow": value, "aria-valuemax": maxVal, "aria-disabled": disabled, onPointerDown: handlePointerDownOnThumb, onKeyDown: (event) => handleOnKeyboardEvent(event, false), onKeyUp: (event) => handleOnKeyboardEvent(event, true), onFocus: () => setHasFocus(true), onBlur: () => setHasFocus(false), onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false) })));
65
+ }, className: cx('iui-slider-thumb', { 'iui-active': isActive }, className), role: 'slider', tabIndex: disabled ? undefined : 0, "aria-valuemin": minVal, "aria-valuenow": value, "aria-valuemax": maxVal, "aria-disabled": disabled, onPointerDown: handlePointerDownOnThumb, onKeyDown: (event) => handleOnKeyboardEvent(event, false), onKeyUp: (event) => handleOnKeyboardEvent(event, true) })));
68
66
  };
@@ -4,7 +4,7 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import cx from 'classnames';
6
6
  import React from 'react';
7
- import { useTheme, useMergedRefs, getBoundedValue, useContainerWidth, useIsomorphicLayoutEffect, } from '../utils';
7
+ import { useTheme, useMergedRefs, getBoundedValue, useContainerWidth, useIsomorphicLayoutEffect, useIsClient, } from '../utils';
8
8
  import '@itwin/itwinui-css/css/tabs.css';
9
9
  import { Tab } from './Tab';
10
10
  /**
@@ -44,6 +44,7 @@ export const Tabs = (props) => {
44
44
  }
45
45
  const { labels, activeIndex, onTabSelected, focusActivationMode = 'auto', type = 'default', color = 'blue', orientation = 'horizontal', tabsClassName, contentClassName, wrapperClassName, children, ...rest } = props;
46
46
  useTheme();
47
+ const isClient = useIsClient();
47
48
  const tablistRef = React.useRef(null);
48
49
  const [tablistSizeRef, tabsWidth] = useContainerWidth(type !== 'default');
49
50
  const refs = useMergedRefs(tablistRef, tablistSizeRef);
@@ -155,7 +156,8 @@ export const Tabs = (props) => {
155
156
  return (React.createElement("div", { className: cx('iui-tabs-wrapper', `iui-${orientation}`, wrapperClassName), style: stripeProperties },
156
157
  React.createElement("ul", { className: cx('iui-tabs', `iui-${type}`, {
157
158
  'iui-green': color === 'green',
158
- 'iui-animated': type !== 'default',
159
+ 'iui-animated': type !== 'default' && isClient,
160
+ 'iui-not-animated': type !== 'default' && !isClient,
159
161
  'iui-large': hasSublabel,
160
162
  }, tabsClassName), role: 'tablist', ref: refs, onKeyDown: onKeyDown, ...rest }, labels.map((label, index) => {
161
163
  const onClick = () => {
@@ -1,5 +1,16 @@
1
1
  import React from 'react';
2
+ import { PolymorphicComponentProps } from '../utils';
2
3
  import '@itwin/itwinui-css/css/tile.css';
4
+ declare type TileActionOwnProps = {};
5
+ /**
6
+ * Polymorphic Tile action component. Recommended to be used in a "name" of `Tile`.
7
+ * Renders `a` element by default.
8
+ * @example
9
+ * <Tile
10
+ * name={<Tile.Action href='/new-page'>Tile name<Tile.Action/>}
11
+ * />
12
+ */
13
+ export declare const TileAction: (props: PolymorphicComponentProps<'a', TileActionOwnProps>) => JSX.Element;
3
14
  export declare type TileProps = {
4
15
  /**
5
16
  * Name or title of the tile.
@@ -102,7 +113,8 @@ export declare type TileProps = {
102
113
  * @default false
103
114
  */
104
115
  isDisabled?: boolean;
105
- } & React.ComponentPropsWithoutRef<'div'>;
116
+ onClick?: React.MouseEventHandler<HTMLElement>;
117
+ } & Omit<React.ComponentPropsWithoutRef<'div'>, 'onClick'>;
106
118
  /**
107
119
  * Tile component that displays content and actions in a card-like format.
108
120
  * @example
@@ -120,5 +132,15 @@ export declare type TileProps = {
120
132
  * isNew={false}
121
133
  * />
122
134
  */
123
- export declare const Tile: (props: TileProps) => JSX.Element;
135
+ export declare const Tile: ((props: TileProps) => JSX.Element) & {
136
+ /**
137
+ * Polymorphic Tile action component. Recommended to be used in a "name" of `Tile`.
138
+ * Renders `a` element by default.
139
+ * @example
140
+ * <Tile
141
+ * name={<Tile.Action href='/new-page'>Tile name<Tile.Action/>}
142
+ * />
143
+ */
144
+ Action: (props: PolymorphicComponentProps<'a', TileActionOwnProps>) => JSX.Element;
145
+ };
124
146
  export default Tile;
@@ -4,11 +4,31 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import React from 'react';
6
6
  import cx from 'classnames';
7
- import { StatusIconMap, useTheme, SvgMore, SvgNew, SvgCheckmark, } from '../utils';
7
+ import { StatusIconMap, useTheme, SvgMore, SvgNew, SvgCheckmark, LinkAction, useSafeContext, getWindow, } from '../utils';
8
8
  import '@itwin/itwinui-css/css/tile.css';
9
9
  import { DropdownMenu } from '../DropdownMenu';
10
10
  import { IconButton } from '../Buttons';
11
11
  import { ProgressRadial } from '../ProgressIndicators';
12
+ const TileContext = React.createContext(undefined);
13
+ /**
14
+ * Polymorphic Tile action component. Recommended to be used in a "name" of `Tile`.
15
+ * Renders `a` element by default.
16
+ * @example
17
+ * <Tile
18
+ * name={<Tile.Action href='/new-page'>Tile name<Tile.Action/>}
19
+ * />
20
+ */
21
+ export const TileAction = (props) => {
22
+ var _a, _b, _c;
23
+ const tileContext = useSafeContext(TileContext);
24
+ const supportsHas = (_c = (_b = (_a = getWindow()) === null || _a === void 0 ? void 0 : _a.CSS) === null || _b === void 0 ? void 0 : _b.supports) === null || _c === void 0 ? void 0 : _c.call(_b, 'selector(:has(+ *))');
25
+ React.useEffect(() => {
26
+ if (!supportsHas) {
27
+ tileContext.setActionable(true);
28
+ }
29
+ }, [supportsHas, tileContext]);
30
+ return React.createElement(LinkAction, { ...props });
31
+ };
12
32
  /**
13
33
  * Tile component that displays content and actions in a card-like format.
14
34
  * @example
@@ -26,58 +46,73 @@ import { ProgressRadial } from '../ProgressIndicators';
26
46
  * isNew={false}
27
47
  * />
28
48
  */
29
- export const Tile = (props) => {
30
- const { className, name, description, metadata, thumbnail, buttons, leftIcon, rightIcon, badge, isNew, isSelected, moreOptions, variant = 'default', children, isActionable, status, isLoading = false, isDisabled = false, ...rest } = props;
49
+ export const Tile = Object.assign((props) => {
50
+ const { className, name, description, metadata, thumbnail, buttons, leftIcon, rightIcon, badge, isNew, isSelected, moreOptions, variant = 'default', children, isActionable: isActionableProp, status, isLoading = false, isDisabled = false, onClick, ...rest } = props;
31
51
  useTheme();
32
52
  const [isMenuVisible, setIsMenuVisible] = React.useState(false);
33
53
  const showMenu = React.useCallback(() => setIsMenuVisible(true), []);
34
54
  const hideMenu = React.useCallback(() => setIsMenuVisible(false), []);
35
- return (React.createElement("div", { className: cx('iui-tile', {
36
- 'iui-folder': variant === 'folder',
37
- 'iui-new': isNew,
38
- 'iui-selected': isSelected,
39
- 'iui-actionable': isActionable,
40
- [`iui-${status}`]: !!status,
41
- 'iui-loading': isLoading,
42
- }, className), "aria-disabled": isDisabled, tabIndex: isActionable && !isDisabled ? 0 : undefined, ...rest },
43
- thumbnail && (React.createElement("div", { className: 'iui-tile-thumbnail' },
44
- typeof thumbnail === 'string' ? (React.createElement("div", { className: 'iui-tile-thumbnail-picture', style: { backgroundImage: `url(${thumbnail})` } })) : thumbnail && thumbnail.type === 'img' ? (React.cloneElement(thumbnail, {
45
- className: 'iui-tile-thumbnail-picture',
46
- })) : React.isValidElement(thumbnail) ? (React.cloneElement(thumbnail, {
47
- className: cx('iui-thumbnail-icon', thumbnail.props.className),
48
- })) : (thumbnail),
49
- leftIcon &&
50
- React.cloneElement(leftIcon, {
51
- className: 'iui-tile-thumbnail-type-indicator',
52
- 'data-iui-size': 'small',
53
- }),
54
- rightIcon &&
55
- React.cloneElement(rightIcon, {
56
- className: 'iui-tile-thumbnail-quick-action',
57
- 'data-iui-size': 'small',
58
- }),
59
- badge && (React.createElement("div", { className: 'iui-tile-thumbnail-badge-container' }, badge)))),
60
- React.createElement("div", { className: 'iui-tile-content' },
61
- React.createElement("div", { className: 'iui-tile-name' },
62
- React.createElement(TitleIcon, { isLoading: isLoading, isSelected: isSelected, isNew: isNew, status: status }),
63
- React.createElement("span", { className: 'iui-tile-name-label' }, name)),
64
- description != undefined && (React.createElement("div", { className: 'iui-tile-description' }, description)),
65
- metadata != undefined && (React.createElement("div", { className: 'iui-tile-metadata' }, metadata)),
66
- moreOptions && (React.createElement(DropdownMenu, { onShow: showMenu, onHide: hideMenu, menuItems: (close) => moreOptions.map((option) => React.cloneElement(option, {
67
- onClick: (value) => {
68
- var _a, _b;
69
- close();
70
- (_b = (_a = option.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, value);
71
- },
72
- })) },
73
- React.createElement("div", { className: cx('iui-tile-more-options', {
74
- 'iui-visible': isMenuVisible,
75
- }) },
76
- React.createElement(IconButton, { styleType: 'borderless', size: 'small', "aria-label": 'More options' },
77
- React.createElement(SvgMore, null))))),
78
- children),
79
- buttons && React.createElement("div", { className: 'iui-tile-buttons' }, buttons)));
80
- };
55
+ const [localActionable, setLocalActionable] = React.useState(isActionableProp);
56
+ const isActionable = isActionableProp !== null && isActionableProp !== void 0 ? isActionableProp : localActionable;
57
+ const tileName = (React.createElement("div", { className: 'iui-tile-name' },
58
+ React.createElement(TitleIcon, { isLoading: isLoading, isSelected: isSelected, isNew: isNew, status: status }),
59
+ React.createElement("span", { className: 'iui-tile-name-label' }, isActionable && onClick ? (React.createElement(LinkAction, { as: 'button', onClick: !isDisabled ? onClick : undefined, "aria-disabled": isDisabled }, name)) : (name))));
60
+ return (React.createElement(TileContext.Provider, { value: { setActionable: setLocalActionable } },
61
+ React.createElement("div", { className: cx('iui-tile', {
62
+ 'iui-folder': variant === 'folder',
63
+ 'iui-new': isNew,
64
+ 'iui-selected': isSelected,
65
+ 'iui-actionable': isActionable,
66
+ [`iui-${status}`]: !!status,
67
+ 'iui-loading': isLoading,
68
+ }, className), "aria-disabled": isDisabled, ...rest },
69
+ variant !== 'folder' ? tileName : null,
70
+ thumbnail && (React.createElement("div", { className: 'iui-tile-thumbnail' },
71
+ typeof thumbnail === 'string' ? (React.createElement("div", { className: 'iui-tile-thumbnail-picture', style: { backgroundImage: `url(${thumbnail})` } })) : thumbnail && thumbnail.type === 'img' ? (React.cloneElement(thumbnail, {
72
+ className: 'iui-tile-thumbnail-picture',
73
+ })) : React.isValidElement(thumbnail) ? (React.cloneElement(thumbnail, {
74
+ className: cx('iui-thumbnail-icon', thumbnail.props.className),
75
+ })) : (thumbnail),
76
+ leftIcon &&
77
+ React.cloneElement(leftIcon, {
78
+ className: 'iui-tile-thumbnail-type-indicator',
79
+ 'data-iui-size': 'small',
80
+ }),
81
+ rightIcon &&
82
+ React.cloneElement(rightIcon, {
83
+ className: 'iui-tile-thumbnail-quick-action',
84
+ 'data-iui-size': 'small',
85
+ }),
86
+ badge && (React.createElement("div", { className: 'iui-tile-thumbnail-badge-container' }, badge)))),
87
+ React.createElement("div", { className: 'iui-tile-content' },
88
+ variant === 'folder' ? tileName : null,
89
+ description != undefined && (React.createElement("div", { className: 'iui-tile-description' }, description)),
90
+ metadata != undefined && (React.createElement("div", { className: 'iui-tile-metadata' }, metadata)),
91
+ moreOptions && (React.createElement(DropdownMenu, { onShow: showMenu, onHide: hideMenu, menuItems: (close) => moreOptions.map((option) => React.cloneElement(option, {
92
+ onClick: (value) => {
93
+ var _a, _b;
94
+ close();
95
+ (_b = (_a = option.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, value);
96
+ },
97
+ })) },
98
+ React.createElement("div", { className: cx('iui-tile-more-options', {
99
+ 'iui-visible': isMenuVisible,
100
+ }) },
101
+ React.createElement(IconButton, { styleType: 'borderless', size: 'small', "aria-label": 'More options' },
102
+ React.createElement(SvgMore, null))))),
103
+ children),
104
+ buttons && React.createElement("div", { className: 'iui-tile-buttons' }, buttons))));
105
+ }, {
106
+ /**
107
+ * Polymorphic Tile action component. Recommended to be used in a "name" of `Tile`.
108
+ * Renders `a` element by default.
109
+ * @example
110
+ * <Tile
111
+ * name={<Tile.Action href='/new-page'>Tile name<Tile.Action/>}
112
+ * />
113
+ */
114
+ Action: TileAction,
115
+ });
81
116
  const TitleIcon = ({ isLoading = false, isSelected = false, isNew = false, status, }) => {
82
117
  const StatusIcon = !!status && StatusIconMap[status];
83
118
  if (isLoading) {