@itwin/itwinui-react 2.1.1 → 2.2.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 +12 -0
- package/cjs/core/DatePicker/DatePicker.js +2 -2
- package/cjs/core/NotificationMarker/NotificationMarker.d.ts +51 -0
- package/cjs/core/NotificationMarker/NotificationMarker.js +32 -0
- package/cjs/core/NotificationMarker/index.d.ts +4 -0
- package/cjs/core/NotificationMarker/index.js +10 -0
- package/cjs/core/Table/Table.d.ts +1 -1
- package/cjs/core/Table/Table.js +19 -2
- package/cjs/core/Table/actionHandlers/filterHandler.d.ts +1 -1
- package/cjs/core/Table/actionHandlers/filterHandler.js +3 -2
- package/cjs/core/TimePicker/TimePicker.d.ts +26 -1
- package/cjs/core/TimePicker/TimePicker.js +89 -7
- package/cjs/core/index.d.ts +2 -0
- package/cjs/core/index.js +4 -2
- package/esm/core/DatePicker/DatePicker.js +2 -2
- package/esm/core/NotificationMarker/NotificationMarker.d.ts +51 -0
- package/esm/core/NotificationMarker/NotificationMarker.js +26 -0
- package/esm/core/NotificationMarker/index.d.ts +4 -0
- package/esm/core/NotificationMarker/index.js +6 -0
- package/esm/core/Table/Table.d.ts +1 -1
- package/esm/core/Table/Table.js +19 -2
- package/esm/core/Table/actionHandlers/filterHandler.d.ts +1 -1
- package/esm/core/Table/actionHandlers/filterHandler.js +3 -2
- package/esm/core/TimePicker/TimePicker.d.ts +26 -1
- package/esm/core/TimePicker/TimePicker.js +89 -7
- package/esm/core/index.d.ts +2 -0
- package/esm/core/index.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.2.0](https://www.github.com/iTwin/iTwinUI-react/compare/v2.1.0...v2.2.0) (2022-12-19)
|
|
4
|
+
|
|
5
|
+
### What's new
|
|
6
|
+
|
|
7
|
+
* **NotificationMarker:** Add new `NotificationMarker` component ([#829](https://www.github.com/iTwin/iTwinUI-react/issues/829)) ([6a0a6c3](https://www.github.com/iTwin/iTwinUI-react/commit/6a0a6c3913a0d5ca51f9835f517bdac36236e8c9))
|
|
8
|
+
* **Table:** `onFilter` gets filtered rows ([#958](https://www.github.com/iTwin/iTwinUI-react/issues/958)) ([84bc9e9](https://www.github.com/iTwin/iTwinUI-react/commit/84bc9e9dd957ed7e87ef9f3fc617c83c74127a5f))
|
|
9
|
+
* **Timepicker:** Add combined time column ([#844](https://www.github.com/iTwin/iTwinUI-react/issues/844)) ([86050eb](https://www.github.com/iTwin/iTwinUI-react/commit/86050eb6d971437f287d13dbf9935d8dafbbb281))
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
|
|
13
|
+
* **Table:** Reset `columnOrder` when `columns` changes ([#983](https://www.github.com/iTwin/iTwinUI-react/issues/983)) ([4f184f6](https://www.github.com/iTwin/iTwinUI-react/commit/4f184f6caf37545a0008e6b9cd5d218bd218fdbd))
|
|
14
|
+
|
|
3
15
|
### 2.1.1 (2022-12-16)
|
|
4
16
|
|
|
5
17
|
### Fixes
|
|
@@ -130,7 +130,7 @@ exports.generateLocalizedStrings = generateLocalizedStrings;
|
|
|
130
130
|
*/
|
|
131
131
|
const DatePicker = (props) => {
|
|
132
132
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
133
|
-
const { date, onChange, localizedNames, className, style, setFocus = false, showTime = false, use12Hours = false, precision, hourStep, minuteStep, secondStep, showYearSelection = false, enableRangeSelect = false, startDate, endDate, ...rest } = props;
|
|
133
|
+
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;
|
|
134
134
|
(0, utils_1.useTheme)();
|
|
135
135
|
const monthNames = (_a = localizedNames === null || localizedNames === void 0 ? void 0 : localizedNames.months) !== null && _a !== void 0 ? _a : defaultMonths;
|
|
136
136
|
const shortDays = (_b = localizedNames === null || localizedNames === void 0 ? void 0 : localizedNames.shortDays) !== null && _b !== void 0 ? _b : defaultShortDays;
|
|
@@ -368,7 +368,7 @@ const DatePicker = (props) => {
|
|
|
368
368
|
(element === null || element === void 0 ? void 0 : element.focus()) }, dateValue));
|
|
369
369
|
})));
|
|
370
370
|
}))),
|
|
371
|
-
showTime && (react_1.default.createElement(TimePicker_1.TimePicker, { date: selectedStartDay !== null && selectedStartDay !== void 0 ? selectedStartDay : selectedDay, use12Hours: use12Hours, precision: precision, hourStep: hourStep, minuteStep: minuteStep, secondStep: secondStep, onChange: (date) => {
|
|
371
|
+
showTime && (react_1.default.createElement(TimePicker_1.TimePicker, { date: selectedStartDay !== null && selectedStartDay !== void 0 ? selectedStartDay : selectedDay, use12Hours: use12Hours, precision: precision, hourStep: hourStep, minuteStep: minuteStep, secondStep: secondStep, useCombinedRenderer: useCombinedRenderer, combinedRenderer: combinedRenderer, hourRenderer: hourRenderer, minuteRenderer: minuteRenderer, secondRenderer: secondRenderer, meridiemRenderer: meridiemRenderer, onChange: (date) => {
|
|
372
372
|
var _a, _b, _c, _d, _e, _f;
|
|
373
373
|
return isSingleOnChange(onChange, enableRangeSelect)
|
|
374
374
|
? onChange === null || onChange === void 0 ? void 0 : onChange(date)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '@itwin/itwinui-css/css/utils.css';
|
|
3
|
+
export declare type NotificationMarkerProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Content of the NotificationMarker.
|
|
6
|
+
*/
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Status of notification
|
|
10
|
+
*
|
|
11
|
+
* - 'primary' = blue,
|
|
12
|
+
* - 'positive' = green,
|
|
13
|
+
* - 'warning' = orange,
|
|
14
|
+
* - 'negative' = red,
|
|
15
|
+
* - 'white' = white (useful for notifications in buttons with `style` = `high-visibility` | `cta`),
|
|
16
|
+
* @default 'primary'
|
|
17
|
+
*/
|
|
18
|
+
status?: 'primary' | 'positive' | 'warning' | 'negative' | 'white';
|
|
19
|
+
/**
|
|
20
|
+
* Adds a pulse effect to the notification.
|
|
21
|
+
*
|
|
22
|
+
* **WARNING**: Avoid overuse of this prop.
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
pulsing?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Set this programmatically to false when you just want to render the passed children without the notification
|
|
28
|
+
* @default true
|
|
29
|
+
* @example
|
|
30
|
+
* let [newMessagesCount, ...] = useState(0);
|
|
31
|
+
* ...
|
|
32
|
+
* <NotificationMarker enabled={newMessagesCount > 0}>
|
|
33
|
+
* <SvgNotification />
|
|
34
|
+
* </NotificationMarker>
|
|
35
|
+
*/
|
|
36
|
+
enabled?: boolean;
|
|
37
|
+
} & React.ComponentProps<'span'>;
|
|
38
|
+
/**
|
|
39
|
+
* A small notification circle to the top-right of the passed children prop.
|
|
40
|
+
* This can be applied to almost anything but mostly intended for icons within buttons with `styleType = default / borderless`.
|
|
41
|
+
* @example
|
|
42
|
+
* <IconButton styleType='borderless'>
|
|
43
|
+
* <NotificationMarker>
|
|
44
|
+
* <SvgNotification />
|
|
45
|
+
* </NotificationMarker>
|
|
46
|
+
* </IconButton>
|
|
47
|
+
* @example
|
|
48
|
+
* <NotificationMarker status='positive' pulsing={true}>Live</NotificationMarker>
|
|
49
|
+
*/
|
|
50
|
+
export declare const NotificationMarker: React.ForwardRefExoticComponent<Pick<NotificationMarkerProps, "key" | "status" | keyof React.HTMLAttributes<HTMLSpanElement> | "enabled" | "pulsing"> & React.RefAttributes<HTMLSpanElement>>;
|
|
51
|
+
export default NotificationMarker;
|
|
@@ -0,0 +1,32 @@
|
|
|
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.NotificationMarker = 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 utils_1 = require("../utils");
|
|
13
|
+
const classnames_1 = __importDefault(require("classnames"));
|
|
14
|
+
require("@itwin/itwinui-css/css/utils.css");
|
|
15
|
+
/**
|
|
16
|
+
* A small notification circle to the top-right of the passed children prop.
|
|
17
|
+
* This can be applied to almost anything but mostly intended for icons within buttons with `styleType = default / borderless`.
|
|
18
|
+
* @example
|
|
19
|
+
* <IconButton styleType='borderless'>
|
|
20
|
+
* <NotificationMarker>
|
|
21
|
+
* <SvgNotification />
|
|
22
|
+
* </NotificationMarker>
|
|
23
|
+
* </IconButton>
|
|
24
|
+
* @example
|
|
25
|
+
* <NotificationMarker status='positive' pulsing={true}>Live</NotificationMarker>
|
|
26
|
+
*/
|
|
27
|
+
exports.NotificationMarker = react_1.default.forwardRef((props, ref) => {
|
|
28
|
+
const { className, children, status = 'primary', pulsing = false, enabled = true, ...rest } = props;
|
|
29
|
+
(0, utils_1.useTheme)();
|
|
30
|
+
return (react_1.default.createElement("span", { ref: ref, className: (0, classnames_1.default)({ 'iui-notification-marker': enabled }, className), "data-iui-variant": enabled ? status : null, "data-iui-urgent": enabled ? pulsing : null, ...rest }, children));
|
|
31
|
+
});
|
|
32
|
+
exports.default = exports.NotificationMarker;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NotificationMarker = void 0;
|
|
4
|
+
/*---------------------------------------------------------------------------------------------
|
|
5
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
6
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
7
|
+
*--------------------------------------------------------------------------------------------*/
|
|
8
|
+
var NotificationMarker_1 = require("./NotificationMarker");
|
|
9
|
+
Object.defineProperty(exports, "NotificationMarker", { enumerable: true, get: function () { return NotificationMarker_1.NotificationMarker; } });
|
|
10
|
+
exports.default = './NotificationMarker';
|
|
@@ -117,7 +117,7 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
|
|
|
117
117
|
* Use with `manualFilters` to handle filtering yourself e.g. filter in server-side.
|
|
118
118
|
* Must be memoized.
|
|
119
119
|
*/
|
|
120
|
-
onFilter?: (filters: TableFilterValue<T>[], state: TableState<T>) => void;
|
|
120
|
+
onFilter?: (filters: TableFilterValue<T>[], state: TableState<T>, filteredData?: Row<T>[]) => void;
|
|
121
121
|
/**
|
|
122
122
|
* Value used for global filtering.
|
|
123
123
|
* Use with `globalFilter` and/or `manualGlobalFilter` to handle filtering yourself e.g. filter in server-side.
|
package/cjs/core/Table/Table.js
CHANGED
|
@@ -123,13 +123,15 @@ const Table = (props) => {
|
|
|
123
123
|
disableUserSelect,
|
|
124
124
|
enableUserSelect,
|
|
125
125
|
]);
|
|
126
|
+
const previousFilter = react_1.default.useRef([]);
|
|
127
|
+
const currentFilter = react_1.default.useRef(previousFilter.current);
|
|
126
128
|
const tableStateReducer = react_1.default.useCallback((newState, action, previousState, instance) => {
|
|
127
129
|
switch (action.type) {
|
|
128
130
|
case react_table_1.actions.toggleSortBy:
|
|
129
131
|
onSort === null || onSort === void 0 ? void 0 : onSort(newState);
|
|
130
132
|
break;
|
|
131
133
|
case react_table_1.actions.setFilter:
|
|
132
|
-
(0, actionHandlers_1.onFilterHandler)(newState, action, previousState,
|
|
134
|
+
currentFilter.current = (0, actionHandlers_1.onFilterHandler)(newState, action, previousState, currentFilter.current, instance);
|
|
133
135
|
break;
|
|
134
136
|
case react_table_1.actions.toggleRowExpanded:
|
|
135
137
|
case react_table_1.actions.toggleAllRowsExpanded:
|
|
@@ -173,7 +175,6 @@ const Table = (props) => {
|
|
|
173
175
|
hasManualSelectionColumn,
|
|
174
176
|
isRowDisabled,
|
|
175
177
|
onExpand,
|
|
176
|
-
onFilter,
|
|
177
178
|
onSelect,
|
|
178
179
|
onSort,
|
|
179
180
|
stateReducer,
|
|
@@ -249,6 +250,22 @@ const Table = (props) => {
|
|
|
249
250
|
react_1.default.useEffect(() => {
|
|
250
251
|
setPageSize(pageSize);
|
|
251
252
|
}, [pageSize, setPageSize]);
|
|
253
|
+
react_1.default.useEffect(() => {
|
|
254
|
+
if (previousFilter.current !== currentFilter.current) {
|
|
255
|
+
previousFilter.current = currentFilter.current;
|
|
256
|
+
onFilter === null || onFilter === void 0 ? void 0 : onFilter(currentFilter.current, state, instance.filteredRows);
|
|
257
|
+
}
|
|
258
|
+
}, [state, instance.filteredRows, onFilter]);
|
|
259
|
+
const lastPassedColumns = react_1.default.useRef([]);
|
|
260
|
+
// Reset the column order whenever new columns are passed
|
|
261
|
+
// This is to avoid the old columnOrder from affecting the new columns' columnOrder
|
|
262
|
+
react_1.default.useEffect(() => {
|
|
263
|
+
// Check if columns have changed (by value)
|
|
264
|
+
if (JSON.stringify(lastPassedColumns.current) !== JSON.stringify(columns)) {
|
|
265
|
+
instance.setColumnOrder([]);
|
|
266
|
+
}
|
|
267
|
+
lastPassedColumns.current = columns;
|
|
268
|
+
}, [columns, instance]);
|
|
252
269
|
const paginatorRendererProps = react_1.default.useMemo(() => ({
|
|
253
270
|
currentPage: state.pageIndex,
|
|
254
271
|
pageSize: state.pageSize,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ActionType, TableInstance, TableState } from 'react-table';
|
|
2
2
|
import { TableFilterValue } from '../filters';
|
|
3
|
-
export declare const onFilterHandler: <T extends Record<string, unknown>>(newState: TableState<T>, action: ActionType, previousState: TableState<T>,
|
|
3
|
+
export declare const onFilterHandler: <T extends Record<string, unknown>>(newState: TableState<T>, action: ActionType, previousState: TableState<T>, currentFilter: TableFilterValue<T>[], instance?: TableInstance<T> | undefined) => TableFilterValue<T>[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.onFilterHandler = void 0;
|
|
4
|
-
const onFilterHandler = (newState, action, previousState,
|
|
4
|
+
const onFilterHandler = (newState, action, previousState, currentFilter, instance) => {
|
|
5
5
|
const previousFilter = previousState.filters.find((f) => f.id === action.columnId);
|
|
6
6
|
if ((previousFilter === null || previousFilter === void 0 ? void 0 : previousFilter.value) != action.filterValue) {
|
|
7
7
|
const filters = newState.filters.map((f) => {
|
|
@@ -14,7 +14,8 @@ const onFilterHandler = (newState, action, previousState, instance, onFilter) =>
|
|
|
14
14
|
filterType: (_b = column === null || column === void 0 ? void 0 : column.filter) !== null && _b !== void 0 ? _b : 'text',
|
|
15
15
|
};
|
|
16
16
|
});
|
|
17
|
-
|
|
17
|
+
return filters;
|
|
18
18
|
}
|
|
19
|
+
return currentFilter;
|
|
19
20
|
};
|
|
20
21
|
exports.onFilterHandler = onFilterHandler;
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { StylingProps } from '../utils';
|
|
3
3
|
import '@itwin/itwinui-css/css/time-picker.css';
|
|
4
4
|
export declare type MeridiemType = 'AM' | 'PM';
|
|
5
|
+
export declare type Precision = 'hours' | 'minutes' | 'seconds';
|
|
5
6
|
export declare type TimePickerProps = {
|
|
6
7
|
/**
|
|
7
8
|
* Selected date. Only time is used from Date object.
|
|
@@ -20,7 +21,7 @@ export declare type TimePickerProps = {
|
|
|
20
21
|
* Precision of the time.
|
|
21
22
|
* @default 'minutes'
|
|
22
23
|
*/
|
|
23
|
-
precision?:
|
|
24
|
+
precision?: Precision;
|
|
24
25
|
/**
|
|
25
26
|
* Change step of the hours displayed.
|
|
26
27
|
* @default 1
|
|
@@ -61,6 +62,30 @@ export declare type TimePickerProps = {
|
|
|
61
62
|
* @default (meridiem: MeridiemType) => meridiem
|
|
62
63
|
*/
|
|
63
64
|
meridiemRenderer?: (meridiem: MeridiemType) => React.ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* Use combined time renderer. Combines hour, minute, and seconds into one column.
|
|
67
|
+
* **WARNING**: Using the combined renderer with a `precision` of 'seconds' along with
|
|
68
|
+
* small time steps (`hourStep`, `minuteStep`, and especially `secondStep`) can result in slow performance!
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
useCombinedRenderer?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Custom combined time renderer.
|
|
74
|
+
* Default returns time in `HH:MM:SS` format
|
|
75
|
+
* @default (date: Date, precision: Precision) => {
|
|
76
|
+
* let dateString = '';
|
|
77
|
+
* switch (precision) {
|
|
78
|
+
* case 'seconds':
|
|
79
|
+
* dateString = ':' + date.getSeconds().toLocaleString(undefined, { minimumIntegerDigits: 2 });
|
|
80
|
+
* case 'minutes':
|
|
81
|
+
* dateString = ':' + date.getMinutes().toLocaleString(undefined, { minimumIntegerDigits: 2 }) + dateString;
|
|
82
|
+
* case 'hours':
|
|
83
|
+
* dateString = date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }) + dateString;
|
|
84
|
+
* }
|
|
85
|
+
* return dateString;
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
combinedRenderer?: (date: Date, precision: Precision) => React.ReactNode;
|
|
64
89
|
} & StylingProps;
|
|
65
90
|
/**
|
|
66
91
|
* Time picker component
|
|
@@ -27,6 +27,24 @@ const isSameMinute = (date1, date2) => {
|
|
|
27
27
|
const isSameSecond = (date1, date2) => {
|
|
28
28
|
return !!date2 && date1.getSeconds() === date2.getSeconds();
|
|
29
29
|
};
|
|
30
|
+
const isSameTime = (date1, date2, precision, meridiem) => {
|
|
31
|
+
let isSameTime = true;
|
|
32
|
+
switch (precision) {
|
|
33
|
+
case 'seconds':
|
|
34
|
+
isSameTime = isSameSecond(date1, date2);
|
|
35
|
+
if (!isSameTime) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case 'minutes':
|
|
39
|
+
isSameTime = isSameMinute(date1, date2);
|
|
40
|
+
if (!isSameTime) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case 'hours':
|
|
44
|
+
isSameTime = isSameHour(date1, date2, meridiem);
|
|
45
|
+
}
|
|
46
|
+
return isSameTime;
|
|
47
|
+
};
|
|
30
48
|
const isSameMeridiem = (meridiem, date) => {
|
|
31
49
|
return (!!date && (meridiem === 'AM' ? date.getHours() < 12 : date.getHours() >= 12));
|
|
32
50
|
};
|
|
@@ -37,13 +55,36 @@ const formatHourFrom12 = (hour, meridiem) => {
|
|
|
37
55
|
const setHours = (hour, date) => {
|
|
38
56
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hour, date.getMinutes(), date.getSeconds());
|
|
39
57
|
};
|
|
58
|
+
const defaultCombinedRenderer = (date, precision) => {
|
|
59
|
+
let dateString = '';
|
|
60
|
+
switch (precision) {
|
|
61
|
+
case 'seconds':
|
|
62
|
+
dateString =
|
|
63
|
+
':' +
|
|
64
|
+
date
|
|
65
|
+
.getSeconds()
|
|
66
|
+
.toLocaleString(undefined, { minimumIntegerDigits: 2 });
|
|
67
|
+
case 'minutes':
|
|
68
|
+
dateString =
|
|
69
|
+
':' +
|
|
70
|
+
date
|
|
71
|
+
.getMinutes()
|
|
72
|
+
.toLocaleString(undefined, { minimumIntegerDigits: 2 }) +
|
|
73
|
+
dateString;
|
|
74
|
+
case 'hours':
|
|
75
|
+
dateString =
|
|
76
|
+
date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }) +
|
|
77
|
+
dateString;
|
|
78
|
+
}
|
|
79
|
+
return dateString;
|
|
80
|
+
};
|
|
40
81
|
/**
|
|
41
82
|
* Time picker component
|
|
42
83
|
* @example
|
|
43
84
|
* <TimePicker date={new Date()} onChange={(e) => console.log('New time value: ' + e)} />
|
|
44
85
|
*/
|
|
45
86
|
const TimePicker = (props) => {
|
|
46
|
-
const { date, onChange, use12Hours = false, precision = 'minutes', hourStep = 1, minuteStep = 1, secondStep = 1, setFocusHour = false, hourRenderer = (date) => date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }), minuteRenderer = (date) => date.getMinutes().toLocaleString(undefined, { minimumIntegerDigits: 2 }), secondRenderer = (date) => date.getSeconds().toLocaleString(undefined, { minimumIntegerDigits: 2 }), meridiemRenderer = (meridiem) => meridiem, className, ...rest } = props;
|
|
87
|
+
const { date, onChange, use12Hours = false, precision = 'minutes', hourStep = 1, minuteStep = 1, secondStep = 1, setFocusHour = false, hourRenderer = (date) => date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }), minuteRenderer = (date) => date.getMinutes().toLocaleString(undefined, { minimumIntegerDigits: 2 }), secondRenderer = (date) => date.getSeconds().toLocaleString(undefined, { minimumIntegerDigits: 2 }), meridiemRenderer = (meridiem) => meridiem, useCombinedRenderer = false, combinedRenderer = defaultCombinedRenderer, className, ...rest } = props;
|
|
47
88
|
(0, utils_1.useTheme)();
|
|
48
89
|
const [selectedTime, setSelectedTime] = react_1.default.useState(date);
|
|
49
90
|
const [focusedTime, setFocusedTime] = react_1.default.useState(selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date());
|
|
@@ -59,6 +100,13 @@ const TimePicker = (props) => {
|
|
|
59
100
|
const adjustedSelectedTime = setHours(adjustedHour, selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date());
|
|
60
101
|
updateCurrentTime(adjustedSelectedTime);
|
|
61
102
|
};
|
|
103
|
+
const onTimeClick = (date) => {
|
|
104
|
+
const adjustedHour = use12Hours
|
|
105
|
+
? formatHourFrom12(date.getHours(), meridiem)
|
|
106
|
+
: date.getHours();
|
|
107
|
+
const adjustedSelectedTime = setHours(adjustedHour, date);
|
|
108
|
+
updateCurrentTime(adjustedSelectedTime);
|
|
109
|
+
};
|
|
62
110
|
const onMeridiemClick = (value) => {
|
|
63
111
|
let adjustedSelectedTime = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
64
112
|
const currentHours = adjustedSelectedTime.getHours();
|
|
@@ -90,6 +138,12 @@ const TimePicker = (props) => {
|
|
|
90
138
|
: date.getHours();
|
|
91
139
|
setFocusedTime(setHours(adjustedHour, focusedTime));
|
|
92
140
|
};
|
|
141
|
+
const onTimeFocus = (date) => {
|
|
142
|
+
const adjustedHour = use12Hours
|
|
143
|
+
? formatHourFrom12(date.getHours(), meridiem)
|
|
144
|
+
: date.getHours();
|
|
145
|
+
setFocusedTime(setHours(adjustedHour, date));
|
|
146
|
+
};
|
|
93
147
|
const onMeridiemFocus = (value) => {
|
|
94
148
|
let adjustedSelectedTime = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
95
149
|
const currentHours = adjustedSelectedTime.getHours();
|
|
@@ -105,13 +159,40 @@ const TimePicker = (props) => {
|
|
|
105
159
|
};
|
|
106
160
|
const generateDataList = (size, value, step) => {
|
|
107
161
|
const data = [];
|
|
108
|
-
for (let i = 0; i < size; ++
|
|
162
|
+
for (let i = 0; i < size; i++) {
|
|
109
163
|
if (i % step === 0) {
|
|
110
164
|
data.push(value(i));
|
|
111
165
|
}
|
|
112
166
|
}
|
|
113
167
|
return data;
|
|
114
168
|
};
|
|
169
|
+
const time = react_1.default.useMemo(() => {
|
|
170
|
+
const time = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
171
|
+
const data = [];
|
|
172
|
+
const hoursArray = Array.from(Array(use12Hours ? 12 : 24).keys())
|
|
173
|
+
.filter((i) => i % hourStep === 0)
|
|
174
|
+
.map((i) => (use12Hours && i === 0 ? 12 : i));
|
|
175
|
+
const minutesArray = Array.from(Array(60).keys()).filter((i) => i % minuteStep === 0);
|
|
176
|
+
const secondsArray = Array.from(Array(60).keys()).filter((i) => i % secondStep === 0);
|
|
177
|
+
hoursArray.forEach((hour) => {
|
|
178
|
+
if (precision === 'hours') {
|
|
179
|
+
data.push(new Date(time.getFullYear(), time.getMonth(), time.getDate(), hour, time.getMinutes(), time.getSeconds()));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
minutesArray.forEach((minute) => {
|
|
183
|
+
if (precision === 'minutes') {
|
|
184
|
+
data.push(new Date(time.getFullYear(), time.getMonth(), time.getDate(), hour, minute, time.getSeconds()));
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
secondsArray.forEach((second) => {
|
|
188
|
+
data.push(new Date(time.getFullYear(), time.getMonth(), time.getDate(), hour, minute, second));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
return data;
|
|
195
|
+
}, [hourStep, minuteStep, secondStep, selectedTime, use12Hours, precision]);
|
|
115
196
|
const hours = react_1.default.useMemo(() => {
|
|
116
197
|
const time = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
117
198
|
return generateDataList(use12Hours ? 12 : 24, (i) => new Date(time.getFullYear(), time.getMonth(), time.getDate(), use12Hours && i === 0 ? 12 : i, time.getMinutes(), time.getSeconds()), hourStep);
|
|
@@ -125,14 +206,15 @@ const TimePicker = (props) => {
|
|
|
125
206
|
return generateDataList(60, (i) => new Date(time.getFullYear(), time.getMonth(), time.getDate(), time.getHours(), time.getMinutes(), i), secondStep);
|
|
126
207
|
}, [secondStep, selectedTime]);
|
|
127
208
|
return (react_1.default.createElement("div", { className: (0, classnames_1.default)('iui-time-picker', className), ...rest },
|
|
128
|
-
react_1.default.createElement(TimePickerColumn, { data:
|
|
129
|
-
|
|
130
|
-
|
|
209
|
+
useCombinedRenderer ? (react_1.default.createElement(TimePickerColumn, { data: time, isSameFocused: (val) => isSameTime(val, focusedTime, precision, use12Hours ? meridiem : undefined), isSameSelected: (val) => isSameTime(val, selectedTime, precision, use12Hours ? meridiem : undefined), onFocusChange: onTimeFocus, onSelectChange: onTimeClick, setFocus: setFocusHour, precision: precision, valueRenderer: combinedRenderer })) : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
210
|
+
react_1.default.createElement(TimePickerColumn, { data: hours, isSameFocused: (val) => isSameHour(val, focusedTime, use12Hours ? meridiem : undefined), isSameSelected: (val) => isSameHour(val, selectedTime, use12Hours ? meridiem : undefined), onFocusChange: onHourFocus, onSelectChange: onHourClick, setFocus: setFocusHour, valueRenderer: hourRenderer }),
|
|
211
|
+
precision !== 'hours' && (react_1.default.createElement(TimePickerColumn, { data: minutes, isSameFocused: (val) => isSameMinute(val, focusedTime), isSameSelected: (val) => isSameMinute(val, selectedTime), onFocusChange: (date) => setFocusedTime(date), onSelectChange: (date) => updateCurrentTime(date), valueRenderer: minuteRenderer })),
|
|
212
|
+
precision === 'seconds' && (react_1.default.createElement(TimePickerColumn, { data: seconds, isSameFocused: (val) => isSameSecond(val, focusedTime), isSameSelected: (val) => isSameSecond(val, selectedTime), onFocusChange: (date) => setFocusedTime(date), onSelectChange: (date) => updateCurrentTime(date), valueRenderer: secondRenderer })))),
|
|
131
213
|
use12Hours && (react_1.default.createElement(TimePickerColumn, { data: ['AM', 'PM'], isSameFocused: (val) => isSameMeridiem(val, focusedTime), isSameSelected: (val) => isSameMeridiem(val, selectedTime), onFocusChange: (date) => onMeridiemFocus(date), onSelectChange: (value) => onMeridiemClick(value), valueRenderer: meridiemRenderer, className: 'iui-period' }))));
|
|
132
214
|
};
|
|
133
215
|
exports.TimePicker = TimePicker;
|
|
134
216
|
const TimePickerColumn = (props) => {
|
|
135
|
-
const { data, onFocusChange, onSelectChange, isSameFocused, isSameSelected, setFocus = false, valueRenderer, className = 'iui-time', } = props;
|
|
217
|
+
const { data, onFocusChange, onSelectChange, isSameFocused, isSameSelected, setFocus = false, valueRenderer, precision = 'minutes', className = 'iui-time', } = props;
|
|
136
218
|
const needFocus = react_1.default.useRef(setFocus);
|
|
137
219
|
// Used to focus row only when changed (keyboard navigation)
|
|
138
220
|
// e.g. without this on every rerender it would be focused
|
|
@@ -182,7 +264,7 @@ const TimePickerColumn = (props) => {
|
|
|
182
264
|
needFocus.current && isSameFocus && (ref === null || ref === void 0 ? void 0 : ref.focus());
|
|
183
265
|
}, onClick: () => {
|
|
184
266
|
onSelectChange(value);
|
|
185
|
-
} }, valueRenderer(value)));
|
|
267
|
+
} }, valueRenderer(value, precision)));
|
|
186
268
|
}))));
|
|
187
269
|
};
|
|
188
270
|
exports.default = exports.TimePicker;
|
package/cjs/core/index.d.ts
CHANGED
|
@@ -60,6 +60,8 @@ export { Menu, MenuItem, MenuDivider, MenuExtraContent, MenuItemSkeleton, } from
|
|
|
60
60
|
export type { MenuProps, MenuItemProps, MenuDividerProps, MenuExtraContentProps, MenuItemSkeletonProps, } from './Menu';
|
|
61
61
|
export { Modal, ModalButtonBar, ModalContent } from './Modal';
|
|
62
62
|
export type { ModalProps, ModalButtonBarProps, ModalContentProps, } from './Modal';
|
|
63
|
+
export { NotificationMarker } from './NotificationMarker';
|
|
64
|
+
export type { NotificationMarkerProps } from './NotificationMarker';
|
|
63
65
|
export { ProgressLinear, ProgressRadial } from './ProgressIndicators';
|
|
64
66
|
export type { ProgressLinearProps, ProgressRadialProps, } from './ProgressIndicators';
|
|
65
67
|
export { Radio } from './Radio';
|
package/cjs/core/index.js
CHANGED
|
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.LabeledInput = exports.Label = exports.Input = exports.InformationPanelContent = exports.InformationPanelBody = exports.InformationPanelHeader = exports.InformationPanelWrapper = exports.InformationPanel = exports.HorizontalTabs = exports.Tab = exports.Tabs = exports.VerticalTabs = exports.HeaderLogo = exports.HeaderButton = exports.HeaderBreadcrumbs = exports.Header = exports.defaultFooterElements = exports.Footer = exports.FileUploadTemplate = exports.FileUpload = exports.Fieldset = exports.ExpandableBlock = exports.NonIdealState = exports.ErrorPage = exports.DropdownMenu = exports.Dialog = exports.generateLocalizedStrings = exports.DatePicker = exports.ComboBox = exports.ColorPalette = exports.ColorInputPanel = exports.ColorBuilder = exports.ColorSwatch = exports.ColorPicker = exports.Checkbox = exports.Carousel = exports.ButtonGroup = exports.SplitButton = exports.IdeasButton = exports.IconButton = exports.DropdownButton = exports.Button = exports.Breadcrumbs = exports.Badge = exports.Backdrop = exports.UserIconGroup = exports.AvatarGroup = exports.UserIcon = exports.Avatar = exports.Alert = void 0;
|
|
7
|
-
exports.
|
|
8
|
-
exports.MiddleTextTruncation = exports.ColorValue = exports.useTheme = exports.getUserColor = exports.WorkflowDiagram = exports.Stepper = exports.Wizard = exports.Text = exports.KbdKeys = exports.Kbd = exports.Code = exports.Blockquote = exports.Title = exports.Subheading = exports.Small = exports.Leading = void 0;
|
|
7
|
+
exports.Body = exports.Anchor = exports.TreeNodeExpander = exports.TreeNode = exports.Tree = exports.Tooltip = exports.ToggleSwitch = exports.ThemeProvider = exports.toaster = exports.TimePicker = exports.Tile = exports.Textarea = exports.TagContainer = exports.Tag = exports.SelectionColumn = exports.ExpanderColumn = exports.ActionColumn = exports.TablePaginator = exports.EditableCell = exports.DefaultCell = exports.FilterButtonBar = exports.BaseFilter = exports.tableFilters = exports.Table = exports.Surface = exports.StatusMessage = exports.Slider = exports.SkipToContentLink = exports.SidenavSubmenuHeader = exports.SidenavSubmenu = exports.SidenavButton = exports.SideNavigation = exports.Select = exports.RadioTileGroup = exports.RadioTile = exports.Radio = exports.ProgressRadial = exports.ProgressLinear = exports.NotificationMarker = exports.ModalContent = exports.ModalButtonBar = exports.Modal = exports.MenuItemSkeleton = exports.MenuExtraContent = exports.MenuDivider = exports.MenuItem = exports.Menu = exports.LabeledTextarea = exports.LabeledSelect = exports.InputGroup = void 0;
|
|
8
|
+
exports.MiddleTextTruncation = exports.ColorValue = exports.useTheme = exports.getUserColor = exports.WorkflowDiagram = exports.Stepper = exports.Wizard = exports.Text = exports.KbdKeys = exports.Kbd = exports.Code = exports.Blockquote = exports.Title = exports.Subheading = exports.Small = exports.Leading = exports.Headline = void 0;
|
|
9
9
|
/*---------------------------------------------------------------------------------------------
|
|
10
10
|
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
11
11
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
@@ -102,6 +102,8 @@ var Modal_1 = require("./Modal");
|
|
|
102
102
|
Object.defineProperty(exports, "Modal", { enumerable: true, get: function () { return Modal_1.Modal; } });
|
|
103
103
|
Object.defineProperty(exports, "ModalButtonBar", { enumerable: true, get: function () { return Modal_1.ModalButtonBar; } });
|
|
104
104
|
Object.defineProperty(exports, "ModalContent", { enumerable: true, get: function () { return Modal_1.ModalContent; } });
|
|
105
|
+
var NotificationMarker_1 = require("./NotificationMarker");
|
|
106
|
+
Object.defineProperty(exports, "NotificationMarker", { enumerable: true, get: function () { return NotificationMarker_1.NotificationMarker; } });
|
|
105
107
|
var ProgressIndicators_1 = require("./ProgressIndicators");
|
|
106
108
|
Object.defineProperty(exports, "ProgressLinear", { enumerable: true, get: function () { return ProgressIndicators_1.ProgressLinear; } });
|
|
107
109
|
Object.defineProperty(exports, "ProgressRadial", { enumerable: true, get: function () { return ProgressIndicators_1.ProgressRadial; } });
|
|
@@ -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, 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, ...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;
|
|
@@ -361,7 +361,7 @@ export const DatePicker = (props) => {
|
|
|
361
361
|
(element === null || element === void 0 ? void 0 : element.focus()) }, dateValue));
|
|
362
362
|
})));
|
|
363
363
|
}))),
|
|
364
|
-
showTime && (React.createElement(TimePicker, { date: selectedStartDay !== null && selectedStartDay !== void 0 ? selectedStartDay : selectedDay, use12Hours: use12Hours, precision: precision, hourStep: hourStep, minuteStep: minuteStep, secondStep: secondStep, onChange: (date) => {
|
|
364
|
+
showTime && (React.createElement(TimePicker, { date: selectedStartDay !== null && selectedStartDay !== void 0 ? selectedStartDay : selectedDay, use12Hours: use12Hours, precision: precision, hourStep: hourStep, minuteStep: minuteStep, secondStep: secondStep, useCombinedRenderer: useCombinedRenderer, combinedRenderer: combinedRenderer, hourRenderer: hourRenderer, minuteRenderer: minuteRenderer, secondRenderer: secondRenderer, meridiemRenderer: meridiemRenderer, onChange: (date) => {
|
|
365
365
|
var _a, _b, _c, _d, _e, _f;
|
|
366
366
|
return isSingleOnChange(onChange, enableRangeSelect)
|
|
367
367
|
? onChange === null || onChange === void 0 ? void 0 : onChange(date)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '@itwin/itwinui-css/css/utils.css';
|
|
3
|
+
export declare type NotificationMarkerProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Content of the NotificationMarker.
|
|
6
|
+
*/
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Status of notification
|
|
10
|
+
*
|
|
11
|
+
* - 'primary' = blue,
|
|
12
|
+
* - 'positive' = green,
|
|
13
|
+
* - 'warning' = orange,
|
|
14
|
+
* - 'negative' = red,
|
|
15
|
+
* - 'white' = white (useful for notifications in buttons with `style` = `high-visibility` | `cta`),
|
|
16
|
+
* @default 'primary'
|
|
17
|
+
*/
|
|
18
|
+
status?: 'primary' | 'positive' | 'warning' | 'negative' | 'white';
|
|
19
|
+
/**
|
|
20
|
+
* Adds a pulse effect to the notification.
|
|
21
|
+
*
|
|
22
|
+
* **WARNING**: Avoid overuse of this prop.
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
pulsing?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Set this programmatically to false when you just want to render the passed children without the notification
|
|
28
|
+
* @default true
|
|
29
|
+
* @example
|
|
30
|
+
* let [newMessagesCount, ...] = useState(0);
|
|
31
|
+
* ...
|
|
32
|
+
* <NotificationMarker enabled={newMessagesCount > 0}>
|
|
33
|
+
* <SvgNotification />
|
|
34
|
+
* </NotificationMarker>
|
|
35
|
+
*/
|
|
36
|
+
enabled?: boolean;
|
|
37
|
+
} & React.ComponentProps<'span'>;
|
|
38
|
+
/**
|
|
39
|
+
* A small notification circle to the top-right of the passed children prop.
|
|
40
|
+
* This can be applied to almost anything but mostly intended for icons within buttons with `styleType = default / borderless`.
|
|
41
|
+
* @example
|
|
42
|
+
* <IconButton styleType='borderless'>
|
|
43
|
+
* <NotificationMarker>
|
|
44
|
+
* <SvgNotification />
|
|
45
|
+
* </NotificationMarker>
|
|
46
|
+
* </IconButton>
|
|
47
|
+
* @example
|
|
48
|
+
* <NotificationMarker status='positive' pulsing={true}>Live</NotificationMarker>
|
|
49
|
+
*/
|
|
50
|
+
export declare const NotificationMarker: React.ForwardRefExoticComponent<Pick<NotificationMarkerProps, "key" | "status" | keyof React.HTMLAttributes<HTMLSpanElement> | "enabled" | "pulsing"> & React.RefAttributes<HTMLSpanElement>>;
|
|
51
|
+
export default NotificationMarker;
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
import React from 'react';
|
|
6
|
+
import { useTheme } from '../utils';
|
|
7
|
+
import cx from 'classnames';
|
|
8
|
+
import '@itwin/itwinui-css/css/utils.css';
|
|
9
|
+
/**
|
|
10
|
+
* A small notification circle to the top-right of the passed children prop.
|
|
11
|
+
* This can be applied to almost anything but mostly intended for icons within buttons with `styleType = default / borderless`.
|
|
12
|
+
* @example
|
|
13
|
+
* <IconButton styleType='borderless'>
|
|
14
|
+
* <NotificationMarker>
|
|
15
|
+
* <SvgNotification />
|
|
16
|
+
* </NotificationMarker>
|
|
17
|
+
* </IconButton>
|
|
18
|
+
* @example
|
|
19
|
+
* <NotificationMarker status='positive' pulsing={true}>Live</NotificationMarker>
|
|
20
|
+
*/
|
|
21
|
+
export const NotificationMarker = React.forwardRef((props, ref) => {
|
|
22
|
+
const { className, children, status = 'primary', pulsing = false, enabled = true, ...rest } = props;
|
|
23
|
+
useTheme();
|
|
24
|
+
return (React.createElement("span", { ref: ref, className: cx({ 'iui-notification-marker': enabled }, className), "data-iui-variant": enabled ? status : null, "data-iui-urgent": enabled ? pulsing : null, ...rest }, children));
|
|
25
|
+
});
|
|
26
|
+
export default NotificationMarker;
|
|
@@ -0,0 +1,6 @@
|
|
|
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
|
+
export { NotificationMarker } from './NotificationMarker';
|
|
6
|
+
export default './NotificationMarker';
|
|
@@ -117,7 +117,7 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
|
|
|
117
117
|
* Use with `manualFilters` to handle filtering yourself e.g. filter in server-side.
|
|
118
118
|
* Must be memoized.
|
|
119
119
|
*/
|
|
120
|
-
onFilter?: (filters: TableFilterValue<T>[], state: TableState<T>) => void;
|
|
120
|
+
onFilter?: (filters: TableFilterValue<T>[], state: TableState<T>, filteredData?: Row<T>[]) => void;
|
|
121
121
|
/**
|
|
122
122
|
* Value used for global filtering.
|
|
123
123
|
* Use with `globalFilter` and/or `manualGlobalFilter` to handle filtering yourself e.g. filter in server-side.
|
package/esm/core/Table/Table.js
CHANGED
|
@@ -117,13 +117,15 @@ export const Table = (props) => {
|
|
|
117
117
|
disableUserSelect,
|
|
118
118
|
enableUserSelect,
|
|
119
119
|
]);
|
|
120
|
+
const previousFilter = React.useRef([]);
|
|
121
|
+
const currentFilter = React.useRef(previousFilter.current);
|
|
120
122
|
const tableStateReducer = React.useCallback((newState, action, previousState, instance) => {
|
|
121
123
|
switch (action.type) {
|
|
122
124
|
case TableActions.toggleSortBy:
|
|
123
125
|
onSort === null || onSort === void 0 ? void 0 : onSort(newState);
|
|
124
126
|
break;
|
|
125
127
|
case TableActions.setFilter:
|
|
126
|
-
onFilterHandler(newState, action, previousState,
|
|
128
|
+
currentFilter.current = onFilterHandler(newState, action, previousState, currentFilter.current, instance);
|
|
127
129
|
break;
|
|
128
130
|
case TableActions.toggleRowExpanded:
|
|
129
131
|
case TableActions.toggleAllRowsExpanded:
|
|
@@ -167,7 +169,6 @@ export const Table = (props) => {
|
|
|
167
169
|
hasManualSelectionColumn,
|
|
168
170
|
isRowDisabled,
|
|
169
171
|
onExpand,
|
|
170
|
-
onFilter,
|
|
171
172
|
onSelect,
|
|
172
173
|
onSort,
|
|
173
174
|
stateReducer,
|
|
@@ -243,6 +244,22 @@ export const Table = (props) => {
|
|
|
243
244
|
React.useEffect(() => {
|
|
244
245
|
setPageSize(pageSize);
|
|
245
246
|
}, [pageSize, setPageSize]);
|
|
247
|
+
React.useEffect(() => {
|
|
248
|
+
if (previousFilter.current !== currentFilter.current) {
|
|
249
|
+
previousFilter.current = currentFilter.current;
|
|
250
|
+
onFilter === null || onFilter === void 0 ? void 0 : onFilter(currentFilter.current, state, instance.filteredRows);
|
|
251
|
+
}
|
|
252
|
+
}, [state, instance.filteredRows, onFilter]);
|
|
253
|
+
const lastPassedColumns = React.useRef([]);
|
|
254
|
+
// Reset the column order whenever new columns are passed
|
|
255
|
+
// This is to avoid the old columnOrder from affecting the new columns' columnOrder
|
|
256
|
+
React.useEffect(() => {
|
|
257
|
+
// Check if columns have changed (by value)
|
|
258
|
+
if (JSON.stringify(lastPassedColumns.current) !== JSON.stringify(columns)) {
|
|
259
|
+
instance.setColumnOrder([]);
|
|
260
|
+
}
|
|
261
|
+
lastPassedColumns.current = columns;
|
|
262
|
+
}, [columns, instance]);
|
|
246
263
|
const paginatorRendererProps = React.useMemo(() => ({
|
|
247
264
|
currentPage: state.pageIndex,
|
|
248
265
|
pageSize: state.pageSize,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ActionType, TableInstance, TableState } from 'react-table';
|
|
2
2
|
import { TableFilterValue } from '../filters';
|
|
3
|
-
export declare const onFilterHandler: <T extends Record<string, unknown>>(newState: TableState<T>, action: ActionType, previousState: TableState<T>,
|
|
3
|
+
export declare const onFilterHandler: <T extends Record<string, unknown>>(newState: TableState<T>, action: ActionType, previousState: TableState<T>, currentFilter: TableFilterValue<T>[], instance?: TableInstance<T> | undefined) => TableFilterValue<T>[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const onFilterHandler = (newState, action, previousState,
|
|
1
|
+
export const onFilterHandler = (newState, action, previousState, currentFilter, instance) => {
|
|
2
2
|
const previousFilter = previousState.filters.find((f) => f.id === action.columnId);
|
|
3
3
|
if ((previousFilter === null || previousFilter === void 0 ? void 0 : previousFilter.value) != action.filterValue) {
|
|
4
4
|
const filters = newState.filters.map((f) => {
|
|
@@ -11,6 +11,7 @@ export const onFilterHandler = (newState, action, previousState, instance, onFil
|
|
|
11
11
|
filterType: (_b = column === null || column === void 0 ? void 0 : column.filter) !== null && _b !== void 0 ? _b : 'text',
|
|
12
12
|
};
|
|
13
13
|
});
|
|
14
|
-
|
|
14
|
+
return filters;
|
|
15
15
|
}
|
|
16
|
+
return currentFilter;
|
|
16
17
|
};
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { StylingProps } from '../utils';
|
|
3
3
|
import '@itwin/itwinui-css/css/time-picker.css';
|
|
4
4
|
export declare type MeridiemType = 'AM' | 'PM';
|
|
5
|
+
export declare type Precision = 'hours' | 'minutes' | 'seconds';
|
|
5
6
|
export declare type TimePickerProps = {
|
|
6
7
|
/**
|
|
7
8
|
* Selected date. Only time is used from Date object.
|
|
@@ -20,7 +21,7 @@ export declare type TimePickerProps = {
|
|
|
20
21
|
* Precision of the time.
|
|
21
22
|
* @default 'minutes'
|
|
22
23
|
*/
|
|
23
|
-
precision?:
|
|
24
|
+
precision?: Precision;
|
|
24
25
|
/**
|
|
25
26
|
* Change step of the hours displayed.
|
|
26
27
|
* @default 1
|
|
@@ -61,6 +62,30 @@ export declare type TimePickerProps = {
|
|
|
61
62
|
* @default (meridiem: MeridiemType) => meridiem
|
|
62
63
|
*/
|
|
63
64
|
meridiemRenderer?: (meridiem: MeridiemType) => React.ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* Use combined time renderer. Combines hour, minute, and seconds into one column.
|
|
67
|
+
* **WARNING**: Using the combined renderer with a `precision` of 'seconds' along with
|
|
68
|
+
* small time steps (`hourStep`, `minuteStep`, and especially `secondStep`) can result in slow performance!
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
useCombinedRenderer?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Custom combined time renderer.
|
|
74
|
+
* Default returns time in `HH:MM:SS` format
|
|
75
|
+
* @default (date: Date, precision: Precision) => {
|
|
76
|
+
* let dateString = '';
|
|
77
|
+
* switch (precision) {
|
|
78
|
+
* case 'seconds':
|
|
79
|
+
* dateString = ':' + date.getSeconds().toLocaleString(undefined, { minimumIntegerDigits: 2 });
|
|
80
|
+
* case 'minutes':
|
|
81
|
+
* dateString = ':' + date.getMinutes().toLocaleString(undefined, { minimumIntegerDigits: 2 }) + dateString;
|
|
82
|
+
* case 'hours':
|
|
83
|
+
* dateString = date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }) + dateString;
|
|
84
|
+
* }
|
|
85
|
+
* return dateString;
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
combinedRenderer?: (date: Date, precision: Precision) => React.ReactNode;
|
|
64
89
|
} & StylingProps;
|
|
65
90
|
/**
|
|
66
91
|
* Time picker component
|
|
@@ -21,6 +21,24 @@ const isSameMinute = (date1, date2) => {
|
|
|
21
21
|
const isSameSecond = (date1, date2) => {
|
|
22
22
|
return !!date2 && date1.getSeconds() === date2.getSeconds();
|
|
23
23
|
};
|
|
24
|
+
const isSameTime = (date1, date2, precision, meridiem) => {
|
|
25
|
+
let isSameTime = true;
|
|
26
|
+
switch (precision) {
|
|
27
|
+
case 'seconds':
|
|
28
|
+
isSameTime = isSameSecond(date1, date2);
|
|
29
|
+
if (!isSameTime) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case 'minutes':
|
|
33
|
+
isSameTime = isSameMinute(date1, date2);
|
|
34
|
+
if (!isSameTime) {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'hours':
|
|
38
|
+
isSameTime = isSameHour(date1, date2, meridiem);
|
|
39
|
+
}
|
|
40
|
+
return isSameTime;
|
|
41
|
+
};
|
|
24
42
|
const isSameMeridiem = (meridiem, date) => {
|
|
25
43
|
return (!!date && (meridiem === 'AM' ? date.getHours() < 12 : date.getHours() >= 12));
|
|
26
44
|
};
|
|
@@ -31,13 +49,36 @@ const formatHourFrom12 = (hour, meridiem) => {
|
|
|
31
49
|
const setHours = (hour, date) => {
|
|
32
50
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hour, date.getMinutes(), date.getSeconds());
|
|
33
51
|
};
|
|
52
|
+
const defaultCombinedRenderer = (date, precision) => {
|
|
53
|
+
let dateString = '';
|
|
54
|
+
switch (precision) {
|
|
55
|
+
case 'seconds':
|
|
56
|
+
dateString =
|
|
57
|
+
':' +
|
|
58
|
+
date
|
|
59
|
+
.getSeconds()
|
|
60
|
+
.toLocaleString(undefined, { minimumIntegerDigits: 2 });
|
|
61
|
+
case 'minutes':
|
|
62
|
+
dateString =
|
|
63
|
+
':' +
|
|
64
|
+
date
|
|
65
|
+
.getMinutes()
|
|
66
|
+
.toLocaleString(undefined, { minimumIntegerDigits: 2 }) +
|
|
67
|
+
dateString;
|
|
68
|
+
case 'hours':
|
|
69
|
+
dateString =
|
|
70
|
+
date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }) +
|
|
71
|
+
dateString;
|
|
72
|
+
}
|
|
73
|
+
return dateString;
|
|
74
|
+
};
|
|
34
75
|
/**
|
|
35
76
|
* Time picker component
|
|
36
77
|
* @example
|
|
37
78
|
* <TimePicker date={new Date()} onChange={(e) => console.log('New time value: ' + e)} />
|
|
38
79
|
*/
|
|
39
80
|
export const TimePicker = (props) => {
|
|
40
|
-
const { date, onChange, use12Hours = false, precision = 'minutes', hourStep = 1, minuteStep = 1, secondStep = 1, setFocusHour = false, hourRenderer = (date) => date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }), minuteRenderer = (date) => date.getMinutes().toLocaleString(undefined, { minimumIntegerDigits: 2 }), secondRenderer = (date) => date.getSeconds().toLocaleString(undefined, { minimumIntegerDigits: 2 }), meridiemRenderer = (meridiem) => meridiem, className, ...rest } = props;
|
|
81
|
+
const { date, onChange, use12Hours = false, precision = 'minutes', hourStep = 1, minuteStep = 1, secondStep = 1, setFocusHour = false, hourRenderer = (date) => date.getHours().toLocaleString(undefined, { minimumIntegerDigits: 2 }), minuteRenderer = (date) => date.getMinutes().toLocaleString(undefined, { minimumIntegerDigits: 2 }), secondRenderer = (date) => date.getSeconds().toLocaleString(undefined, { minimumIntegerDigits: 2 }), meridiemRenderer = (meridiem) => meridiem, useCombinedRenderer = false, combinedRenderer = defaultCombinedRenderer, className, ...rest } = props;
|
|
41
82
|
useTheme();
|
|
42
83
|
const [selectedTime, setSelectedTime] = React.useState(date);
|
|
43
84
|
const [focusedTime, setFocusedTime] = React.useState(selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date());
|
|
@@ -53,6 +94,13 @@ export const TimePicker = (props) => {
|
|
|
53
94
|
const adjustedSelectedTime = setHours(adjustedHour, selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date());
|
|
54
95
|
updateCurrentTime(adjustedSelectedTime);
|
|
55
96
|
};
|
|
97
|
+
const onTimeClick = (date) => {
|
|
98
|
+
const adjustedHour = use12Hours
|
|
99
|
+
? formatHourFrom12(date.getHours(), meridiem)
|
|
100
|
+
: date.getHours();
|
|
101
|
+
const adjustedSelectedTime = setHours(adjustedHour, date);
|
|
102
|
+
updateCurrentTime(adjustedSelectedTime);
|
|
103
|
+
};
|
|
56
104
|
const onMeridiemClick = (value) => {
|
|
57
105
|
let adjustedSelectedTime = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
58
106
|
const currentHours = adjustedSelectedTime.getHours();
|
|
@@ -84,6 +132,12 @@ export const TimePicker = (props) => {
|
|
|
84
132
|
: date.getHours();
|
|
85
133
|
setFocusedTime(setHours(adjustedHour, focusedTime));
|
|
86
134
|
};
|
|
135
|
+
const onTimeFocus = (date) => {
|
|
136
|
+
const adjustedHour = use12Hours
|
|
137
|
+
? formatHourFrom12(date.getHours(), meridiem)
|
|
138
|
+
: date.getHours();
|
|
139
|
+
setFocusedTime(setHours(adjustedHour, date));
|
|
140
|
+
};
|
|
87
141
|
const onMeridiemFocus = (value) => {
|
|
88
142
|
let adjustedSelectedTime = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
89
143
|
const currentHours = adjustedSelectedTime.getHours();
|
|
@@ -99,13 +153,40 @@ export const TimePicker = (props) => {
|
|
|
99
153
|
};
|
|
100
154
|
const generateDataList = (size, value, step) => {
|
|
101
155
|
const data = [];
|
|
102
|
-
for (let i = 0; i < size; ++
|
|
156
|
+
for (let i = 0; i < size; i++) {
|
|
103
157
|
if (i % step === 0) {
|
|
104
158
|
data.push(value(i));
|
|
105
159
|
}
|
|
106
160
|
}
|
|
107
161
|
return data;
|
|
108
162
|
};
|
|
163
|
+
const time = React.useMemo(() => {
|
|
164
|
+
const time = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
165
|
+
const data = [];
|
|
166
|
+
const hoursArray = Array.from(Array(use12Hours ? 12 : 24).keys())
|
|
167
|
+
.filter((i) => i % hourStep === 0)
|
|
168
|
+
.map((i) => (use12Hours && i === 0 ? 12 : i));
|
|
169
|
+
const minutesArray = Array.from(Array(60).keys()).filter((i) => i % minuteStep === 0);
|
|
170
|
+
const secondsArray = Array.from(Array(60).keys()).filter((i) => i % secondStep === 0);
|
|
171
|
+
hoursArray.forEach((hour) => {
|
|
172
|
+
if (precision === 'hours') {
|
|
173
|
+
data.push(new Date(time.getFullYear(), time.getMonth(), time.getDate(), hour, time.getMinutes(), time.getSeconds()));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
minutesArray.forEach((minute) => {
|
|
177
|
+
if (precision === 'minutes') {
|
|
178
|
+
data.push(new Date(time.getFullYear(), time.getMonth(), time.getDate(), hour, minute, time.getSeconds()));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
secondsArray.forEach((second) => {
|
|
182
|
+
data.push(new Date(time.getFullYear(), time.getMonth(), time.getDate(), hour, minute, second));
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return data;
|
|
189
|
+
}, [hourStep, minuteStep, secondStep, selectedTime, use12Hours, precision]);
|
|
109
190
|
const hours = React.useMemo(() => {
|
|
110
191
|
const time = selectedTime !== null && selectedTime !== void 0 ? selectedTime : new Date();
|
|
111
192
|
return generateDataList(use12Hours ? 12 : 24, (i) => new Date(time.getFullYear(), time.getMonth(), time.getDate(), use12Hours && i === 0 ? 12 : i, time.getMinutes(), time.getSeconds()), hourStep);
|
|
@@ -119,13 +200,14 @@ export const TimePicker = (props) => {
|
|
|
119
200
|
return generateDataList(60, (i) => new Date(time.getFullYear(), time.getMonth(), time.getDate(), time.getHours(), time.getMinutes(), i), secondStep);
|
|
120
201
|
}, [secondStep, selectedTime]);
|
|
121
202
|
return (React.createElement("div", { className: cx('iui-time-picker', className), ...rest },
|
|
122
|
-
React.createElement(TimePickerColumn, { data:
|
|
123
|
-
|
|
124
|
-
|
|
203
|
+
useCombinedRenderer ? (React.createElement(TimePickerColumn, { data: time, isSameFocused: (val) => isSameTime(val, focusedTime, precision, use12Hours ? meridiem : undefined), isSameSelected: (val) => isSameTime(val, selectedTime, precision, use12Hours ? meridiem : undefined), onFocusChange: onTimeFocus, onSelectChange: onTimeClick, setFocus: setFocusHour, precision: precision, valueRenderer: combinedRenderer })) : (React.createElement(React.Fragment, null,
|
|
204
|
+
React.createElement(TimePickerColumn, { data: hours, isSameFocused: (val) => isSameHour(val, focusedTime, use12Hours ? meridiem : undefined), isSameSelected: (val) => isSameHour(val, selectedTime, use12Hours ? meridiem : undefined), onFocusChange: onHourFocus, onSelectChange: onHourClick, setFocus: setFocusHour, valueRenderer: hourRenderer }),
|
|
205
|
+
precision !== 'hours' && (React.createElement(TimePickerColumn, { data: minutes, isSameFocused: (val) => isSameMinute(val, focusedTime), isSameSelected: (val) => isSameMinute(val, selectedTime), onFocusChange: (date) => setFocusedTime(date), onSelectChange: (date) => updateCurrentTime(date), valueRenderer: minuteRenderer })),
|
|
206
|
+
precision === 'seconds' && (React.createElement(TimePickerColumn, { data: seconds, isSameFocused: (val) => isSameSecond(val, focusedTime), isSameSelected: (val) => isSameSecond(val, selectedTime), onFocusChange: (date) => setFocusedTime(date), onSelectChange: (date) => updateCurrentTime(date), valueRenderer: secondRenderer })))),
|
|
125
207
|
use12Hours && (React.createElement(TimePickerColumn, { data: ['AM', 'PM'], isSameFocused: (val) => isSameMeridiem(val, focusedTime), isSameSelected: (val) => isSameMeridiem(val, selectedTime), onFocusChange: (date) => onMeridiemFocus(date), onSelectChange: (value) => onMeridiemClick(value), valueRenderer: meridiemRenderer, className: 'iui-period' }))));
|
|
126
208
|
};
|
|
127
209
|
const TimePickerColumn = (props) => {
|
|
128
|
-
const { data, onFocusChange, onSelectChange, isSameFocused, isSameSelected, setFocus = false, valueRenderer, className = 'iui-time', } = props;
|
|
210
|
+
const { data, onFocusChange, onSelectChange, isSameFocused, isSameSelected, setFocus = false, valueRenderer, precision = 'minutes', className = 'iui-time', } = props;
|
|
129
211
|
const needFocus = React.useRef(setFocus);
|
|
130
212
|
// Used to focus row only when changed (keyboard navigation)
|
|
131
213
|
// e.g. without this on every rerender it would be focused
|
|
@@ -175,7 +257,7 @@ const TimePickerColumn = (props) => {
|
|
|
175
257
|
needFocus.current && isSameFocus && (ref === null || ref === void 0 ? void 0 : ref.focus());
|
|
176
258
|
}, onClick: () => {
|
|
177
259
|
onSelectChange(value);
|
|
178
|
-
} }, valueRenderer(value)));
|
|
260
|
+
} }, valueRenderer(value, precision)));
|
|
179
261
|
}))));
|
|
180
262
|
};
|
|
181
263
|
export default TimePicker;
|
package/esm/core/index.d.ts
CHANGED
|
@@ -60,6 +60,8 @@ export { Menu, MenuItem, MenuDivider, MenuExtraContent, MenuItemSkeleton, } from
|
|
|
60
60
|
export type { MenuProps, MenuItemProps, MenuDividerProps, MenuExtraContentProps, MenuItemSkeletonProps, } from './Menu';
|
|
61
61
|
export { Modal, ModalButtonBar, ModalContent } from './Modal';
|
|
62
62
|
export type { ModalProps, ModalButtonBarProps, ModalContentProps, } from './Modal';
|
|
63
|
+
export { NotificationMarker } from './NotificationMarker';
|
|
64
|
+
export type { NotificationMarkerProps } from './NotificationMarker';
|
|
63
65
|
export { ProgressLinear, ProgressRadial } from './ProgressIndicators';
|
|
64
66
|
export type { ProgressLinearProps, ProgressRadialProps, } from './ProgressIndicators';
|
|
65
67
|
export { Radio } from './Radio';
|
package/esm/core/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export { LabeledSelect } from './LabeledSelect';
|
|
|
33
33
|
export { LabeledTextarea } from './LabeledTextarea';
|
|
34
34
|
export { Menu, MenuItem, MenuDivider, MenuExtraContent, MenuItemSkeleton, } from './Menu';
|
|
35
35
|
export { Modal, ModalButtonBar, ModalContent } from './Modal';
|
|
36
|
+
export { NotificationMarker } from './NotificationMarker';
|
|
36
37
|
export { ProgressLinear, ProgressRadial } from './ProgressIndicators';
|
|
37
38
|
export { Radio } from './Radio';
|
|
38
39
|
export { RadioTile, RadioTileGroup } from './RadioTiles';
|