@itwin/itwinui-react 2.9.1 → 2.10.1
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 +30 -0
- package/cjs/core/Buttons/IconButton/IconButton.d.ts +11 -4
- package/cjs/core/Buttons/IconButton/IconButton.js +13 -5
- package/cjs/core/Carousel/Carousel.d.ts +2 -0
- package/cjs/core/Carousel/CarouselNavigation.d.ts +2 -0
- package/cjs/core/DatePicker/DatePicker.js +2 -13
- package/cjs/core/FileUpload/FileUploadCard.js +15 -4
- package/cjs/core/LabeledInput/LabeledInput.js +4 -3
- package/cjs/core/LabeledSelect/LabeledSelect.js +7 -3
- package/cjs/core/LabeledTextarea/LabeledTextarea.js +4 -3
- package/cjs/core/Select/Select.d.ts +4 -0
- package/cjs/core/Select/Select.js +27 -29
- package/cjs/core/Table/filters/DateRangeFilter/DatePickerInput.d.ts +8 -0
- package/cjs/core/Table/filters/DateRangeFilter/DatePickerInput.js +10 -2
- package/cjs/core/Table/filters/DateRangeFilter/DateRangeFilter.js +2 -2
- package/cjs/core/utils/components/InputContainer.d.ts +2 -0
- package/cjs/core/utils/components/InputContainer.js +4 -3
- package/cjs/core/utils/functions/date.d.ts +4 -0
- package/cjs/core/utils/functions/date.js +21 -0
- package/cjs/core/utils/functions/index.d.ts +1 -0
- package/cjs/core/utils/functions/index.js +1 -0
- package/cjs/core/utils/hooks/useId.d.ts +3 -3
- package/cjs/core/utils/hooks/useId.js +8 -5
- package/esm/core/Buttons/IconButton/IconButton.d.ts +11 -4
- package/esm/core/Buttons/IconButton/IconButton.js +14 -6
- package/esm/core/Carousel/Carousel.d.ts +2 -0
- package/esm/core/Carousel/CarouselNavigation.d.ts +2 -0
- package/esm/core/DatePicker/DatePicker.js +1 -12
- package/esm/core/FileUpload/FileUploadCard.js +15 -4
- package/esm/core/LabeledInput/LabeledInput.js +5 -4
- package/esm/core/LabeledSelect/LabeledSelect.js +8 -4
- package/esm/core/LabeledTextarea/LabeledTextarea.js +5 -4
- package/esm/core/Select/Select.d.ts +4 -0
- package/esm/core/Select/Select.js +28 -30
- package/esm/core/Table/filters/DateRangeFilter/DatePickerInput.d.ts +8 -0
- package/esm/core/Table/filters/DateRangeFilter/DatePickerInput.js +11 -3
- package/esm/core/Table/filters/DateRangeFilter/DateRangeFilter.js +2 -2
- package/esm/core/utils/components/InputContainer.d.ts +2 -0
- package/esm/core/utils/components/InputContainer.js +4 -3
- package/esm/core/utils/functions/date.d.ts +4 -0
- package/esm/core/utils/functions/date.js +17 -0
- package/esm/core/utils/functions/index.d.ts +1 -0
- package/esm/core/utils/functions/index.js +1 -0
- package/esm/core/utils/hooks/useId.d.ts +3 -3
- package/esm/core/utils/hooks/useId.js +7 -5
- package/package.json +7 -2
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ButtonProps } from '../Button';
|
|
3
|
+
import type { PolymorphicForwardRefComponent } from '../../utils';
|
|
3
4
|
import '@itwin/itwinui-css/css/button.css';
|
|
5
|
+
import '@itwin/itwinui-css/css/tooltip.css';
|
|
4
6
|
export declare type IconButtonProps = {
|
|
5
7
|
/**
|
|
6
8
|
* Button gets active style.
|
|
7
9
|
* @default false
|
|
8
10
|
*/
|
|
9
11
|
isActive?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Name of the button, shown in a tooltip and exposed to assistive technologies.
|
|
14
|
+
*/
|
|
15
|
+
label?: React.ReactNode;
|
|
10
16
|
} & Omit<ButtonProps, 'startIcon' | 'endIcon'>;
|
|
11
17
|
declare type IconButtonComponent = PolymorphicForwardRefComponent<'button', IconButtonProps>;
|
|
12
18
|
/**
|
|
13
19
|
* Icon button
|
|
14
20
|
* @example
|
|
15
|
-
* <IconButton><SvgAdd /></IconButton>
|
|
16
|
-
*
|
|
21
|
+
* <IconButton label='Add'><SvgAdd /></IconButton>
|
|
22
|
+
* @example
|
|
23
|
+
* <IconButton label='Add' styleType='borderless'><SvgAdd /></IconButton>
|
|
17
24
|
*/
|
|
18
25
|
export declare const IconButton: IconButtonComponent;
|
|
19
26
|
export default IconButton;
|
|
@@ -4,18 +4,26 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import cx from 'classnames';
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import { useTheme } from '../../utils';
|
|
7
|
+
import { useTheme, VisuallyHidden, Popover } from '../../utils';
|
|
8
8
|
import '@itwin/itwinui-css/css/button.css';
|
|
9
|
+
import '@itwin/itwinui-css/css/tooltip.css';
|
|
9
10
|
/**
|
|
10
11
|
* Icon button
|
|
11
12
|
* @example
|
|
12
|
-
* <IconButton><SvgAdd /></IconButton>
|
|
13
|
-
*
|
|
13
|
+
* <IconButton label='Add'><SvgAdd /></IconButton>
|
|
14
|
+
* @example
|
|
15
|
+
* <IconButton label='Add' styleType='borderless'><SvgAdd /></IconButton>
|
|
14
16
|
*/
|
|
15
17
|
export const IconButton = React.forwardRef((props, ref) => {
|
|
16
|
-
const { isActive, children, styleType = 'default', size, type = 'button', className, as: Element = 'button', ...rest } = props;
|
|
18
|
+
const { isActive, children, styleType = 'default', size, type = 'button', className, as: Element = 'button', label, ...rest } = props;
|
|
17
19
|
useTheme();
|
|
18
|
-
return (React.createElement(
|
|
19
|
-
React.createElement(
|
|
20
|
+
return (React.createElement(IconButtonTooltip, { label: label },
|
|
21
|
+
React.createElement(Element, { 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, type: type, ...rest },
|
|
22
|
+
React.createElement("span", { className: 'iui-button-icon', "aria-hidden": true }, children),
|
|
23
|
+
label ? React.createElement(VisuallyHidden, null, label) : null)));
|
|
20
24
|
});
|
|
25
|
+
const IconButtonTooltip = (props) => {
|
|
26
|
+
const { label, children } = props;
|
|
27
|
+
return label ? (React.createElement(Popover, { interactive: false, offset: [0, 4], aria: { content: null }, content: React.createElement("div", { "aria-hidden": 'true', className: 'iui-tooltip' }, label) }, children)) : (children);
|
|
28
|
+
};
|
|
21
29
|
export default IconButton;
|
|
@@ -49,9 +49,11 @@ export declare const Carousel: React.ForwardRefExoticComponent<{
|
|
|
49
49
|
Navigation: React.ForwardRefExoticComponent<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "key" | keyof React.HTMLAttributes<HTMLElement>> & React.RefAttributes<HTMLElement>> & {
|
|
50
50
|
PreviousButton: React.ForwardRefExoticComponent<{
|
|
51
51
|
isActive?: boolean | undefined;
|
|
52
|
+
label?: React.ReactNode;
|
|
52
53
|
} & Omit<import("..").ButtonProps<"button">, "startIcon" | "endIcon"> & React.RefAttributes<HTMLButtonElement>>;
|
|
53
54
|
NextButton: React.ForwardRefExoticComponent<{
|
|
54
55
|
isActive?: boolean | undefined;
|
|
56
|
+
label?: React.ReactNode;
|
|
55
57
|
} & Omit<import("..").ButtonProps<"button">, "startIcon" | "endIcon"> & React.RefAttributes<HTMLButtonElement>>;
|
|
56
58
|
};
|
|
57
59
|
DotsList: React.ForwardRefExoticComponent<{
|
|
@@ -8,8 +8,10 @@ import React from 'react';
|
|
|
8
8
|
export declare const CarouselNavigation: React.ForwardRefExoticComponent<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>, "key" | keyof React.HTMLAttributes<HTMLElement>> & React.RefAttributes<HTMLElement>> & {
|
|
9
9
|
PreviousButton: React.ForwardRefExoticComponent<{
|
|
10
10
|
isActive?: boolean | undefined;
|
|
11
|
+
label?: React.ReactNode;
|
|
11
12
|
} & Omit<import("../Buttons").ButtonProps<"button">, "startIcon" | "endIcon"> & React.RefAttributes<HTMLButtonElement>>;
|
|
12
13
|
NextButton: React.ForwardRefExoticComponent<{
|
|
13
14
|
isActive?: boolean | undefined;
|
|
15
|
+
label?: React.ReactNode;
|
|
14
16
|
} & Omit<import("../Buttons").ButtonProps<"button">, "startIcon" | "endIcon"> & React.RefAttributes<HTMLButtonElement>>;
|
|
15
17
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import cx from 'classnames';
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import { useTheme, SvgChevronLeft, SvgChevronRight, SvgChevronLeftDouble, SvgChevronRightDouble, } from '../utils';
|
|
7
|
+
import { useTheme, SvgChevronLeft, SvgChevronRight, SvgChevronLeftDouble, SvgChevronRightDouble, isBefore, } from '../utils';
|
|
8
8
|
import '@itwin/itwinui-css/css/date-picker.css';
|
|
9
9
|
import { IconButton } from '../Buttons/IconButton';
|
|
10
10
|
import { TimePicker } from '../TimePicker';
|
|
@@ -27,17 +27,6 @@ const isInDateRange = (date, startDate, endDate) => {
|
|
|
27
27
|
maxDate && maxDate.setHours(0, 0, 0, 0);
|
|
28
28
|
return testDate > minDate && testDate < maxDate;
|
|
29
29
|
};
|
|
30
|
-
// compares to see if one date is earlier than another
|
|
31
|
-
const isBefore = (beforeDate, afterDate) => {
|
|
32
|
-
if (!beforeDate || !afterDate) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
const firstDate = new Date(beforeDate);
|
|
36
|
-
const secondDate = new Date(afterDate);
|
|
37
|
-
firstDate && firstDate.setHours(0, 0, 0, 0);
|
|
38
|
-
secondDate && secondDate.setHours(0, 0, 0, 0);
|
|
39
|
-
return firstDate < secondDate;
|
|
40
|
-
};
|
|
41
30
|
// Type guard for multiple did not work
|
|
42
31
|
const isSingleOnChange = (onChange, enableRangeSelect) => {
|
|
43
32
|
return !enableRangeSelect;
|
|
@@ -23,16 +23,19 @@ const FileUploadCardIcon = React.forwardRef((props, ref) => {
|
|
|
23
23
|
const { children, className, ...rest } = props;
|
|
24
24
|
return (React.createElement("span", { className: cx('iui-file-card-icon', className), ref: ref, ...rest }, children !== null && children !== void 0 ? children : React.createElement(SvgDocument, null)));
|
|
25
25
|
});
|
|
26
|
+
FileUploadCardIcon.displayName = 'FileUploadCard.Icon';
|
|
26
27
|
const FileUploadCardInfo = React.forwardRef((props, ref) => {
|
|
27
28
|
const { children, className, ...rest } = props;
|
|
28
29
|
return (React.createElement("span", { className: cx('iui-file-card-text', className), ref: ref, ...rest }, children));
|
|
29
30
|
});
|
|
31
|
+
FileUploadCardInfo.displayName = 'FileUploadCard.Info';
|
|
30
32
|
const FileUploadCardTitle = React.forwardRef((props, ref) => {
|
|
31
33
|
const { children, className, ...rest } = props;
|
|
32
34
|
const { files } = useSafeContext(FileUploadCardContext);
|
|
33
35
|
const title = files.length > 1 ? files.length + ' files selected' : files[0].name;
|
|
34
36
|
return (React.createElement("span", { className: cx('iui-file-card-title', className), ref: ref, ...rest }, children !== null && children !== void 0 ? children : title));
|
|
35
37
|
});
|
|
38
|
+
FileUploadCardTitle.displayName = 'FileUploadCard.Title';
|
|
36
39
|
const FileUploadCardDescription = React.forwardRef((props, ref) => {
|
|
37
40
|
const { children, className, ...rest } = props;
|
|
38
41
|
const { files } = useSafeContext(FileUploadCardContext);
|
|
@@ -44,15 +47,18 @@ const FileUploadCardDescription = React.forwardRef((props, ref) => {
|
|
|
44
47
|
}
|
|
45
48
|
return (React.createElement("span", { className: cx('iui-file-card-description', className), ref: ref, ...rest }, children !== null && children !== void 0 ? children : description));
|
|
46
49
|
});
|
|
50
|
+
FileUploadCardDescription.displayName = 'FileUploadCard.Description';
|
|
47
51
|
const FileUploadCardAction = React.forwardRef((props, ref) => {
|
|
48
52
|
const { children, className, ...rest } = props;
|
|
49
53
|
return (React.createElement("div", { className: cx('iui-file-card-action', className), ref: ref, ...rest }, children));
|
|
50
54
|
});
|
|
55
|
+
FileUploadCardAction.displayName = 'FileUploadCard.Action';
|
|
51
56
|
const FileUploadCardInputLabel = React.forwardRef((props, ref) => {
|
|
52
57
|
const { children, className, ...rest } = props;
|
|
53
58
|
const { inputId } = useSafeContext(FileUploadCardContext);
|
|
54
59
|
return (React.createElement("label", { className: cx('iui-anchor', className), ref: ref, htmlFor: inputId, ...rest }, children));
|
|
55
60
|
});
|
|
61
|
+
FileUploadCardInputLabel.displayName = 'FileUploadCard.InputLabel';
|
|
56
62
|
const FileUploadCardInput = React.forwardRef((props, ref) => {
|
|
57
63
|
const { children, className, onChange, id, ...rest } = props;
|
|
58
64
|
const { files, onFilesChange, setInternalFiles, inputId, setInputId } = useSafeContext(FileUploadCardContext);
|
|
@@ -67,9 +73,11 @@ const FileUploadCardInput = React.forwardRef((props, ref) => {
|
|
|
67
73
|
node.files = dataTransfer.files;
|
|
68
74
|
}, [files]);
|
|
69
75
|
const refs = useMergedRefs(ref, setNativeFilesRef);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
React.useEffect(() => {
|
|
77
|
+
if (id && id !== inputId) {
|
|
78
|
+
setInputId(id);
|
|
79
|
+
}
|
|
80
|
+
}, [id, inputId, setInputId]);
|
|
73
81
|
return (React.createElement(React.Fragment, null,
|
|
74
82
|
React.createElement("input", { className: cx('iui-visually-hidden', className), type: 'file', onChange: (e) => {
|
|
75
83
|
onChange === null || onChange === void 0 ? void 0 : onChange(e);
|
|
@@ -81,6 +89,7 @@ const FileUploadCardInput = React.forwardRef((props, ref) => {
|
|
|
81
89
|
}, ref: refs, id: id !== null && id !== void 0 ? id : inputId, ...rest }),
|
|
82
90
|
children));
|
|
83
91
|
});
|
|
92
|
+
FileUploadCardInput.displayName = 'FileUploadCard.Input';
|
|
84
93
|
/**
|
|
85
94
|
* Default card to be used with the `FileUpload` wrapper component for single-file uploading.
|
|
86
95
|
* @example
|
|
@@ -109,7 +118,8 @@ export const FileUploadCard = Object.assign(React.forwardRef((props, ref) => {
|
|
|
109
118
|
var _a;
|
|
110
119
|
const { className, children, files: filesProp, onFilesChange, emptyCard = React.createElement(FileEmptyCard, null), input, ...rest } = props;
|
|
111
120
|
const [internalFiles, setInternalFiles] = React.useState();
|
|
112
|
-
const
|
|
121
|
+
const uid = useId();
|
|
122
|
+
const [inputId, setInputId] = React.useState(uid);
|
|
113
123
|
const files = (_a = filesProp !== null && filesProp !== void 0 ? filesProp : internalFiles) !== null && _a !== void 0 ? _a : [];
|
|
114
124
|
return (React.createElement(FileUploadCardContext.Provider, { value: {
|
|
115
125
|
files,
|
|
@@ -134,5 +144,6 @@ export const FileUploadCard = Object.assign(React.forwardRef((props, ref) => {
|
|
|
134
144
|
InputLabel: FileUploadCardInputLabel,
|
|
135
145
|
Input: FileUploadCardInput,
|
|
136
146
|
});
|
|
147
|
+
FileUploadCard.displayName = 'FileUploadCard';
|
|
137
148
|
export default FileUploadCard;
|
|
138
149
|
export const FileUploadCardContext = React.createContext(undefined);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { Input } from '../Input/Input';
|
|
7
|
-
import { StatusIconMap, useTheme, InputContainer } from '../utils';
|
|
7
|
+
import { StatusIconMap, useTheme, InputContainer, useId } from '../utils';
|
|
8
8
|
import '@itwin/itwinui-css/css/input.css';
|
|
9
9
|
/**
|
|
10
10
|
* Basic labeled input component
|
|
@@ -15,10 +15,11 @@ import '@itwin/itwinui-css/css/input.css';
|
|
|
15
15
|
* <LabeledInput status='negative' label='Negative' setFocus />
|
|
16
16
|
*/
|
|
17
17
|
export const LabeledInput = React.forwardRef((props, ref) => {
|
|
18
|
-
const
|
|
18
|
+
const uid = useId();
|
|
19
|
+
const { className, disabled = false, label, message, status, svgIcon, style, inputClassName, inputStyle, displayStyle = 'default', iconDisplayStyle = displayStyle === 'default' ? 'block' : 'inline', required = false, id = uid, ...rest } = props;
|
|
19
20
|
useTheme();
|
|
20
21
|
const icon = svgIcon !== null && svgIcon !== void 0 ? svgIcon : (status && StatusIconMap[status]());
|
|
21
|
-
return (React.createElement(InputContainer, {
|
|
22
|
-
React.createElement(Input, { disabled: disabled, className: inputClassName, style: inputStyle, required: required, ref: ref, ...rest })));
|
|
22
|
+
return (React.createElement(InputContainer, { label: label, disabled: disabled, required: required, status: status, message: message, icon: icon, isLabelInline: displayStyle === 'inline', isIconInline: iconDisplayStyle === 'inline', className: className, style: style, inputId: id },
|
|
23
|
+
React.createElement(Input, { disabled: disabled, className: inputClassName, style: inputStyle, required: required, ref: ref, id: id, ...rest })));
|
|
23
24
|
});
|
|
24
25
|
export default LabeledInput;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { Select } from '../Select';
|
|
7
|
-
import { StatusIconMap, useTheme, InputContainer } from '../utils';
|
|
7
|
+
import { StatusIconMap, useTheme, InputContainer, useId } from '../utils';
|
|
8
8
|
import '@itwin/itwinui-css/css/input.css';
|
|
9
9
|
/**
|
|
10
10
|
* Labeled select component to select value from options.
|
|
@@ -40,8 +40,9 @@ import '@itwin/itwinui-css/css/input.css';
|
|
|
40
40
|
* />
|
|
41
41
|
*/
|
|
42
42
|
export const LabeledSelect = (props) => {
|
|
43
|
-
const { className, disabled = false, label, message, status, svgIcon, displayStyle = 'default', style, selectClassName, selectStyle, required = false, ...rest } = props;
|
|
43
|
+
const { className, disabled = false, label, message, status, svgIcon, displayStyle = 'default', style, selectClassName, selectStyle, required = false, triggerProps, ...rest } = props;
|
|
44
44
|
useTheme();
|
|
45
|
+
const labelId = `${useId()}-label`;
|
|
45
46
|
const icon = () => {
|
|
46
47
|
if (svgIcon) {
|
|
47
48
|
return React.cloneElement(svgIcon, { 'aria-hidden': true });
|
|
@@ -51,7 +52,10 @@ export const LabeledSelect = (props) => {
|
|
|
51
52
|
}
|
|
52
53
|
return undefined;
|
|
53
54
|
};
|
|
54
|
-
return (React.createElement(InputContainer, { label: label, disabled: disabled, required: required, status: status, message: message, icon: displayStyle === 'default' ? icon() : undefined, isLabelInline: displayStyle === 'inline', className: className, style: style },
|
|
55
|
-
React.createElement(Select, { disabled: disabled, className: selectClassName, style: selectStyle, ...rest
|
|
55
|
+
return (React.createElement(InputContainer, { label: label, disabled: disabled, required: required, status: status, message: message, icon: displayStyle === 'default' ? icon() : undefined, isLabelInline: displayStyle === 'inline', className: className, style: style, labelId: labelId },
|
|
56
|
+
React.createElement(Select, { disabled: disabled, className: selectClassName, style: selectStyle, ...rest, triggerProps: {
|
|
57
|
+
'aria-labelledby': labelId,
|
|
58
|
+
...triggerProps,
|
|
59
|
+
} })));
|
|
56
60
|
};
|
|
57
61
|
export default LabeledSelect;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import React from 'react';
|
|
6
|
-
import { StatusIconMap, useTheme, InputContainer } from '../utils';
|
|
6
|
+
import { StatusIconMap, useTheme, InputContainer, useId } from '../utils';
|
|
7
7
|
import { Textarea } from '../Textarea';
|
|
8
8
|
import '@itwin/itwinui-css/css/input.css';
|
|
9
9
|
/**
|
|
@@ -28,10 +28,11 @@ import '@itwin/itwinui-css/css/input.css';
|
|
|
28
28
|
* />
|
|
29
29
|
*/
|
|
30
30
|
export const LabeledTextarea = React.forwardRef((props, ref) => {
|
|
31
|
-
const
|
|
31
|
+
const uid = useId();
|
|
32
|
+
const { className, style, disabled = false, label, message, status, textareaClassName, textareaStyle, displayStyle = 'default', iconDisplayStyle = displayStyle === 'default' ? 'block' : 'inline', svgIcon, required = false, id = uid, ...textareaProps } = props;
|
|
32
33
|
useTheme();
|
|
33
34
|
const icon = svgIcon !== null && svgIcon !== void 0 ? svgIcon : (status && StatusIconMap[status]());
|
|
34
|
-
return (React.createElement(InputContainer, {
|
|
35
|
-
React.createElement(Textarea, { disabled: disabled, className: textareaClassName, style: textareaStyle, required: required, ...textareaProps, ref: ref })));
|
|
35
|
+
return (React.createElement(InputContainer, { label: label, disabled: disabled, required: required, status: status, message: message, icon: icon, isLabelInline: displayStyle === 'inline', isIconInline: iconDisplayStyle === 'inline', className: className, style: style, inputId: id },
|
|
36
|
+
React.createElement(Textarea, { disabled: disabled, className: textareaClassName, style: textareaStyle, required: required, id: id, ...textareaProps, ref: ref })));
|
|
36
37
|
});
|
|
37
38
|
export default LabeledTextarea;
|
|
@@ -111,6 +111,10 @@ export declare type SelectProps<T> = {
|
|
|
111
111
|
* @see [tippy.js props](https://atomiks.github.io/tippyjs/v6/all-props/)
|
|
112
112
|
*/
|
|
113
113
|
popoverProps?: Omit<PopoverProps, 'onShow' | 'onHide' | 'disabled'>;
|
|
114
|
+
/**
|
|
115
|
+
* Props to pass to the select button (trigger) element.
|
|
116
|
+
*/
|
|
117
|
+
triggerProps?: React.ComponentPropsWithoutRef<'div'>;
|
|
114
118
|
} & SelectMultipleTypeProps<T> & Pick<PopoverProps, 'onShow' | 'onHide'> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
|
|
115
119
|
/**
|
|
116
120
|
* Select component to select value from options.
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import cx from 'classnames';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { useTheme, SvgCaretDownSmall, } from '../utils';
|
|
7
|
+
import { Menu, MenuItem } from '../Menu';
|
|
8
|
+
import { useTheme, SvgCaretDownSmall, Popover, useId, } from '../utils';
|
|
10
9
|
import '@itwin/itwinui-css/css/select.css';
|
|
11
10
|
import SelectTag from './SelectTag';
|
|
12
11
|
import SelectTagContainer from './SelectTagContainer';
|
|
@@ -68,14 +67,12 @@ const isSingleOnChange = (onChange, multiple) => {
|
|
|
68
67
|
*/
|
|
69
68
|
export const Select = (props) => {
|
|
70
69
|
var _a;
|
|
71
|
-
const
|
|
70
|
+
const uid = useId();
|
|
71
|
+
const { options, value, onChange, placeholder, disabled = false, size, setFocus = false, itemRenderer, selectedItemRenderer, className, style, menuClassName, menuStyle, onShow, onHide, popoverProps, multiple = false, triggerProps, ...rest } = props;
|
|
72
72
|
useTheme();
|
|
73
|
-
const [
|
|
74
|
-
|
|
75
|
-
setIsOpen((open) => { var _a; return (_a = popoverProps === null || popoverProps === void 0 ? void 0 : popoverProps.visible) !== null && _a !== void 0 ? _a : open; });
|
|
76
|
-
}, [popoverProps]);
|
|
73
|
+
const [isOpenState, setIsOpen] = React.useState(false);
|
|
74
|
+
const isOpen = (_a = popoverProps === null || popoverProps === void 0 ? void 0 : popoverProps.visible) !== null && _a !== void 0 ? _a : isOpenState;
|
|
77
75
|
const [minWidth, setMinWidth] = React.useState(0);
|
|
78
|
-
const toggle = () => setIsOpen((open) => !open);
|
|
79
76
|
const selectRef = React.useRef(null);
|
|
80
77
|
const toggleButtonRef = React.useRef(null);
|
|
81
78
|
const onShowHandler = React.useCallback((instance) => {
|
|
@@ -83,7 +80,9 @@ export const Select = (props) => {
|
|
|
83
80
|
onShow === null || onShow === void 0 ? void 0 : onShow(instance);
|
|
84
81
|
}, [onShow]);
|
|
85
82
|
const onHideHandler = React.useCallback((instance) => {
|
|
83
|
+
var _a;
|
|
86
84
|
setIsOpen(false);
|
|
85
|
+
(_a = selectRef.current) === null || _a === void 0 ? void 0 : _a.focus({ preventScroll: true }); // move focus back to select button
|
|
87
86
|
onHide === null || onHide === void 0 ? void 0 : onHide(instance);
|
|
88
87
|
}, [onHide]);
|
|
89
88
|
React.useEffect(() => {
|
|
@@ -96,30 +95,29 @@ export const Select = (props) => {
|
|
|
96
95
|
setMinWidth(selectRef.current.offsetWidth);
|
|
97
96
|
}
|
|
98
97
|
}, [isOpen]);
|
|
99
|
-
const onKeyDown = (event
|
|
98
|
+
const onKeyDown = (event) => {
|
|
100
99
|
if (event.altKey) {
|
|
101
100
|
return;
|
|
102
101
|
}
|
|
103
102
|
switch (event.key) {
|
|
104
103
|
case 'Enter':
|
|
105
104
|
case ' ':
|
|
106
|
-
case 'Spacebar':
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
event.preventDefault();
|
|
110
|
-
}
|
|
105
|
+
case 'Spacebar': {
|
|
106
|
+
setIsOpen((o) => !o);
|
|
107
|
+
event.preventDefault();
|
|
111
108
|
break;
|
|
109
|
+
}
|
|
112
110
|
default:
|
|
113
111
|
break;
|
|
114
112
|
}
|
|
115
113
|
};
|
|
116
|
-
const menuItems = React.
|
|
114
|
+
const menuItems = React.useMemo(() => {
|
|
117
115
|
return options.map((option, index) => {
|
|
118
116
|
var _a;
|
|
119
117
|
const isSelected = isMultipleEnabled(value, multiple)
|
|
120
118
|
? (_a = value === null || value === void 0 ? void 0 : value.includes(option.value)) !== null && _a !== void 0 ? _a : false
|
|
121
119
|
: value === option.value;
|
|
122
|
-
const menuItem = itemRenderer ? (itemRenderer(option, { close, isSelected })) : (React.createElement(MenuItem, null, option.label));
|
|
120
|
+
const menuItem = itemRenderer ? (itemRenderer(option, { close: () => setIsOpen(false), isSelected })) : (React.createElement(MenuItem, null, option.label));
|
|
123
121
|
const { label, ...restOption } = option;
|
|
124
122
|
return React.cloneElement(menuItem, {
|
|
125
123
|
key: `${label}-${index}`,
|
|
@@ -130,7 +128,7 @@ export const Select = (props) => {
|
|
|
130
128
|
}
|
|
131
129
|
if (isSingleOnChange(onChange, multiple)) {
|
|
132
130
|
onChange === null || onChange === void 0 ? void 0 : onChange(option.value);
|
|
133
|
-
|
|
131
|
+
setIsOpen(false);
|
|
134
132
|
}
|
|
135
133
|
else {
|
|
136
134
|
onChange === null || onChange === void 0 ? void 0 : onChange(option.value, isSelected ? 'removed' : 'added');
|
|
@@ -138,7 +136,7 @@ export const Select = (props) => {
|
|
|
138
136
|
},
|
|
139
137
|
ref: (el) => {
|
|
140
138
|
if (isSelected && !multiple) {
|
|
141
|
-
el === null || el === void 0 ? void 0 : el.scrollIntoView();
|
|
139
|
+
el === null || el === void 0 ? void 0 : el.scrollIntoView({ block: 'nearest' });
|
|
142
140
|
}
|
|
143
141
|
},
|
|
144
142
|
role: 'option',
|
|
@@ -158,29 +156,29 @@ export const Select = (props) => {
|
|
|
158
156
|
const tagRenderer = React.useCallback((item) => {
|
|
159
157
|
return React.createElement(SelectTag, { key: item.label, label: item.label });
|
|
160
158
|
}, []);
|
|
161
|
-
return (React.createElement("div", { className: cx('iui-input-with-icon', className),
|
|
162
|
-
React.createElement(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
159
|
+
return (React.createElement("div", { className: cx('iui-input-with-icon', className), style: style, ...rest },
|
|
160
|
+
React.createElement(Popover, { content: React.createElement(Menu, { role: 'listbox', className: cx('iui-scroll', menuClassName), style: {
|
|
161
|
+
minWidth,
|
|
162
|
+
maxWidth: `min(${minWidth * 2}px, 90vw)`,
|
|
163
|
+
...menuStyle,
|
|
164
|
+
}, id: `${uid}-menu`, key: `${uid}-menu` }, menuItems), placement: 'bottom-start', aria: { content: null }, onShow: onShowHandler, onHide: onHideHandler, ...popoverProps, visible: isOpen, onClickOutside: (_, { target }) => {
|
|
167
165
|
var _a;
|
|
168
166
|
if (!((_a = toggleButtonRef.current) === null || _a === void 0 ? void 0 : _a.contains(target))) {
|
|
169
167
|
setIsOpen(false);
|
|
170
168
|
}
|
|
171
169
|
} },
|
|
172
|
-
React.createElement("div", { ref: selectRef, className: cx('iui-select-button', {
|
|
170
|
+
React.createElement("div", { tabIndex: 0, role: 'combobox', ref: selectRef, "data-iui-size": size, onClick: () => !disabled && setIsOpen((o) => !o), onKeyDown: (e) => !disabled && onKeyDown(e), "aria-disabled": disabled, "aria-autocomplete": 'none', "aria-expanded": isOpen, "aria-haspopup": 'listbox', "aria-controls": `${uid}-menu`, ...triggerProps, className: cx('iui-select-button', {
|
|
173
171
|
'iui-placeholder': (!selectedItems || selectedItems.length === 0) && !!placeholder,
|
|
174
172
|
'iui-disabled': disabled,
|
|
175
|
-
}
|
|
173
|
+
}, triggerProps === null || triggerProps === void 0 ? void 0 : triggerProps.className) },
|
|
176
174
|
(!selectedItems || selectedItems.length === 0) && (React.createElement("span", { className: 'iui-content' }, placeholder)),
|
|
177
175
|
isMultipleEnabled(selectedItems, multiple) ? (React.createElement(MultipleSelectButton, { selectedItems: selectedItems, selectedItemsRenderer: selectedItemRenderer, tagRenderer: tagRenderer })) : (React.createElement(SingleSelectButton, { selectedItem: selectedItems, selectedItemRenderer: selectedItemRenderer })))),
|
|
178
|
-
React.createElement("span", { ref: toggleButtonRef, className: cx('iui-end-icon', {
|
|
176
|
+
React.createElement("span", { "aria-hidden": true, ref: toggleButtonRef, className: cx('iui-end-icon', {
|
|
179
177
|
'iui-actionable': !disabled,
|
|
180
178
|
'iui-disabled': disabled,
|
|
181
179
|
'iui-open': isOpen,
|
|
182
|
-
}), onClick: () => !disabled &&
|
|
183
|
-
React.createElement(SvgCaretDownSmall,
|
|
180
|
+
}), onClick: () => !disabled && setIsOpen((o) => !o) },
|
|
181
|
+
React.createElement(SvgCaretDownSmall, null))));
|
|
184
182
|
};
|
|
185
183
|
const SingleSelectButton = ({ selectedItem, selectedItemRenderer, }) => {
|
|
186
184
|
return (React.createElement(React.Fragment, null,
|
|
@@ -5,6 +5,14 @@ export declare type DatePickerInputProps = {
|
|
|
5
5
|
onChange: (date?: Date) => void;
|
|
6
6
|
parseInput: (text: string) => Date;
|
|
7
7
|
formatDate: (date: Date) => string;
|
|
8
|
+
/**
|
|
9
|
+
* Decides if this component is used for the 'from' or 'to' date
|
|
10
|
+
*/
|
|
11
|
+
isFromOrTo?: 'from' | 'to';
|
|
12
|
+
/**
|
|
13
|
+
* The 'to' date for the 'from' DatePickerInput or the 'from' date for the 'to' DatePickerInput
|
|
14
|
+
*/
|
|
15
|
+
selectedDate?: Date;
|
|
8
16
|
} & Omit<LabeledInputProps, 'value' | 'onChange' | 'svgIcon' | 'displayStyle'>;
|
|
9
17
|
declare const DatePickerInput: (props: DatePickerInputProps) => JSX.Element;
|
|
10
18
|
export default DatePickerInput;
|
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import React from 'react';
|
|
6
|
-
import { Popover, SvgCalendar } from '../../../utils';
|
|
6
|
+
import { Popover, SvgCalendar, isBefore } from '../../../utils';
|
|
7
7
|
import { LabeledInput } from '../../../LabeledInput';
|
|
8
8
|
import { DatePicker } from '../../../DatePicker';
|
|
9
9
|
import { IconButton } from '../../../Buttons';
|
|
10
10
|
const DatePickerInput = (props) => {
|
|
11
|
-
const { onChange, date, parseInput, formatDate, ...rest } = props;
|
|
11
|
+
const { onChange, date, parseInput, formatDate, isFromOrTo, selectedDate, ...rest } = props;
|
|
12
|
+
const isDateDisabled = (date) => {
|
|
13
|
+
if (isFromOrTo === 'to') {
|
|
14
|
+
return isBefore(date, selectedDate);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return isBefore(selectedDate, date);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
12
20
|
const buttonRef = React.useRef(null);
|
|
13
21
|
const [inputValue, setInputValue] = React.useState('');
|
|
14
22
|
React.useEffect(() => {
|
|
@@ -35,7 +43,7 @@ const DatePickerInput = (props) => {
|
|
|
35
43
|
onChange(parsedDate);
|
|
36
44
|
}
|
|
37
45
|
}, [onChange, parseInput]);
|
|
38
|
-
return (React.createElement(Popover, { content: React.createElement(DatePicker, { date: date, onChange: onDateSelected, setFocus: true }), placement: 'bottom', visible: isVisible, onClickOutside: (_, e) => {
|
|
46
|
+
return (React.createElement(Popover, { content: React.createElement(DatePicker, { date: date, onChange: onDateSelected, setFocus: true, isDateDisabled: isDateDisabled }), placement: 'bottom', visible: isVisible, onClickOutside: (_, e) => {
|
|
39
47
|
var _a;
|
|
40
48
|
if (!((_a = buttonRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
|
|
41
49
|
close();
|
|
@@ -59,7 +59,7 @@ export const DateRangeFilter = (props) => {
|
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
61
|
return (React.createElement(BaseFilter, null,
|
|
62
|
-
React.createElement(DatePickerInput, { label: translatedStrings.from, date: from, onChange: onFromChange, formatDate: formatDate, parseInput: parseInput, onKeyDown: onKeyDown, placeholder: placeholder, setFocus: true }),
|
|
63
|
-
React.createElement(DatePickerInput, { label: translatedStrings.to, date: to, onChange: onToChange, formatDate: formatDate, parseInput: parseInput, onKeyDown: onKeyDown, placeholder: placeholder }),
|
|
62
|
+
React.createElement(DatePickerInput, { label: translatedStrings.from, date: from, onChange: onFromChange, formatDate: formatDate, parseInput: parseInput, onKeyDown: onKeyDown, placeholder: placeholder, selectedDate: to, isFromOrTo: 'from', setFocus: true }),
|
|
63
|
+
React.createElement(DatePickerInput, { label: translatedStrings.to, date: to, onChange: onToChange, formatDate: formatDate, parseInput: parseInput, onKeyDown: onKeyDown, placeholder: placeholder, selectedDate: from, isFromOrTo: 'to' }),
|
|
64
64
|
React.createElement(FilterButtonBar, { setFilter: () => setFilter([from, to]), clearFilter: clearFilter, translatedLabels: translatedLabels })));
|
|
65
65
|
};
|
|
@@ -11,6 +11,8 @@ export declare type InputContainerProps<T extends React.ElementType = 'div'> = {
|
|
|
11
11
|
isLabelInline?: boolean;
|
|
12
12
|
isIconInline?: boolean;
|
|
13
13
|
statusMessage?: React.ReactNode;
|
|
14
|
+
inputId?: string;
|
|
15
|
+
labelId?: string;
|
|
14
16
|
} & React.ComponentPropsWithoutRef<T>;
|
|
15
17
|
/**
|
|
16
18
|
* Input container to wrap inputs with label, and add optional message and icon.
|
|
@@ -11,7 +11,8 @@ import '@itwin/itwinui-css/css/utils.css';
|
|
|
11
11
|
*/
|
|
12
12
|
export const InputContainer = (props) => {
|
|
13
13
|
var _a;
|
|
14
|
-
const { as: Element = 'div', label, disabled, required, status, message, icon, isLabelInline, isIconInline, children, className, style, statusMessage, ...rest } = props;
|
|
14
|
+
const { as: Element = 'div', label, disabled, required, status, message, icon, isLabelInline, isIconInline, children, className, style, statusMessage, inputId, labelId, ...rest } = props;
|
|
15
|
+
const LabelElement = inputId && Element !== 'label' ? 'label' : 'div';
|
|
15
16
|
return (React.createElement(Element, { className: cx('iui-input-container', {
|
|
16
17
|
'iui-disabled': disabled,
|
|
17
18
|
[`iui-${status}`]: !!status,
|
|
@@ -19,9 +20,9 @@ export const InputContainer = (props) => {
|
|
|
19
20
|
'iui-inline-icon': isIconInline,
|
|
20
21
|
'iui-with-message': (!!message || !!icon || !!statusMessage) && !isLabelInline,
|
|
21
22
|
}, className), style: style, ...rest },
|
|
22
|
-
label && (React.createElement(
|
|
23
|
+
label && (React.createElement(LabelElement, { className: cx('iui-label', {
|
|
23
24
|
'iui-required': required,
|
|
24
|
-
}) }, label)),
|
|
25
|
+
}), htmlFor: inputId, id: labelId }, label)),
|
|
25
26
|
children,
|
|
26
27
|
statusMessage ? (statusMessage) : (React.createElement(React.Fragment, null,
|
|
27
28
|
icon &&
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
/**
|
|
6
|
+
* Return true if the first date is earlier than the second date
|
|
7
|
+
*/
|
|
8
|
+
export const isBefore = (beforeDate, afterDate) => {
|
|
9
|
+
if (!beforeDate || !afterDate) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const firstDate = new Date(beforeDate);
|
|
13
|
+
const secondDate = new Date(afterDate);
|
|
14
|
+
firstDate && firstDate.setHours(0, 0, 0, 0);
|
|
15
|
+
secondDate && secondDate.setHours(0, 0, 0, 0);
|
|
16
|
+
return firstDate < secondDate;
|
|
17
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
export * from './date';
|
|
5
6
|
export * from './dom';
|
|
6
7
|
export * from './colors';
|
|
7
8
|
export * from './numbers';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
2
|
+
* Wrapper around React's `useId` hook, which prefixes the id with `iui-` and uses
|
|
3
|
+
* a random value as fallback for older React versions which don't include `useId`.
|
|
4
4
|
*/
|
|
5
|
-
export declare const useId:
|
|
5
|
+
export declare const useId: () => string;
|
|
@@ -6,9 +6,11 @@ var _a;
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import { getRandomValue } from '../functions/numbers';
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Wrapper around React's `useId` hook, which prefixes the id with `iui-` and uses
|
|
10
|
+
* a random value as fallback for older React versions which don't include `useId`.
|
|
10
11
|
*/
|
|
11
|
-
export const useId = (
|
|
12
|
-
const
|
|
13
|
-
return
|
|
14
|
-
}
|
|
12
|
+
export const useId = () => {
|
|
13
|
+
const uniqueValue = useUniqueValue();
|
|
14
|
+
return React.useMemo(() => `iui-${uniqueValue}`, [uniqueValue]);
|
|
15
|
+
};
|
|
16
|
+
const useUniqueValue = (_a = React.useId) !== null && _a !== void 0 ? _a : (() => getRandomValue(10));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itwin/itwinui-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.1",
|
|
4
4
|
"author": "Bentley Systems",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "cjs/index.js",
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
],
|
|
32
32
|
"description": "A react component library for iTwinUI",
|
|
33
33
|
"homepage": "https://github.com/iTwin/iTwinUI",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/iTwin/iTwinUI.git",
|
|
37
|
+
"directory": "packages/itwinui-react"
|
|
38
|
+
},
|
|
34
39
|
"keywords": [
|
|
35
40
|
"component",
|
|
36
41
|
"components",
|
|
@@ -62,7 +67,7 @@
|
|
|
62
67
|
"dev:types": "concurrently \"tsc -p tsconfig.cjs.json --emitDeclarationOnly --watch --preserveWatchOutput\" \"tsc -p tsconfig.esm.json --emitDeclarationOnly --watch --preserveWatchOutput\""
|
|
63
68
|
},
|
|
64
69
|
"dependencies": {
|
|
65
|
-
"@itwin/itwinui-css": "^1.10.
|
|
70
|
+
"@itwin/itwinui-css": "^1.10.3",
|
|
66
71
|
"@itwin/itwinui-illustrations-react": "^2.0.0",
|
|
67
72
|
"@itwin/itwinui-variables": "^2.0.0",
|
|
68
73
|
"@tippyjs/react": "^4.2.6",
|