@itwin/itwinui-react 3.0.10 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +1 -1
- package/cjs/core/ButtonGroup/ButtonGroup.d.ts +2 -0
- package/cjs/core/ButtonGroup/ButtonGroup.js +26 -21
- package/cjs/core/Buttons/IconButton.d.ts +6 -0
- package/cjs/core/Buttons/IconButton.js +4 -2
- package/cjs/core/Carousel/Carousel.d.ts +110 -2
- package/cjs/core/Carousel/CarouselNavigation.d.ts +110 -2
- package/cjs/core/ComboBox/ComboBoxInput.js +17 -5
- package/cjs/core/FileUpload/FileUpload.d.ts +3 -1
- package/cjs/core/FileUpload/FileUpload.js +5 -8
- package/cjs/core/InputWithDecorations/InputWithDecorations.d.ts +55 -1
- package/cjs/core/Popover/Popover.d.ts +22 -14
- package/cjs/core/Popover/Popover.js +14 -2
- package/cjs/core/Table/Table.js +28 -2
- package/cjs/core/Tabs/Tabs.js +23 -3
- package/cjs/core/ThemeProvider/ThemeProvider.js +44 -21
- package/cjs/core/Tile/Tile.d.ts +61 -1
- package/cjs/core/Tooltip/Tooltip.js +6 -2
- package/cjs/core/Tree/Tree.d.ts +6 -0
- package/cjs/core/Tree/Tree.js +4 -3
- package/cjs/core/Tree/TreeContext.d.ts +4 -0
- package/cjs/core/Tree/TreeNodeExpander.js +4 -1
- package/cjs/core/utils/icons/SvgChevronRightSmall.d.ts +2 -0
- package/cjs/core/utils/icons/SvgChevronRightSmall.js +15 -0
- package/cjs/core/utils/icons/index.d.ts +1 -0
- package/cjs/core/utils/icons/index.js +1 -0
- package/cjs/styles.js +2 -2
- package/esm/core/ButtonGroup/ButtonGroup.d.ts +2 -0
- package/esm/core/ButtonGroup/ButtonGroup.js +25 -20
- package/esm/core/Buttons/IconButton.d.ts +6 -0
- package/esm/core/Buttons/IconButton.js +4 -2
- package/esm/core/Carousel/Carousel.d.ts +110 -2
- package/esm/core/Carousel/CarouselNavigation.d.ts +110 -2
- package/esm/core/ComboBox/ComboBoxInput.js +17 -5
- package/esm/core/FileUpload/FileUpload.d.ts +3 -1
- package/esm/core/FileUpload/FileUpload.js +6 -9
- package/esm/core/InputWithDecorations/InputWithDecorations.d.ts +55 -1
- package/esm/core/Popover/Popover.d.ts +22 -14
- package/esm/core/Popover/Popover.js +15 -3
- package/esm/core/Table/Table.js +28 -2
- package/esm/core/Tabs/Tabs.js +23 -3
- package/esm/core/ThemeProvider/ThemeProvider.js +45 -22
- package/esm/core/Tile/Tile.d.ts +61 -1
- package/esm/core/Tooltip/Tooltip.js +6 -2
- package/esm/core/Tree/Tree.d.ts +6 -0
- package/esm/core/Tree/Tree.js +4 -3
- package/esm/core/Tree/TreeContext.d.ts +4 -0
- package/esm/core/Tree/TreeNodeExpander.js +5 -2
- package/esm/core/utils/icons/SvgChevronRightSmall.d.ts +2 -0
- package/esm/core/utils/icons/SvgChevronRightSmall.js +10 -0
- package/esm/core/utils/icons/index.d.ts +1 -0
- package/esm/core/utils/icons/index.js +1 -0
- package/esm/styles.js +2 -2
- package/package.json +4 -4
- package/styles.css +15 -15
|
@@ -14,7 +14,8 @@ const Portal_js_1 = require("../utils/components/Portal.js");
|
|
|
14
14
|
const ThemeProvider_js_1 = require("../ThemeProvider/ThemeProvider.js");
|
|
15
15
|
// ----------------------------------------------------------------------------
|
|
16
16
|
const usePopover = (options) => {
|
|
17
|
-
const { placement = 'bottom-start', visible, onVisibleChange, closeOnOutsideClick, autoUpdateOptions,
|
|
17
|
+
const { placement = 'bottom-start', visible, onVisibleChange, closeOnOutsideClick, autoUpdateOptions, matchWidth, trigger = { click: true, hover: false, focus: false }, role, } = options;
|
|
18
|
+
const middleware = { flip: true, shift: true, ...options.middleware };
|
|
18
19
|
const [open, onOpenChange] = (0, index_js_1.useControlledState)(false, visible, onVisibleChange);
|
|
19
20
|
const floating = (0, react_1.useFloating)({
|
|
20
21
|
placement,
|
|
@@ -86,7 +87,10 @@ exports.Popover = React.forwardRef((props, forwardedRef) => {
|
|
|
86
87
|
const { portal = true,
|
|
87
88
|
//
|
|
88
89
|
// popover options
|
|
89
|
-
visible, placement = 'bottom-start', onVisibleChange, closeOnOutsideClick = true,
|
|
90
|
+
visible, placement = 'bottom-start', onVisibleChange, closeOnOutsideClick = true, middleware,
|
|
91
|
+
//
|
|
92
|
+
// extra props
|
|
93
|
+
positionReference,
|
|
90
94
|
//
|
|
91
95
|
// dom props
|
|
92
96
|
className, children, content, applyBackground = false, ...rest } = props;
|
|
@@ -96,11 +100,19 @@ exports.Popover = React.forwardRef((props, forwardedRef) => {
|
|
|
96
100
|
onVisibleChange,
|
|
97
101
|
closeOnOutsideClick,
|
|
98
102
|
role: 'dialog',
|
|
103
|
+
middleware,
|
|
99
104
|
});
|
|
100
105
|
const [popoverElement, setPopoverElement] = React.useState();
|
|
101
106
|
const popoverRef = (0, index_js_1.useMergedRefs)(popover.refs.setFloating, forwardedRef, setPopoverElement);
|
|
102
107
|
const triggerId = `${(0, index_js_1.useId)()}-trigger`;
|
|
103
108
|
const hasAriaLabel = !!props['aria-labelledby'] || !!props['aria-label'];
|
|
109
|
+
(0, index_js_1.useIsomorphicLayoutEffect)(() => {
|
|
110
|
+
if (!positionReference) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
popover.refs.setPositionReference(positionReference);
|
|
114
|
+
return () => void popover.refs.setPositionReference(null);
|
|
115
|
+
}, [popover.refs, positionReference]);
|
|
104
116
|
return (React.createElement(React.Fragment, null,
|
|
105
117
|
(0, index_js_1.cloneElementWithRef)(children, (children) => ({
|
|
106
118
|
id: children.props.id || triggerId,
|
package/cjs/core/Table/Table.js
CHANGED
|
@@ -7,6 +7,7 @@ const tslib_1 = require("tslib");
|
|
|
7
7
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
8
8
|
*--------------------------------------------------------------------------------------------*/
|
|
9
9
|
const React = tslib_1.__importStar(require("react"));
|
|
10
|
+
const ReactDOM = tslib_1.__importStar(require("react-dom"));
|
|
10
11
|
const classnames_1 = tslib_1.__importDefault(require("classnames"));
|
|
11
12
|
const react_table_1 = require("react-table");
|
|
12
13
|
const ProgressRadial_js_1 = require("../ProgressIndicators/ProgressRadial.js");
|
|
@@ -465,11 +466,19 @@ const Table = (props) => {
|
|
|
465
466
|
updateStickyState();
|
|
466
467
|
}
|
|
467
468
|
}, tabIndex: -1, "aria-multiselectable": (isSelectable && selectionMode === 'multi') || undefined },
|
|
469
|
+
React.createElement(ShadowTemplate, null,
|
|
470
|
+
React.createElement("slot", null),
|
|
471
|
+
React.createElement("div", { "aria-hidden": true, style: {
|
|
472
|
+
// This ensures that the table-body is always the same width as the table-header,
|
|
473
|
+
// even if the table has no rows. See https://github.com/iTwin/iTwinUI/pull/1725
|
|
474
|
+
width: headerRef.current?.scrollWidth,
|
|
475
|
+
height: 0.1,
|
|
476
|
+
} })),
|
|
468
477
|
data.length !== 0 && (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualScroll_js_1.default, { itemsLength: page.length, itemRenderer: virtualizedItemRenderer, scrollToIndex: scrollToIndex })) : (page.map((_, index) => getPreparedRow(index))))),
|
|
469
478
|
isLoading && data.length === 0 && (React.createElement(index_js_1.Box, { as: 'div', ...emptyTableContentProps, className: (0, classnames_1.default)('iui-table-empty', emptyTableContentProps?.className) },
|
|
470
479
|
React.createElement(ProgressRadial_js_1.ProgressRadial, { indeterminate: true }))),
|
|
471
|
-
isLoading && data.length !== 0 && (React.createElement(index_js_1.Box, { className: 'iui-table-row' },
|
|
472
|
-
React.createElement(index_js_1.Box, { className: 'iui-table-cell'
|
|
480
|
+
isLoading && data.length !== 0 && (React.createElement(index_js_1.Box, { className: 'iui-table-row', "data-iui-loading": 'true' },
|
|
481
|
+
React.createElement(index_js_1.Box, { className: 'iui-table-cell' },
|
|
473
482
|
React.createElement(ProgressRadial_js_1.ProgressRadial, { indeterminate: true, size: 'small' })))),
|
|
474
483
|
!isLoading && data.length === 0 && !areFiltersSet && (React.createElement(index_js_1.Box, { as: 'div', ...emptyTableContentProps, className: (0, classnames_1.default)('iui-table-empty', emptyTableContentProps?.className) },
|
|
475
484
|
React.createElement("div", null, emptyTableContent))),
|
|
@@ -481,3 +490,20 @@ const Table = (props) => {
|
|
|
481
490
|
};
|
|
482
491
|
exports.Table = Table;
|
|
483
492
|
exports.default = exports.Table;
|
|
493
|
+
// ----------------------------------------------------------------------------
|
|
494
|
+
/**
|
|
495
|
+
* Wrapper around `<template>` element that attaches shadow root to its parent
|
|
496
|
+
* and moves its children into the shadow root.
|
|
497
|
+
*/
|
|
498
|
+
const ShadowTemplate = ({ children }) => {
|
|
499
|
+
const [shadowRoot, setShadowRoot] = React.useState();
|
|
500
|
+
const attachShadowRef = React.useCallback((template) => {
|
|
501
|
+
const parent = template?.parentElement;
|
|
502
|
+
if (!template || !parent || parent.shadowRoot) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
setShadowRoot(parent.attachShadow({ mode: 'open' }));
|
|
506
|
+
template.remove();
|
|
507
|
+
}, []);
|
|
508
|
+
return (React.createElement("template", { ref: attachShadowRef }, shadowRoot && ReactDOM.createPortal(children, shadowRoot)));
|
|
509
|
+
};
|
package/cjs/core/Tabs/Tabs.js
CHANGED
|
@@ -37,7 +37,7 @@ const TabList = React.forwardRef((props, ref) => {
|
|
|
37
37
|
const isClient = (0, index_js_1.useIsClient)();
|
|
38
38
|
const tablistRef = React.useRef(null);
|
|
39
39
|
const [tablistSizeRef, tabsWidth] = (0, index_js_1.useContainerWidth)(type !== 'default');
|
|
40
|
-
const refs = (0, index_js_1.useMergedRefs)(ref, tablistRef, tablistSizeRef);
|
|
40
|
+
const refs = (0, index_js_1.useMergedRefs)(ref, tablistRef, tablistSizeRef, useScrollbarGutter());
|
|
41
41
|
return (React.createElement(index_js_1.Box, { className: (0, classnames_1.default)('iui-tabs', `iui-${type}`, {
|
|
42
42
|
'iui-green': color === 'green',
|
|
43
43
|
'iui-animated': type !== 'default' && isClient,
|
|
@@ -138,7 +138,7 @@ const Tab = React.forwardRef((props, forwardedRef) => {
|
|
|
138
138
|
setActiveValue(value);
|
|
139
139
|
}
|
|
140
140
|
}, [activeValue, setActiveValue, value]);
|
|
141
|
-
return (React.createElement(index_js_1.ButtonBase, { className: (0, classnames_1.default)('iui-tab', className), role: 'tab', tabIndex: isActive ? 0 : -1, "aria-selected": isActive, "aria-controls": `${idPrefix}-panel-${value}`, ref: (0, index_js_1.useMergedRefs)(tabRef, forwardedRef, setInitialActiveRef), ...rest, id: `${idPrefix}-tab-${value}`, onClick: (0, index_js_1.mergeEventHandlers)(props.onClick, () => setActiveValue(value)), onKeyDown: (0, index_js_1.mergeEventHandlers)(props.onKeyDown, onKeyDown), onFocus: (0, index_js_1.mergeEventHandlers)(props.onFocus, () => {
|
|
141
|
+
return (React.createElement(index_js_1.ButtonBase, { className: (0, classnames_1.default)('iui-tab', className), role: 'tab', tabIndex: isActive ? 0 : -1, "aria-selected": isActive, "aria-controls": `${idPrefix}-panel-${value.replaceAll(' ', '-')}`, ref: (0, index_js_1.useMergedRefs)(tabRef, forwardedRef, setInitialActiveRef), ...rest, id: `${idPrefix}-tab-${value.replaceAll(' ', '-')}`, onClick: (0, index_js_1.mergeEventHandlers)(props.onClick, () => setActiveValue(value)), onKeyDown: (0, index_js_1.mergeEventHandlers)(props.onKeyDown, onKeyDown), onFocus: (0, index_js_1.mergeEventHandlers)(props.onFocus, () => {
|
|
142
142
|
tabRef.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
143
143
|
if (focusActivationMode === 'auto' && !props.disabled) {
|
|
144
144
|
setActiveValue(value);
|
|
@@ -178,7 +178,7 @@ TabsActions.displayName = 'Tabs.Actions';
|
|
|
178
178
|
const TabsPanel = React.forwardRef((props, ref) => {
|
|
179
179
|
const { value, className, children, ...rest } = props;
|
|
180
180
|
const { activeValue, idPrefix } = (0, index_js_1.useSafeContext)(TabsContext);
|
|
181
|
-
return (React.createElement(index_js_1.Box, { className: (0, classnames_1.default)('iui-tabs-content', className), "aria-labelledby": `${idPrefix}-tab-${value}`, role: 'tabpanel', hidden: activeValue !== value ? true : undefined, ref: ref, ...rest, id: `${idPrefix}-panel-${value}` }, children));
|
|
181
|
+
return (React.createElement(index_js_1.Box, { className: (0, classnames_1.default)('iui-tabs-content', className), "aria-labelledby": `${idPrefix}-tab-${value.replaceAll(' ', '-')}`, role: 'tabpanel', hidden: activeValue !== value ? true : undefined, ref: ref, ...rest, id: `${idPrefix}-panel-${value.replaceAll(' ', '-')}` }, children));
|
|
182
182
|
});
|
|
183
183
|
TabsPanel.displayName = 'Tabs.Panel';
|
|
184
184
|
const LegacyTabsComponent = React.forwardRef((props, forwardedRef) => {
|
|
@@ -323,3 +323,23 @@ exports.Tabs = Object.assign(LegacyTabsComponent, {
|
|
|
323
323
|
const TabsContext = React.createContext(undefined);
|
|
324
324
|
const TabListContext = React.createContext(undefined);
|
|
325
325
|
exports.default = exports.Tabs;
|
|
326
|
+
// ----------------------------------------------------------------------------
|
|
327
|
+
/**
|
|
328
|
+
* This conditionally adds `scrollbar-gutter: stable` only if the content overflows.
|
|
329
|
+
* This is a workaround to prevent layout shift that happens because of scrollbar width.
|
|
330
|
+
*
|
|
331
|
+
* @see https://github.com/iTwin/iTwinUI/issues/1627
|
|
332
|
+
*/
|
|
333
|
+
const useScrollbarGutter = () => {
|
|
334
|
+
return React.useCallback((element) => {
|
|
335
|
+
if (element) {
|
|
336
|
+
if (element.scrollHeight > element.clientHeight) {
|
|
337
|
+
element.style.scrollbarGutter = 'stable';
|
|
338
|
+
// Safari fallback
|
|
339
|
+
if (!CSS.supports('scrollbar-gutter: stable')) {
|
|
340
|
+
element.style.overflowY = 'scroll';
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}, []);
|
|
345
|
+
};
|
|
@@ -42,27 +42,26 @@ const Toaster_js_1 = require("../Toast/Toaster.js");
|
|
|
42
42
|
*/
|
|
43
43
|
exports.ThemeProvider = React.forwardRef((props, forwardedRef) => {
|
|
44
44
|
const { theme: themeProp = 'inherit', children, themeOptions = {}, portalContainer: portalContainerProp, ...rest } = props;
|
|
45
|
-
const [
|
|
46
|
-
const
|
|
45
|
+
const [rootElement, setRootElement] = React.useState(null);
|
|
46
|
+
const parent = useParentThemeAndContext(rootElement);
|
|
47
|
+
const theme = themeProp === 'inherit' ? parent.theme || 'light' : themeProp;
|
|
47
48
|
// default apply background only for topmost ThemeProvider
|
|
48
|
-
themeOptions.applyBackground ?? (themeOptions.applyBackground = !
|
|
49
|
+
themeOptions.applyBackground ?? (themeOptions.applyBackground = !parent.theme);
|
|
49
50
|
// default inherit highContrast option from parent if also inheriting base theme
|
|
50
|
-
themeOptions.highContrast ?? (themeOptions.highContrast = themeProp === 'inherit'
|
|
51
|
-
? parentContext?.themeOptions?.highContrast
|
|
52
|
-
: undefined);
|
|
51
|
+
themeOptions.highContrast ?? (themeOptions.highContrast = themeProp === 'inherit' ? parent.highContrast : undefined);
|
|
53
52
|
/**
|
|
54
53
|
* We will portal our portal container into `portalContainer` prop (if specified),
|
|
55
54
|
* or inherit `portalContainer` from context (if also inheriting theme).
|
|
56
55
|
*/
|
|
57
56
|
const portaledPortalContainer = portalContainerProp ||
|
|
58
|
-
(themeProp === 'inherit' ?
|
|
57
|
+
(themeProp === 'inherit' ? parent.context?.portalContainer : undefined);
|
|
59
58
|
const [portalContainer, setPortalContainer] = (0, index_js_1.useControlledState)(null, portaledPortalContainer);
|
|
60
59
|
const contextValue = React.useMemo(() => ({ theme, themeOptions, portalContainer }),
|
|
61
60
|
// we do include all dependencies below, but we want to stringify the objects as they could be different on each render
|
|
62
61
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
63
62
|
[theme, JSON.stringify(themeOptions), portalContainer]);
|
|
64
63
|
return (React.createElement(ThemeContext_js_1.ThemeContext.Provider, { value: contextValue },
|
|
65
|
-
React.createElement(Root, { theme: theme, themeOptions: themeOptions, ref: (0, index_js_1.useMergedRefs)(forwardedRef,
|
|
64
|
+
React.createElement(Root, { theme: theme, themeOptions: themeOptions, ref: (0, index_js_1.useMergedRefs)(forwardedRef, setRootElement), ...rest },
|
|
66
65
|
React.createElement(Toaster_js_1.ToastProvider, null,
|
|
67
66
|
children,
|
|
68
67
|
portaledPortalContainer ? (ReactDOM.createPortal(React.createElement(Toaster_js_1.Toaster, null), portaledPortalContainer)) : (React.createElement("div", { ref: setPortalContainer, style: { display: 'contents' } },
|
|
@@ -81,22 +80,46 @@ const Root = React.forwardRef((props, forwardedRef) => {
|
|
|
81
80
|
});
|
|
82
81
|
// ----------------------------------------------------------------------------
|
|
83
82
|
/**
|
|
84
|
-
* Returns theme from either parent
|
|
83
|
+
* Returns theme information from either parent ThemeContext or by reading the closest
|
|
85
84
|
* data-iui-theme attribute if context is not found.
|
|
85
|
+
*
|
|
86
|
+
* Also returns the ThemeContext itself (if found).
|
|
86
87
|
*/
|
|
87
|
-
const
|
|
88
|
+
const useParentThemeAndContext = (rootElement) => {
|
|
88
89
|
const parentContext = React.useContext(ThemeContext_js_1.ThemeContext);
|
|
89
|
-
const rootRef = React.useRef(null);
|
|
90
90
|
const [parentThemeState, setParentTheme] = React.useState(parentContext?.theme);
|
|
91
|
+
const [parentHighContrastState, setParentHighContrastState] = React.useState(parentContext?.themeOptions?.highContrast);
|
|
92
|
+
const parentThemeRef = (0, index_js_1.useLatestRef)(parentContext?.theme);
|
|
91
93
|
(0, index_js_1.useIsomorphicLayoutEffect)(() => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
// bail if we already have theme from context
|
|
95
|
+
if (parentThemeRef.current) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// find parent theme from closest data-iui-theme attribute
|
|
99
|
+
const closestRoot = rootElement?.parentElement?.closest('[data-iui-theme]');
|
|
100
|
+
if (!closestRoot) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// helper function that updates state to match data attributes from closest root
|
|
104
|
+
const synchronizeTheme = () => {
|
|
105
|
+
setParentTheme(closestRoot?.getAttribute('data-iui-theme'));
|
|
106
|
+
setParentHighContrastState(closestRoot?.getAttribute('data-iui-contrast') === 'high');
|
|
107
|
+
};
|
|
108
|
+
// set theme for initial mount
|
|
109
|
+
synchronizeTheme();
|
|
110
|
+
// use mutation observers to listen to future updates to data attributes
|
|
111
|
+
const observer = new MutationObserver(() => synchronizeTheme());
|
|
112
|
+
observer.observe(closestRoot, {
|
|
113
|
+
attributes: true,
|
|
114
|
+
attributeFilter: ['data-iui-theme', 'data-iui-contrast'],
|
|
115
|
+
});
|
|
116
|
+
return () => {
|
|
117
|
+
observer.disconnect();
|
|
118
|
+
};
|
|
119
|
+
}, [rootElement, parentThemeRef]);
|
|
120
|
+
return {
|
|
121
|
+
theme: parentContext?.theme ?? parentThemeState,
|
|
122
|
+
highContrast: parentContext?.themeOptions?.highContrast ?? parentHighContrastState,
|
|
123
|
+
context: parentContext,
|
|
124
|
+
};
|
|
102
125
|
};
|
package/cjs/core/Tile/Tile.d.ts
CHANGED
|
@@ -249,9 +249,69 @@ export declare const Tile: PolymorphicForwardRefComponent<"div", TileLegacyProps
|
|
|
249
249
|
*/
|
|
250
250
|
IconButton: PolymorphicForwardRefComponent<"button", Omit<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
|
|
251
251
|
ref?: ((instance: HTMLButtonElement | null) => void) | React.RefObject<HTMLButtonElement> | null | undefined;
|
|
252
|
-
}, "label" | "as" | "size" | "htmlDisabled" | "styleType" | "isActive" | "iconProps"> & {
|
|
252
|
+
}, "label" | "as" | "size" | "htmlDisabled" | "styleType" | "labelProps" | "isActive" | "iconProps"> & {
|
|
253
253
|
isActive?: boolean | undefined;
|
|
254
254
|
label?: React.ReactNode;
|
|
255
|
+
labelProps?: Omit<Omit<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
|
|
256
|
+
ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined;
|
|
257
|
+
}, "as" | "children" | "content" | "portal" | keyof {
|
|
258
|
+
placement?: import("@floating-ui/utils").Placement | undefined;
|
|
259
|
+
visible?: boolean | undefined;
|
|
260
|
+
onVisibleChange?: ((visible: boolean) => void) | undefined;
|
|
261
|
+
autoUpdateOptions?: {
|
|
262
|
+
ancestorScroll?: boolean | undefined;
|
|
263
|
+
ancestorResize?: boolean | undefined;
|
|
264
|
+
elementResize?: boolean | undefined;
|
|
265
|
+
animationFrame?: boolean | undefined;
|
|
266
|
+
layoutShift?: boolean | undefined;
|
|
267
|
+
} | undefined;
|
|
268
|
+
middleware?: {
|
|
269
|
+
offset?: number | undefined;
|
|
270
|
+
flip?: boolean | undefined;
|
|
271
|
+
shift?: boolean | undefined;
|
|
272
|
+
size?: boolean | undefined;
|
|
273
|
+
autoPlacement?: boolean | undefined;
|
|
274
|
+
hide?: boolean | undefined;
|
|
275
|
+
inline?: boolean | undefined;
|
|
276
|
+
} | undefined;
|
|
277
|
+
reference?: HTMLElement | null | undefined;
|
|
278
|
+
ariaStrategy?: "label" | "description" | "none" | undefined; /**
|
|
279
|
+
* Default tile variant or the folder layout.
|
|
280
|
+
* @default 'default'
|
|
281
|
+
*/
|
|
282
|
+
id?: string | undefined;
|
|
283
|
+
}> & {
|
|
284
|
+
content: React.ReactNode;
|
|
285
|
+
children?: React.ReactNode;
|
|
286
|
+
} & import("../utils/index.js").PortalProps & {
|
|
287
|
+
placement?: import("@floating-ui/utils").Placement | undefined;
|
|
288
|
+
visible?: boolean | undefined;
|
|
289
|
+
onVisibleChange?: ((visible: boolean) => void) | undefined;
|
|
290
|
+
autoUpdateOptions?: {
|
|
291
|
+
ancestorScroll?: boolean | undefined;
|
|
292
|
+
ancestorResize?: boolean | undefined;
|
|
293
|
+
elementResize?: boolean | undefined;
|
|
294
|
+
animationFrame?: boolean | undefined;
|
|
295
|
+
layoutShift?: boolean | undefined;
|
|
296
|
+
} | undefined;
|
|
297
|
+
middleware?: {
|
|
298
|
+
offset?: number | undefined;
|
|
299
|
+
flip?: boolean | undefined;
|
|
300
|
+
shift?: boolean | undefined;
|
|
301
|
+
size?: boolean | undefined;
|
|
302
|
+
autoPlacement?: boolean | undefined;
|
|
303
|
+
hide?: boolean | undefined;
|
|
304
|
+
inline?: boolean | undefined;
|
|
305
|
+
} | undefined;
|
|
306
|
+
reference?: HTMLElement | null | undefined;
|
|
307
|
+
ariaStrategy?: "label" | "description" | "none" | undefined; /**
|
|
308
|
+
* Default tile variant or the folder layout.
|
|
309
|
+
* @default 'default'
|
|
310
|
+
*/
|
|
311
|
+
id?: string | undefined;
|
|
312
|
+
} & {
|
|
313
|
+
as?: "div" | undefined;
|
|
314
|
+
}, "ref">, "children" | "content" | "reference" | "ariaStrategy"> | undefined;
|
|
255
315
|
iconProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> | undefined;
|
|
256
316
|
} & Omit<import("../Buttons/Button.js").ButtonProps, "startIcon" | "endIcon" | "labelProps" | "startIconProps" | "endIconProps"> & {
|
|
257
317
|
as?: "button" | undefined;
|
|
@@ -61,8 +61,12 @@ const useTooltip = (options = {}) => {
|
|
|
61
61
|
...interactions.getReferenceProps(),
|
|
62
62
|
}).forEach(([key, value]) => {
|
|
63
63
|
if (typeof value === 'function') {
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
// manually add `.nativeEvent` (react concept) because floating-ui needs it
|
|
65
|
+
const patchedHandler = (event) => {
|
|
66
|
+
value({ ...event, nativeEvent: event });
|
|
67
|
+
};
|
|
68
|
+
reference.addEventListener(domEventName(key), patchedHandler);
|
|
69
|
+
cleanupValues[key] = patchedHandler;
|
|
66
70
|
}
|
|
67
71
|
else if (value) {
|
|
68
72
|
cleanupValues[key] = reference.getAttribute(key);
|
package/cjs/core/Tree/Tree.d.ts
CHANGED
|
@@ -33,6 +33,12 @@ export type NodeData<T> = {
|
|
|
33
33
|
};
|
|
34
34
|
export type NodeRenderProps<T> = Omit<NodeData<T>, 'subNodes'>;
|
|
35
35
|
export type TreeProps<T> = {
|
|
36
|
+
/**
|
|
37
|
+
* Modify size of the tree.
|
|
38
|
+
*
|
|
39
|
+
* @default 'default'
|
|
40
|
+
*/
|
|
41
|
+
size?: 'default' | 'small';
|
|
36
42
|
/**
|
|
37
43
|
* Render function that should return the node element.
|
|
38
44
|
* Recommended to use `TreeNode` component.
|
package/cjs/core/Tree/Tree.js
CHANGED
|
@@ -61,7 +61,7 @@ const TreeContext_js_1 = require("./TreeContext.js");
|
|
|
61
61
|
/>
|
|
62
62
|
*/
|
|
63
63
|
const Tree = (props) => {
|
|
64
|
-
const { data, className, nodeRenderer, getNode, enableVirtualization = false, style, ...rest } = props;
|
|
64
|
+
const { data, className, nodeRenderer, getNode, size = 'default', enableVirtualization = false, style, ...rest } = props;
|
|
65
65
|
const treeRef = React.useRef(null);
|
|
66
66
|
const focusedIndex = React.useRef(0);
|
|
67
67
|
React.useEffect(() => {
|
|
@@ -146,8 +146,9 @@ const Tree = (props) => {
|
|
|
146
146
|
setScrollToIndex(parentNodeIndex);
|
|
147
147
|
}
|
|
148
148
|
: undefined,
|
|
149
|
+
size,
|
|
149
150
|
} }, nodeRenderer(node.nodeProps)));
|
|
150
|
-
}, [firstLevelNodesList.length, flatNodesList, nodeRenderer]);
|
|
151
|
+
}, [firstLevelNodesList.length, flatNodesList, nodeRenderer, size]);
|
|
151
152
|
const [scrollToIndex, setScrollToIndex] = React.useState();
|
|
152
153
|
const flatNodesListRef = React.useRef(flatNodesList);
|
|
153
154
|
React.useEffect(() => {
|
|
@@ -175,7 +176,7 @@ const Tree = (props) => {
|
|
|
175
176
|
items[focusedIndex.current]?.focus();
|
|
176
177
|
}
|
|
177
178
|
};
|
|
178
|
-
return (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualizedTree, { flatNodesList: flatNodesList, itemRenderer: itemRenderer, scrollToIndex: scrollToIndex, onFocus: handleFocus, onKeyDown: handleKeyDown, ref: treeRef, className: className, style: style, ...rest })) : (React.createElement(TreeElement, { onKeyDown: handleKeyDown, onFocus: handleFocus, className: className, style: style, ref: treeRef, ...rest }, flatNodesList.map((_, i) => itemRenderer(i))))));
|
|
179
|
+
return (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualizedTree, { flatNodesList: flatNodesList, itemRenderer: itemRenderer, scrollToIndex: scrollToIndex, onFocus: handleFocus, onKeyDown: handleKeyDown, ref: treeRef, className: className, style: style, ...rest })) : (React.createElement(TreeElement, { onKeyDown: handleKeyDown, onFocus: handleFocus, className: className, "data-iui-size": size === 'small' ? 'small' : undefined, style: style, ref: treeRef, ...rest }, flatNodesList.map((_, i) => itemRenderer(i))))));
|
|
179
180
|
};
|
|
180
181
|
exports.Tree = Tree;
|
|
181
182
|
const TreeElement = index_js_1.polymorphic.ul('iui-tree', {
|
|
@@ -24,6 +24,10 @@ export type TreeContextProps = {
|
|
|
24
24
|
* Function that scrolls to the node's parent node.
|
|
25
25
|
*/
|
|
26
26
|
scrollToParent?: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Size of the tree.
|
|
29
|
+
*/
|
|
30
|
+
size?: 'default' | 'small';
|
|
27
31
|
};
|
|
28
32
|
export declare const TreeContext: React.Context<TreeContextProps | undefined>;
|
|
29
33
|
export declare const useTreeContext: () => TreeContextProps;
|
|
@@ -10,10 +10,13 @@ const React = tslib_1.__importStar(require("react"));
|
|
|
10
10
|
const classnames_1 = tslib_1.__importDefault(require("classnames"));
|
|
11
11
|
const index_js_1 = require("../utils/index.js");
|
|
12
12
|
const IconButton_js_1 = require("../Buttons/IconButton.js");
|
|
13
|
+
const TreeContext_js_1 = require("./TreeContext.js");
|
|
13
14
|
exports.TreeNodeExpander = React.forwardRef((props, ref) => {
|
|
14
15
|
const { isExpanded, ...rest } = props;
|
|
16
|
+
const size = React.useContext(TreeContext_js_1.TreeContext)?.size ?? 'default';
|
|
17
|
+
const ChevronIcon = size === 'small' ? index_js_1.SvgChevronRightSmall : index_js_1.SvgChevronRight;
|
|
15
18
|
return (React.createElement(IconButton_js_1.IconButton, { styleType: 'borderless', size: 'small', "aria-label": isExpanded ? 'Collapse' : 'Expand', ref: ref, ...rest },
|
|
16
|
-
React.createElement(
|
|
19
|
+
React.createElement(ChevronIcon, { className: (0, classnames_1.default)('iui-tree-node-content-expander-icon', {
|
|
17
20
|
'iui-tree-node-content-expander-icon-expanded': isExpanded,
|
|
18
21
|
}) })));
|
|
19
22
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SvgChevronRightSmall = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/*---------------------------------------------------------------------------------------------
|
|
6
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
7
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
8
|
+
*--------------------------------------------------------------------------------------------*/
|
|
9
|
+
const React = tslib_1.__importStar(require("react"));
|
|
10
|
+
const Svg_js_1 = require("./Svg.js");
|
|
11
|
+
const SvgChevronRightSmall = (props) => {
|
|
12
|
+
return (React.createElement(Svg_js_1.Svg, { ...props },
|
|
13
|
+
React.createElement("path", { d: 'm5.525 2-1.05 1.05L9.425 8l-4.95 4.95L5.525 14l6-6-6-6Z' })));
|
|
14
|
+
};
|
|
15
|
+
exports.SvgChevronRightSmall = SvgChevronRightSmall;
|
|
@@ -7,6 +7,7 @@ export * from './SvgClose.js';
|
|
|
7
7
|
export * from './SvgCloseSmall.js';
|
|
8
8
|
export * from './SvgChevronLeft.js';
|
|
9
9
|
export * from './SvgChevronRight.js';
|
|
10
|
+
export * from './SvgChevronRightSmall.js';
|
|
10
11
|
export * from './SvgChevronLeftDouble.js';
|
|
11
12
|
export * from './SvgChevronRightDouble.js';
|
|
12
13
|
export * from './SvgCaretUpSmall.js';
|
|
@@ -14,6 +14,7 @@ tslib_1.__exportStar(require("./SvgClose.js"), exports);
|
|
|
14
14
|
tslib_1.__exportStar(require("./SvgCloseSmall.js"), exports);
|
|
15
15
|
tslib_1.__exportStar(require("./SvgChevronLeft.js"), exports);
|
|
16
16
|
tslib_1.__exportStar(require("./SvgChevronRight.js"), exports);
|
|
17
|
+
tslib_1.__exportStar(require("./SvgChevronRightSmall.js"), exports);
|
|
17
18
|
tslib_1.__exportStar(require("./SvgChevronLeftDouble.js"), exports);
|
|
18
19
|
tslib_1.__exportStar(require("./SvgChevronRightDouble.js"), exports);
|
|
19
20
|
tslib_1.__exportStar(require("./SvgCaretUpSmall.js"), exports);
|
package/cjs/styles.js
CHANGED
|
@@ -208,9 +208,9 @@ const styles = {
|
|
|
208
208
|
"iui-overlay-exiting": "_iui3-overlay-exiting",
|
|
209
209
|
closeAnimation,
|
|
210
210
|
"iui-progress-indicator-radial": "_iui3-progress-indicator-radial",
|
|
211
|
-
"iui-
|
|
211
|
+
"iui-uj1wh1i": "_iui3-uj1wh1i",
|
|
212
212
|
"iui-progress-indicator-linear-label": "_iui3-progress-indicator-linear-label",
|
|
213
|
-
"iui-
|
|
213
|
+
"iui-uj1wh1t": "_iui3-uj1wh1t",
|
|
214
214
|
"iui-radio": "_iui3-radio",
|
|
215
215
|
"iui-radio-tile": "_iui3-radio-tile",
|
|
216
216
|
"iui-radio-tile-icon": "_iui3-radio-tile-icon",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { PolymorphicForwardRefComponent } from '../utils/index.js';
|
|
3
|
+
/** This context is used for letting descendant IconButtons know the ButtonGroup's orientation. */
|
|
4
|
+
export declare const ButtonGroupContext: React.Context<string | undefined>;
|
|
3
5
|
type ButtonGroupProps = {
|
|
4
6
|
/**
|
|
5
7
|
* Buttons in the ButtonGroup.
|
|
@@ -6,6 +6,10 @@ import * as React from 'react';
|
|
|
6
6
|
import cx from 'classnames';
|
|
7
7
|
import { useOverflow, useMergedRefs, Box } from '../utils/index.js';
|
|
8
8
|
import { FloatingDelayGroup } from '@floating-ui/react';
|
|
9
|
+
// ----------------------------------------------------------------------------
|
|
10
|
+
/** This context is used for letting descendant IconButtons know the ButtonGroup's orientation. */
|
|
11
|
+
export const ButtonGroupContext = React.createContext(undefined);
|
|
12
|
+
ButtonGroupContext.displayName = 'ButtonGroupContext';
|
|
9
13
|
/**
|
|
10
14
|
* Group buttons together for common actions.
|
|
11
15
|
* Handles responsive overflow when the `overflowButton` prop is specified.
|
|
@@ -40,25 +44,26 @@ export const ButtonGroup = React.forwardRef((props, ref) => {
|
|
|
40
44
|
const [overflowRef, visibleCount] = useOverflow(items, !overflowButton, orientation);
|
|
41
45
|
const refs = useMergedRefs(overflowRef, ref);
|
|
42
46
|
return (React.createElement(FloatingDelayGroup, { delay: { open: 50, close: 250 } },
|
|
43
|
-
React.createElement(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
React.createElement(ButtonGroupContext.Provider, { value: orientation },
|
|
48
|
+
React.createElement(Box, { className: cx('iui-button-group', {
|
|
49
|
+
'iui-button-group-overflow-x': !!overflowButton && orientation === 'horizontal',
|
|
50
|
+
}, className), "data-iui-orientation": orientation === 'vertical' ? orientation : undefined, ref: refs, ...rest }, (() => {
|
|
51
|
+
if (!(visibleCount < items.length)) {
|
|
52
|
+
return items;
|
|
53
|
+
}
|
|
54
|
+
const overflowStart = overflowPlacement === 'start'
|
|
55
|
+
? items.length - visibleCount
|
|
56
|
+
: visibleCount - 1;
|
|
57
|
+
return (React.createElement(React.Fragment, null,
|
|
58
|
+
overflowButton &&
|
|
59
|
+
overflowPlacement === 'start' &&
|
|
60
|
+
overflowButton(overflowStart),
|
|
61
|
+
overflowPlacement === 'start'
|
|
62
|
+
? items.slice(overflowStart + 1)
|
|
63
|
+
: items.slice(0, Math.max(0, overflowStart)),
|
|
64
|
+
overflowButton &&
|
|
65
|
+
overflowPlacement === 'end' &&
|
|
66
|
+
overflowButton(overflowStart)));
|
|
67
|
+
})()))));
|
|
63
68
|
});
|
|
64
69
|
export default ButtonGroup;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { Tooltip } from '../Tooltip/Tooltip.js';
|
|
2
3
|
import type { ButtonProps } from './Button.js';
|
|
3
4
|
import type { PolymorphicForwardRefComponent } from '../utils/index.js';
|
|
4
5
|
export type IconButtonProps = {
|
|
@@ -11,6 +12,11 @@ export type IconButtonProps = {
|
|
|
11
12
|
* Name of the button, shown in a tooltip and exposed to assistive technologies.
|
|
12
13
|
*/
|
|
13
14
|
label?: React.ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Props passed to the Tooltip that contains the `label`.
|
|
17
|
+
* Can be used for customizing the tooltip's `placement`, etc.
|
|
18
|
+
*/
|
|
19
|
+
labelProps?: Omit<React.ComponentPropsWithoutRef<typeof Tooltip>, 'content' | 'reference' | 'ariaStrategy' | 'children'>;
|
|
14
20
|
/**
|
|
15
21
|
* Passes props to IconButton icon.
|
|
16
22
|
*/
|
|
@@ -7,6 +7,7 @@ import * as React from 'react';
|
|
|
7
7
|
import { Box, ButtonBase } from '../utils/index.js';
|
|
8
8
|
import { Tooltip } from '../Tooltip/Tooltip.js';
|
|
9
9
|
import { VisuallyHidden } from '../VisuallyHidden/VisuallyHidden.js';
|
|
10
|
+
import { ButtonGroupContext } from '../ButtonGroup/ButtonGroup.js';
|
|
10
11
|
/**
|
|
11
12
|
* Icon button
|
|
12
13
|
* @example
|
|
@@ -15,10 +16,11 @@ import { VisuallyHidden } from '../VisuallyHidden/VisuallyHidden.js';
|
|
|
15
16
|
* <IconButton label='Add' styleType='borderless'><SvgAdd /></IconButton>
|
|
16
17
|
*/
|
|
17
18
|
export const IconButton = React.forwardRef((props, ref) => {
|
|
18
|
-
const { isActive, children, styleType = 'default', size, className, label, iconProps, ...rest } = props;
|
|
19
|
+
const { isActive, children, styleType = 'default', size, className, label, iconProps, labelProps, ...rest } = props;
|
|
20
|
+
const buttonGroupOrientation = React.useContext(ButtonGroupContext);
|
|
19
21
|
const button = (React.createElement(ButtonBase, { ref: ref, className: cx('iui-button', className), "data-iui-variant": styleType !== 'default' ? styleType : undefined, "data-iui-size": size, "data-iui-active": isActive, "aria-pressed": isActive, ...rest },
|
|
20
22
|
React.createElement(Box, { as: 'span', "aria-hidden": true, ...iconProps, className: cx('iui-button-icon', iconProps?.className) }, children),
|
|
21
23
|
label ? React.createElement(VisuallyHidden, null, label) : null));
|
|
22
|
-
return label ? (React.createElement(Tooltip, { content: label, ariaStrategy: 'none' }, button)) : (button);
|
|
24
|
+
return label ? (React.createElement(Tooltip, { placement: buttonGroupOrientation === 'vertical' ? 'right' : 'top', ...labelProps, content: label, ariaStrategy: 'none' }, button)) : (button);
|
|
23
25
|
});
|
|
24
26
|
export default IconButton;
|