@neo4j-ndl/react 4.0.19 → 4.1.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/lib/cjs/date-picker/DatePicker.js +11 -3
- package/lib/cjs/date-picker/DatePicker.js.map +1 -1
- package/lib/cjs/date-picker/stories/date-picker-with-timezone-both-mode.story.js +57 -0
- package/lib/cjs/date-picker/stories/date-picker-with-timezone-both-mode.story.js.map +1 -0
- package/lib/cjs/date-picker/stories/date-picker-with-timezone.story.js +52 -0
- package/lib/cjs/date-picker/stories/date-picker-with-timezone.story.js.map +1 -0
- package/lib/cjs/date-picker/stories/date-picker.stories.js +27 -1
- package/lib/cjs/date-picker/stories/date-picker.stories.js.map +1 -1
- package/lib/cjs/date-picker/stories/index.js +9 -1
- package/lib/cjs/date-picker/stories/index.js.map +1 -1
- package/lib/cjs/dropzone/Dropzone.js +1 -1
- package/lib/cjs/dropzone/Dropzone.js.map +1 -1
- package/lib/cjs/index.js +4 -2
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/timezone-picker/TimeZonePicker.js +330 -0
- package/lib/cjs/timezone-picker/TimeZonePicker.js.map +1 -0
- package/lib/cjs/timezone-picker/generate-timezone-options.js +256 -0
- package/lib/cjs/timezone-picker/generate-timezone-options.js.map +1 -0
- package/lib/cjs/timezone-picker/index.js +39 -0
- package/lib/cjs/timezone-picker/index.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/index.js +60 -0
- package/lib/cjs/timezone-picker/stories/index.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-both-mode.story.js +41 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-both-mode.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-default.story.js +35 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-default.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-disabled.story.js +30 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-disabled.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-dst-aware.story.js +54 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-dst-aware.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-fluid.story.js +30 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-fluid.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-in-dialog.story.js +47 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-in-dialog.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-sizes.story.js +32 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-sizes.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-utc-only.story.js +49 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker-utc-only.story.js.map +1 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker.stories.js +145 -0
- package/lib/cjs/timezone-picker/stories/timezone-picker.stories.js.map +1 -0
- package/lib/cjs/timezone-picker/timezone-picker-hooks.js +52 -0
- package/lib/cjs/timezone-picker/timezone-picker-hooks.js.map +1 -0
- package/lib/esm/date-picker/DatePicker.js +11 -3
- package/lib/esm/date-picker/DatePicker.js.map +1 -1
- package/lib/esm/date-picker/stories/date-picker-with-timezone-both-mode.story.js +53 -0
- package/lib/esm/date-picker/stories/date-picker-with-timezone-both-mode.story.js.map +1 -0
- package/lib/esm/date-picker/stories/date-picker-with-timezone.story.js +50 -0
- package/lib/esm/date-picker/stories/date-picker-with-timezone.story.js.map +1 -0
- package/lib/esm/date-picker/stories/date-picker.stories.js +27 -1
- package/lib/esm/date-picker/stories/date-picker.stories.js.map +1 -1
- package/lib/esm/date-picker/stories/index.js +6 -0
- package/lib/esm/date-picker/stories/index.js.map +1 -1
- package/lib/esm/dropzone/Dropzone.js +1 -1
- package/lib/esm/dropzone/Dropzone.js.map +1 -1
- package/lib/esm/index.js +1 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/timezone-picker/TimeZonePicker.js +323 -0
- package/lib/esm/timezone-picker/TimeZonePicker.js.map +1 -0
- package/lib/esm/timezone-picker/generate-timezone-options.js +247 -0
- package/lib/esm/timezone-picker/generate-timezone-options.js.map +1 -0
- package/lib/esm/timezone-picker/index.js +23 -0
- package/lib/esm/timezone-picker/index.js.map +1 -0
- package/lib/esm/timezone-picker/stories/index.js +46 -0
- package/lib/esm/timezone-picker/stories/index.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-both-mode.story.js +37 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-both-mode.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-default.story.js +33 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-default.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-disabled.story.js +28 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-disabled.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-dst-aware.story.js +52 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-dst-aware.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-fluid.story.js +28 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-fluid.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-in-dialog.story.js +45 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-in-dialog.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-sizes.story.js +30 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-sizes.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-utc-only.story.js +47 -0
- package/lib/esm/timezone-picker/stories/timezone-picker-utc-only.story.js.map +1 -0
- package/lib/esm/timezone-picker/stories/timezone-picker.stories.js +142 -0
- package/lib/esm/timezone-picker/stories/timezone-picker.stories.js.map +1 -0
- package/lib/esm/timezone-picker/timezone-picker-hooks.js +47 -0
- package/lib/esm/timezone-picker/timezone-picker-hooks.js.map +1 -0
- package/lib/types/date-picker/DatePicker.d.ts +4 -1
- package/lib/types/date-picker/DatePicker.d.ts.map +1 -1
- package/lib/types/date-picker/stories/date-picker-with-timezone-both-mode.story.d.ts +23 -0
- package/lib/types/date-picker/stories/date-picker-with-timezone-both-mode.story.d.ts.map +1 -0
- package/lib/types/date-picker/stories/date-picker-with-timezone.story.d.ts +24 -0
- package/lib/types/date-picker/stories/date-picker-with-timezone.story.d.ts.map +1 -0
- package/lib/types/date-picker/stories/date-picker.stories.d.ts +2 -0
- package/lib/types/date-picker/stories/date-picker.stories.d.ts.map +1 -1
- package/lib/types/date-picker/stories/index.d.ts +4 -0
- package/lib/types/date-picker/stories/index.d.ts.map +1 -1
- package/lib/types/dropzone/Dropzone.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/timezone-picker/TimeZonePicker.d.ts +68 -0
- package/lib/types/timezone-picker/TimeZonePicker.d.ts.map +1 -0
- package/lib/types/timezone-picker/generate-timezone-options.d.ts +61 -0
- package/lib/types/timezone-picker/generate-timezone-options.d.ts.map +1 -0
- package/lib/types/timezone-picker/index.d.ts +23 -0
- package/lib/types/timezone-picker/index.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/index.d.ts +37 -0
- package/lib/types/timezone-picker/stories/index.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-both-mode.story.d.ts +23 -0
- package/lib/types/timezone-picker/stories/timezone-picker-both-mode.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-default.story.d.ts +24 -0
- package/lib/types/timezone-picker/stories/timezone-picker-default.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-disabled.story.d.ts +24 -0
- package/lib/types/timezone-picker/stories/timezone-picker-disabled.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-dst-aware.story.d.ts +24 -0
- package/lib/types/timezone-picker/stories/timezone-picker-dst-aware.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-fluid.story.d.ts +24 -0
- package/lib/types/timezone-picker/stories/timezone-picker-fluid.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-in-dialog.story.d.ts +24 -0
- package/lib/types/timezone-picker/stories/timezone-picker-in-dialog.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-sizes.story.d.ts +24 -0
- package/lib/types/timezone-picker/stories/timezone-picker-sizes.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker-utc-only.story.d.ts +24 -0
- package/lib/types/timezone-picker/stories/timezone-picker-utc-only.story.d.ts.map +1 -0
- package/lib/types/timezone-picker/stories/timezone-picker.stories.d.ts +34 -0
- package/lib/types/timezone-picker/stories/timezone-picker.stories.d.ts.map +1 -0
- package/lib/types/timezone-picker/timezone-picker-hooks.d.ts +31 -0
- package/lib/types/timezone-picker/timezone-picker-hooks.d.ts.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* Copyright (c) "Neo4j"
|
|
16
|
+
* Neo4j Sweden AB [http://neo4j.com]
|
|
17
|
+
*
|
|
18
|
+
* This file is part of Neo4j.
|
|
19
|
+
*
|
|
20
|
+
* Neo4j is free software: you can redistribute it and/or modify
|
|
21
|
+
* it under the terms of the GNU General Public License as published by
|
|
22
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
23
|
+
* (at your option) any later version.
|
|
24
|
+
*
|
|
25
|
+
* This program is distributed in the hope that it will be useful,
|
|
26
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
27
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
28
|
+
* GNU General Public License for more details.
|
|
29
|
+
*
|
|
30
|
+
* You should have received a copy of the GNU General Public License
|
|
31
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
32
|
+
*/
|
|
33
|
+
import { autoUpdate, flip, FloatingFocusManager, FloatingPortal, offset, shift, useDismiss, useFloating, useInteractions, useMergeRefs, useTransitionStyles, } from '@floating-ui/react';
|
|
34
|
+
import { ChevronDownIcon } from '@heroicons/react/24/outline';
|
|
35
|
+
import { tokens } from '@neo4j-ndl/base';
|
|
36
|
+
import cn from 'classnames';
|
|
37
|
+
import { useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
38
|
+
import { ConditionalWrap } from '../conditional-wrap';
|
|
39
|
+
import { useIsInsideDialog } from '../dialog/dialog-context';
|
|
40
|
+
import { ExclamationCircleIconSolid } from '../icons';
|
|
41
|
+
import { useNeedleTheme } from '../theme';
|
|
42
|
+
import { Typography } from '../typography';
|
|
43
|
+
import { formatTimeZone, generateTimeZoneOptions, generateUTCTimeZoneOptions, isValidUTCFormat, parseCustomUTCOffset, } from './generate-timezone-options';
|
|
44
|
+
import { useKeyboardNavigation, useTimeZonePickerPopover, } from './timezone-picker-hooks';
|
|
45
|
+
export const TimeZonePicker = (_a) => {
|
|
46
|
+
var _b, _c;
|
|
47
|
+
var { isDisabled, isFluid, isReadOnly, isRequired, value, label, onChange, className, onError, style, size = 'medium', errorText, htmlAttributes, floatingStrategy, isPortaled: isPortaledProp, referenceDate, mode = 'city', ref } = _a, restProps = __rest(_a, ["isDisabled", "isFluid", "isReadOnly", "isRequired", "value", "label", "onChange", "className", "onError", "style", "size", "errorText", "htmlAttributes", "floatingStrategy", "isPortaled", "referenceDate", "mode", "ref"]);
|
|
48
|
+
const [inputValue, setInputValue] = useState(value
|
|
49
|
+
? formatTimeZone(value, referenceDate)
|
|
50
|
+
: formatTimeZone(new Date().toISOString().split('T')[1].split('.')[0], referenceDate));
|
|
51
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
52
|
+
const listRef = useRef(null);
|
|
53
|
+
const inputRef = useRef(null);
|
|
54
|
+
useImperativeHandle(ref, () => inputRef.current, []);
|
|
55
|
+
const [error, setError] = useState(undefined);
|
|
56
|
+
const timezoneOptions = useMemo(() => {
|
|
57
|
+
if (mode === 'utc') {
|
|
58
|
+
return generateUTCTimeZoneOptions(referenceDate);
|
|
59
|
+
}
|
|
60
|
+
else if (mode === 'city') {
|
|
61
|
+
return generateTimeZoneOptions(referenceDate);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// 'both' mode - combine both lists, excluding UTC from city list to avoid duplication
|
|
65
|
+
return [
|
|
66
|
+
...generateUTCTimeZoneOptions(referenceDate),
|
|
67
|
+
...generateTimeZoneOptions(referenceDate, true), // excludeUTC = true
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
}, [referenceDate]);
|
|
71
|
+
const { themeClassName } = useNeedleTheme();
|
|
72
|
+
const { isPopoverOpen, setIsPopoverOpen } = useTimeZonePickerPopover(false);
|
|
73
|
+
// Filter options based on search query
|
|
74
|
+
const filteredOptions = useMemo(() => {
|
|
75
|
+
if (!searchQuery)
|
|
76
|
+
return timezoneOptions;
|
|
77
|
+
const query = searchQuery.toLowerCase();
|
|
78
|
+
return timezoneOptions.filter((option) => option.label.toLowerCase().includes(query) ||
|
|
79
|
+
option.value.toLowerCase().includes(query));
|
|
80
|
+
}, [timezoneOptions, searchQuery]);
|
|
81
|
+
// Split options into UTC and city-based sections for 'both' mode
|
|
82
|
+
const { utcOptions, cityOptions } = useMemo(() => {
|
|
83
|
+
if (mode !== 'both') {
|
|
84
|
+
return { utcOptions: [], cityOptions: filteredOptions };
|
|
85
|
+
}
|
|
86
|
+
const utc = [];
|
|
87
|
+
const city = [];
|
|
88
|
+
filteredOptions.forEach((option) => {
|
|
89
|
+
// UTC options are those that start with "UTC" in their value or label
|
|
90
|
+
if (option.value.toUpperCase().startsWith('UTC') ||
|
|
91
|
+
option.label.toUpperCase().startsWith('UTC')) {
|
|
92
|
+
utc.push(option);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
city.push(option);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return { cityOptions: city, utcOptions: utc };
|
|
99
|
+
}, [filteredOptions, mode]);
|
|
100
|
+
const { focusedIndex, setFocusedIndex, handleArrowNavigation } = useKeyboardNavigation(filteredOptions.length);
|
|
101
|
+
const classes = {
|
|
102
|
+
'ndl-small': size === 'small',
|
|
103
|
+
'ndl-medium': size === 'medium',
|
|
104
|
+
'ndl-large': size === 'large',
|
|
105
|
+
'ndl-error': error !== undefined,
|
|
106
|
+
'ndl-fluid': isFluid,
|
|
107
|
+
'ndl-disabled': isDisabled,
|
|
108
|
+
'ndl-read-only': isReadOnly,
|
|
109
|
+
};
|
|
110
|
+
const inputWidth = isFluid === true ? '100%' : '16rem';
|
|
111
|
+
const errorToShow = errorText !== undefined && errorText !== '' ? errorText : error;
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (error !== undefined) {
|
|
114
|
+
onError === null || onError === void 0 ? void 0 : onError(error, inputValue);
|
|
115
|
+
}
|
|
116
|
+
}, [error, inputValue, onError]);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
setInputValue(value ? formatTimeZone(value, referenceDate) : '');
|
|
119
|
+
}, [value, referenceDate]);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (isPopoverOpen) {
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
var _a, _b;
|
|
124
|
+
(_b = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.children[focusedIndex]) === null || _b === void 0 ? void 0 : _b.scrollIntoView({
|
|
125
|
+
block: 'center',
|
|
126
|
+
inline: 'nearest',
|
|
127
|
+
});
|
|
128
|
+
}, 0);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
var _a, _b;
|
|
133
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
|
134
|
+
const selectedIndex = (_b = filteredOptions.findIndex((opt) => opt.value === value)) !== null && _b !== void 0 ? _b : 0;
|
|
135
|
+
setFocusedIndex(selectedIndex >= 0 ? selectedIndex : 0);
|
|
136
|
+
setSearchQuery('');
|
|
137
|
+
}, 0);
|
|
138
|
+
}
|
|
139
|
+
}, [focusedIndex, isPopoverOpen, value, setFocusedIndex, filteredOptions]);
|
|
140
|
+
const handleOnClick = (timezone) => {
|
|
141
|
+
if (onChange) {
|
|
142
|
+
onChange(timezone);
|
|
143
|
+
}
|
|
144
|
+
setError(undefined);
|
|
145
|
+
setInputValue(formatTimeZone(timezone, referenceDate));
|
|
146
|
+
setIsPopoverOpen(false);
|
|
147
|
+
setSearchQuery('');
|
|
148
|
+
};
|
|
149
|
+
const handleInputChange = (e) => {
|
|
150
|
+
const query = e.target.value;
|
|
151
|
+
setSearchQuery(query);
|
|
152
|
+
setInputValue(query);
|
|
153
|
+
// In UTC mode (utc or both), validate custom UTC input
|
|
154
|
+
if ((mode === 'utc' || mode === 'both') &&
|
|
155
|
+
query.trim().toUpperCase().startsWith('UTC')) {
|
|
156
|
+
if (isValidUTCFormat(query.trim())) {
|
|
157
|
+
setError(undefined);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
setError('Invalid UTC format. Use UTC+H:MM or UTC-H:MM');
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// Try to find a matching timezone
|
|
165
|
+
const matchingOption = timezoneOptions.find((opt) => opt.label.toLowerCase().includes(query.toLowerCase()) ||
|
|
166
|
+
opt.value.toLowerCase().includes(query.toLowerCase()));
|
|
167
|
+
if (matchingOption) {
|
|
168
|
+
setError(undefined);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const handleKeyDown = (e) => {
|
|
172
|
+
if (isReadOnly === true || isDisabled === true) {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (e.key === 'Escape') {
|
|
177
|
+
setIsPopoverOpen(false);
|
|
178
|
+
setSearchQuery('');
|
|
179
|
+
setInputValue(value ? formatTimeZone(value, referenceDate) : '');
|
|
180
|
+
}
|
|
181
|
+
setIsPopoverOpen(true);
|
|
182
|
+
if (e.key === 'Enter') {
|
|
183
|
+
// In UTC mode (utc or both), allow custom UTC input
|
|
184
|
+
if ((mode === 'utc' || mode === 'both') &&
|
|
185
|
+
inputValue.trim().toUpperCase().startsWith('UTC')) {
|
|
186
|
+
const parsedOffset = parseCustomUTCOffset(inputValue.trim());
|
|
187
|
+
if (parsedOffset) {
|
|
188
|
+
handleOnClick(parsedOffset);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (filteredOptions.length > 0) {
|
|
193
|
+
const selectedOption = filteredOptions[focusedIndex];
|
|
194
|
+
if (selectedOption) {
|
|
195
|
+
handleOnClick(selectedOption.value);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
handleArrowNavigation(e.key);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
/** Custom floating ui solution */
|
|
205
|
+
const isInsideDialog = useIsInsideDialog();
|
|
206
|
+
const isPortaled = isPortaledProp !== null && isPortaledProp !== void 0 ? isPortaledProp : !isInsideDialog;
|
|
207
|
+
const containerRef = useRef(null);
|
|
208
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
209
|
+
elements: {
|
|
210
|
+
reference: containerRef.current,
|
|
211
|
+
},
|
|
212
|
+
middleware: [
|
|
213
|
+
offset(10),
|
|
214
|
+
flip({
|
|
215
|
+
fallbackPlacements: ['top'],
|
|
216
|
+
fallbackStrategy: 'bestFit',
|
|
217
|
+
}),
|
|
218
|
+
shift({ padding: 5 }),
|
|
219
|
+
],
|
|
220
|
+
onOpenChange: (open) => {
|
|
221
|
+
setIsPopoverOpen(open);
|
|
222
|
+
},
|
|
223
|
+
open: isDisabled !== true && isReadOnly !== true && isPopoverOpen,
|
|
224
|
+
placement: 'bottom',
|
|
225
|
+
strategy: floatingStrategy !== null && floatingStrategy !== void 0 ? floatingStrategy : (isInsideDialog ? 'fixed' : 'absolute'),
|
|
226
|
+
whileElementsMounted: autoUpdate,
|
|
227
|
+
});
|
|
228
|
+
const dismiss = useDismiss(context);
|
|
229
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
|
|
230
|
+
const { styles: transitionStyles } = useTransitionStyles(context, {
|
|
231
|
+
duration: (_b = Number.parseInt(tokens.motion.duration.quick)) !== null && _b !== void 0 ? _b : 0,
|
|
232
|
+
});
|
|
233
|
+
const mergedRef = useMergeRefs([refs.setReference, containerRef]);
|
|
234
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", Object.assign({ className: cn('ndl-timezone-picker', className, Object.assign({}, classes)), ref: mergedRef }, getReferenceProps(), { style: {
|
|
235
|
+
width: isFluid === true ? '100%' : 'fit-content',
|
|
236
|
+
}, children: [_jsxs("label", { className: "ndl-timezone-picker-label", children: [label !== undefined && (_jsx(Typography, { variant: size === 'large' ? 'body-large' : 'body-medium', children: label })), _jsxs("div", { className: "ndl-timezone-picker-input-wrapper", style: Object.assign({ width: inputWidth }, style), children: [_jsx("input", Object.assign({ "aria-label": label, className: "ndl-timezone-picker-input", type: "text", ref: inputRef, value: inputValue, disabled: isDisabled, readOnly: isReadOnly, required: isRequired, onChange: handleInputChange, onKeyDown: handleKeyDown, onClick: (e) => {
|
|
237
|
+
if (isReadOnly === true || isDisabled === true)
|
|
238
|
+
return;
|
|
239
|
+
setIsPopoverOpen(true);
|
|
240
|
+
// Select all text on click for easy searching
|
|
241
|
+
e.currentTarget.select();
|
|
242
|
+
}, onFocus: (e) => {
|
|
243
|
+
if (isReadOnly === true || isDisabled === true)
|
|
244
|
+
return;
|
|
245
|
+
// Select all text on focus for easy searching
|
|
246
|
+
e.currentTarget.select();
|
|
247
|
+
}, onBlur: (e) => {
|
|
248
|
+
var _a;
|
|
249
|
+
if (isReadOnly === true || isDisabled === true)
|
|
250
|
+
return;
|
|
251
|
+
if (((_a = e.relatedTarget) === null || _a === void 0 ? void 0 : _a.classList.contains('ndl-timezone-picker-popover-item')) === true) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// In UTC mode (utc or both), try to parse custom input on blur
|
|
255
|
+
if ((mode === 'utc' || mode === 'both') &&
|
|
256
|
+
inputValue.trim().toUpperCase().startsWith('UTC')) {
|
|
257
|
+
const parsedOffset = parseCustomUTCOffset(inputValue.trim());
|
|
258
|
+
if (parsedOffset) {
|
|
259
|
+
handleOnClick(parsedOffset);
|
|
260
|
+
setError(undefined);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Reset to the current value if no valid selection was made
|
|
265
|
+
if (value) {
|
|
266
|
+
setInputValue(formatTimeZone(value, referenceDate));
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
setInputValue('');
|
|
270
|
+
}
|
|
271
|
+
setSearchQuery('');
|
|
272
|
+
setError(undefined);
|
|
273
|
+
}, placeholder: mode === 'city'
|
|
274
|
+
? 'Select timezone'
|
|
275
|
+
: mode === 'utc'
|
|
276
|
+
? 'Select or type UTC offset (e.g., UTC+5:30)'
|
|
277
|
+
: 'Select timezone or type UTC offset' }, htmlAttributes, restProps)), _jsx(ChevronDownIcon, { className: "ndl-icon-svg ndl-timezone-picker-icon" })] })] }), errorToShow !== undefined && (_jsxs("div", { className: "ndl-timezone-picker-error-wrapper", children: [_jsx(ExclamationCircleIconSolid, { className: "ndl-timezone-picker-error-icon" }), _jsx(Typography, { variant: size === 'large' ? 'body-medium' : 'body-small', className: "ndl-timezone-picker-error-text", children: errorToShow })] }))] })), context.open && (_jsx(ConditionalWrap, { shouldWrap: isPortaled, wrap: (wrapChildren) => (_jsx(FloatingPortal, { children: wrapChildren })), children: _jsx(FloatingFocusManager, { context: context, modal: false, initialFocus: -1, children: _jsx("div", Object.assign({ ref: refs.setFloating, className: cn(themeClassName, 'ndl-timezone-picker-popover', Object.assign({}, classes)), style: Object.assign(Object.assign(Object.assign({}, transitionStyles), floatingStyles), { width: isFluid === true
|
|
278
|
+
? (_c = containerRef.current) === null || _c === void 0 ? void 0 : _c.clientWidth
|
|
279
|
+
: undefined }) }, getFloatingProps(), { children: _jsx("ul", { ref: listRef, tabIndex: -1, children: mode === 'both' ? (_jsxs(_Fragment, { children: [utcOptions.length > 0 && (_jsxs(_Fragment, { children: [_jsx("li", { className: "ndl-timezone-picker-section-header", children: "UTC Offsets" }), utcOptions.map((option, i) => (_jsx("li", { role: "option", "aria-selected": value === option.value, value: option.value, onClick: (e) => {
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
e.preventDefault();
|
|
282
|
+
handleOnClick(option.value);
|
|
283
|
+
setIsPopoverOpen(false);
|
|
284
|
+
}, onMouseDown: (e) => {
|
|
285
|
+
e.stopPropagation();
|
|
286
|
+
e.preventDefault();
|
|
287
|
+
}, tabIndex: -1, className: cn(focusedIndex === i ? 'focused' : '', 'ndl-timezone-picker-popover-item'), onKeyDown: (e) => {
|
|
288
|
+
if (e.key === 'Enter') {
|
|
289
|
+
e.stopPropagation();
|
|
290
|
+
handleOnClick(option.value);
|
|
291
|
+
}
|
|
292
|
+
}, children: option.label }, option.value)))] })), cityOptions.length > 0 && (_jsxs(_Fragment, { children: [_jsx("li", { className: "ndl-timezone-picker-section-header", children: "City-Based Timezones" }), cityOptions.map((option, i) => {
|
|
293
|
+
const adjustedIndex = i + utcOptions.length;
|
|
294
|
+
return (_jsx("li", { role: "option", "aria-selected": value === option.value, value: option.value, onClick: (e) => {
|
|
295
|
+
e.stopPropagation();
|
|
296
|
+
e.preventDefault();
|
|
297
|
+
handleOnClick(option.value);
|
|
298
|
+
setIsPopoverOpen(false);
|
|
299
|
+
}, onMouseDown: (e) => {
|
|
300
|
+
e.stopPropagation();
|
|
301
|
+
e.preventDefault();
|
|
302
|
+
}, tabIndex: -1, className: cn(focusedIndex === adjustedIndex ? 'focused' : '', 'ndl-timezone-picker-popover-item'), onKeyDown: (e) => {
|
|
303
|
+
if (e.key === 'Enter') {
|
|
304
|
+
e.stopPropagation();
|
|
305
|
+
handleOnClick(option.value);
|
|
306
|
+
}
|
|
307
|
+
}, children: option.label }, option.value));
|
|
308
|
+
})] })), utcOptions.length === 0 && cityOptions.length === 0 && (_jsx("li", { className: "ndl-timezone-picker-popover-item ndl-timezone-picker-no-results", children: "No timezones found" }))] })) : (_jsx(_Fragment, { children: filteredOptions.length > 0 ? (filteredOptions.map((option, i) => (_jsx("li", { role: "option", "aria-selected": value === option.value, value: option.value, onClick: (e) => {
|
|
309
|
+
e.stopPropagation();
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
handleOnClick(option.value);
|
|
312
|
+
setIsPopoverOpen(false);
|
|
313
|
+
}, onMouseDown: (e) => {
|
|
314
|
+
e.stopPropagation();
|
|
315
|
+
e.preventDefault();
|
|
316
|
+
}, tabIndex: -1, className: cn(focusedIndex === i ? 'focused' : '', 'ndl-timezone-picker-popover-item'), onKeyDown: (e) => {
|
|
317
|
+
if (e.key === 'Enter') {
|
|
318
|
+
e.stopPropagation();
|
|
319
|
+
handleOnClick(option.value);
|
|
320
|
+
}
|
|
321
|
+
}, children: option.label }, option.value)))) : (_jsx("li", { className: "ndl-timezone-picker-popover-item ndl-timezone-picker-no-results", children: "No timezones found" })) })) }) })) }) }))] }));
|
|
322
|
+
};
|
|
323
|
+
//# sourceMappingURL=TimeZonePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimeZonePicker.js","sourceRoot":"","sources":["../../../src/timezone-picker/TimeZonePicker.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n *\n * Copyright (c) \"Neo4j\"\n * Neo4j Sweden AB [http://neo4j.com]\n *\n * This file is part of Neo4j.\n *\n * Neo4j is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n */\nimport {\n autoUpdate,\n flip,\n FloatingFocusManager,\n FloatingPortal,\n offset,\n shift,\n useDismiss,\n useFloating,\n useInteractions,\n useMergeRefs,\n useTransitionStyles,\n} from '@floating-ui/react';\nimport { ChevronDownIcon } from '@heroicons/react/24/outline';\nimport { tokens } from '@neo4j-ndl/base';\nimport cn from 'classnames';\nimport type React from 'react';\nimport {\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { type CommonProps } from '../_common/types';\nimport { ConditionalWrap } from '../conditional-wrap';\nimport { useIsInsideDialog } from '../dialog/dialog-context';\nimport { ExclamationCircleIconSolid } from '../icons';\nimport { useNeedleTheme } from '../theme';\nimport { Typography } from '../typography';\nimport {\n formatTimeZone,\n generateTimeZoneOptions,\n generateUTCTimeZoneOptions,\n isValidUTCFormat,\n parseCustomUTCOffset,\n} from './generate-timezone-options';\nimport {\n useKeyboardNavigation,\n useTimeZonePickerPopover,\n} from './timezone-picker-hooks';\n\nexport type TimeZonePickerProps = {\n /** The value of the timezone picker (IANA timezone identifier) */\n value?: string;\n /** Whether the timezone picker is fluid */\n isFluid?: boolean;\n /** Whether the timezone picker is read only */\n isReadOnly?: boolean;\n /** Whether the timezone picker is required */\n isRequired?: boolean;\n /** Whether the timezone picker is disabled */\n isDisabled?: boolean;\n /** The label of the timezone picker */\n label?: string;\n /** The size of the timezone picker */\n size?: 'small' | 'medium' | 'large';\n /** Manually set the error text */\n errorText?: string;\n /** Callback function triggered when the timezone picker value changes */\n onChange?: (timezone: string) => void;\n /** Callback function triggered when the timezone picker encounters an error */\n onError?: (error: string, timezone: string) => void;\n /**\n * Strategy for the dropdown floating element.\n * By default it is absolute, but if the component is inside of a needle Dialog, it is set to fixed.\n * If this prop is set, no auto-detection of Dialog is done.\n */\n floatingStrategy?: 'absolute' | 'fixed';\n /** Whether the dropdown element is rendered in a portal. */\n isPortaled?: boolean;\n /** Reference date for calculating timezone offsets (useful for DST-aware offset display) */\n referenceDate?: Date;\n /**\n * Display mode: 'city', 'utc', or 'both'\n * 'city' - shows city-based timezones, This should be used when the user needs to select a timezone for themselves or if they are selecting a timezone that's related to a date.\n * 'utc' - shows UTC timezones, This should be used when the user needs to select a timezone for a database entry or a technical application.\n * 'both' - shows both city-based and UTC timezones in separate sections, This should be used when the user needs to select a timezone for a database entry or a technical application, but also wants to see the city-based timezones for context for example a zoned date time.\n * @default 'city'\n * @example\n * <TimeZonePicker mode=\"city\" />\n * <TimeZonePicker mode=\"utc\" />\n * <TimeZonePicker mode=\"both\" />\n */\n mode?: 'city' | 'utc' | 'both';\n};\n\nexport const TimeZonePicker = ({\n isDisabled,\n isFluid,\n isReadOnly,\n isRequired,\n value,\n label,\n onChange,\n className,\n onError,\n style,\n size = 'medium',\n errorText,\n htmlAttributes,\n floatingStrategy,\n isPortaled: isPortaledProp,\n referenceDate,\n mode = 'city',\n ref,\n ...restProps\n}: CommonProps<'input', TimeZonePickerProps>) => {\n const [inputValue, setInputValue] = useState<string>(\n value\n ? formatTimeZone(value, referenceDate)\n : formatTimeZone(\n new Date().toISOString().split('T')[1].split('.')[0],\n referenceDate,\n ),\n );\n const [searchQuery, setSearchQuery] = useState<string>('');\n const listRef = useRef<HTMLUListElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n useImperativeHandle(ref, () => inputRef.current as HTMLInputElement, []);\n const [error, setError] = useState<string | undefined>(undefined);\n\n const timezoneOptions = useMemo(() => {\n if (mode === 'utc') {\n return generateUTCTimeZoneOptions(referenceDate);\n } else if (mode === 'city') {\n return generateTimeZoneOptions(referenceDate);\n } else {\n // 'both' mode - combine both lists, excluding UTC from city list to avoid duplication\n return [\n ...generateUTCTimeZoneOptions(referenceDate),\n ...generateTimeZoneOptions(referenceDate, true), // excludeUTC = true\n ];\n }\n }, [referenceDate]);\n\n const { themeClassName } = useNeedleTheme();\n const { isPopoverOpen, setIsPopoverOpen } = useTimeZonePickerPopover(false);\n\n // Filter options based on search query\n const filteredOptions = useMemo(() => {\n if (!searchQuery) return timezoneOptions;\n\n const query = searchQuery.toLowerCase();\n return timezoneOptions.filter(\n (option) =>\n option.label.toLowerCase().includes(query) ||\n option.value.toLowerCase().includes(query),\n );\n }, [timezoneOptions, searchQuery]);\n\n // Split options into UTC and city-based sections for 'both' mode\n const { utcOptions, cityOptions } = useMemo(() => {\n if (mode !== 'both') {\n return { utcOptions: [], cityOptions: filteredOptions };\n }\n\n const utc: typeof filteredOptions = [];\n const city: typeof filteredOptions = [];\n\n filteredOptions.forEach((option) => {\n // UTC options are those that start with \"UTC\" in their value or label\n if (\n option.value.toUpperCase().startsWith('UTC') ||\n option.label.toUpperCase().startsWith('UTC')\n ) {\n utc.push(option);\n } else {\n city.push(option);\n }\n });\n\n return { cityOptions: city, utcOptions: utc };\n }, [filteredOptions, mode]);\n\n const { focusedIndex, setFocusedIndex, handleArrowNavigation } =\n useKeyboardNavigation(filteredOptions.length);\n\n const classes = {\n 'ndl-small': size === 'small',\n 'ndl-medium': size === 'medium',\n 'ndl-large': size === 'large',\n 'ndl-error': error !== undefined,\n 'ndl-fluid': isFluid,\n 'ndl-disabled': isDisabled,\n 'ndl-read-only': isReadOnly,\n };\n\n const inputWidth = isFluid === true ? '100%' : '16rem';\n\n const errorToShow =\n errorText !== undefined && errorText !== '' ? errorText : error;\n\n useEffect(() => {\n if (error !== undefined) {\n onError?.(error, inputValue);\n }\n }, [error, inputValue, onError]);\n\n useEffect(() => {\n setInputValue(value ? formatTimeZone(value, referenceDate) : '');\n }, [value, referenceDate]);\n\n useEffect(() => {\n if (isPopoverOpen) {\n setTimeout(() => {\n listRef.current?.children[focusedIndex]?.scrollIntoView({\n block: 'center',\n inline: 'nearest',\n });\n }, 0);\n } else {\n setTimeout(() => {\n inputRef.current?.blur();\n const selectedIndex =\n filteredOptions.findIndex((opt) => opt.value === value) ?? 0;\n setFocusedIndex(selectedIndex >= 0 ? selectedIndex : 0);\n setSearchQuery('');\n }, 0);\n }\n }, [focusedIndex, isPopoverOpen, value, setFocusedIndex, filteredOptions]);\n\n const handleOnClick = (timezone: string) => {\n if (onChange) {\n onChange(timezone);\n }\n setError(undefined);\n setInputValue(formatTimeZone(timezone, referenceDate));\n setIsPopoverOpen(false);\n setSearchQuery('');\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const query = e.target.value;\n setSearchQuery(query);\n setInputValue(query);\n\n // In UTC mode (utc or both), validate custom UTC input\n if (\n (mode === 'utc' || mode === 'both') &&\n query.trim().toUpperCase().startsWith('UTC')\n ) {\n if (isValidUTCFormat(query.trim())) {\n setError(undefined);\n } else {\n setError('Invalid UTC format. Use UTC+H:MM or UTC-H:MM');\n }\n return;\n }\n\n // Try to find a matching timezone\n const matchingOption = timezoneOptions.find(\n (opt) =>\n opt.label.toLowerCase().includes(query.toLowerCase()) ||\n opt.value.toLowerCase().includes(query.toLowerCase()),\n );\n\n if (matchingOption) {\n setError(undefined);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (isReadOnly === true || isDisabled === true) {\n e.preventDefault();\n return;\n }\n\n if (e.key === 'Escape') {\n setIsPopoverOpen(false);\n setSearchQuery('');\n setInputValue(value ? formatTimeZone(value, referenceDate) : '');\n }\n\n setIsPopoverOpen(true);\n\n if (e.key === 'Enter') {\n // In UTC mode (utc or both), allow custom UTC input\n if (\n (mode === 'utc' || mode === 'both') &&\n inputValue.trim().toUpperCase().startsWith('UTC')\n ) {\n const parsedOffset = parseCustomUTCOffset(inputValue.trim());\n if (parsedOffset) {\n handleOnClick(parsedOffset);\n return;\n }\n }\n\n if (filteredOptions.length > 0) {\n const selectedOption = filteredOptions[focusedIndex];\n if (selectedOption) {\n handleOnClick(selectedOption.value);\n }\n }\n } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n handleArrowNavigation(e.key);\n }\n };\n\n /** Custom floating ui solution */\n const isInsideDialog = useIsInsideDialog();\n const isPortaled = isPortaledProp ?? !isInsideDialog;\n const containerRef = useRef<HTMLDivElement>(null);\n const { refs, floatingStyles, context } = useFloating({\n elements: {\n reference: containerRef.current,\n },\n middleware: [\n offset(10),\n flip({\n fallbackPlacements: ['top'],\n fallbackStrategy: 'bestFit',\n }),\n shift({ padding: 5 }),\n ],\n onOpenChange: (open) => {\n setIsPopoverOpen(open);\n },\n open: isDisabled !== true && isReadOnly !== true && isPopoverOpen,\n placement: 'bottom',\n strategy: floatingStrategy ?? (isInsideDialog ? 'fixed' : 'absolute'),\n whileElementsMounted: autoUpdate,\n });\n const dismiss = useDismiss(context);\n const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);\n const { styles: transitionStyles } = useTransitionStyles(context, {\n duration: Number.parseInt(tokens.motion.duration.quick) ?? 0,\n });\n\n const mergedRef = useMergeRefs([refs.setReference, containerRef]);\n\n return (\n <>\n <div\n className={cn('ndl-timezone-picker', className, {\n ...classes,\n })}\n ref={mergedRef}\n {...getReferenceProps()}\n style={{\n width: isFluid === true ? '100%' : 'fit-content',\n }}\n >\n <label className=\"ndl-timezone-picker-label\">\n {label !== undefined && (\n <Typography\n variant={size === 'large' ? 'body-large' : 'body-medium'}\n >\n {label}\n </Typography>\n )}\n <div\n className=\"ndl-timezone-picker-input-wrapper\"\n style={{\n width: inputWidth,\n ...style,\n }}\n >\n <input\n aria-label={label}\n className=\"ndl-timezone-picker-input\"\n type=\"text\"\n ref={inputRef}\n value={inputValue}\n disabled={isDisabled}\n readOnly={isReadOnly}\n required={isRequired}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n onClick={(e) => {\n if (isReadOnly === true || isDisabled === true) return;\n setIsPopoverOpen(true);\n // Select all text on click for easy searching\n e.currentTarget.select();\n }}\n onFocus={(e) => {\n if (isReadOnly === true || isDisabled === true) return;\n // Select all text on focus for easy searching\n e.currentTarget.select();\n }}\n onBlur={(e) => {\n if (isReadOnly === true || isDisabled === true) return;\n\n if (\n e.relatedTarget?.classList.contains(\n 'ndl-timezone-picker-popover-item',\n ) === true\n ) {\n return;\n }\n\n // In UTC mode (utc or both), try to parse custom input on blur\n if (\n (mode === 'utc' || mode === 'both') &&\n inputValue.trim().toUpperCase().startsWith('UTC')\n ) {\n const parsedOffset = parseCustomUTCOffset(inputValue.trim());\n if (parsedOffset) {\n handleOnClick(parsedOffset);\n setError(undefined);\n return;\n }\n }\n\n // Reset to the current value if no valid selection was made\n if (value) {\n setInputValue(formatTimeZone(value, referenceDate));\n } else {\n setInputValue('');\n }\n setSearchQuery('');\n setError(undefined);\n }}\n placeholder={\n mode === 'city'\n ? 'Select timezone'\n : mode === 'utc'\n ? 'Select or type UTC offset (e.g., UTC+5:30)'\n : 'Select timezone or type UTC offset'\n }\n {...htmlAttributes}\n {...restProps}\n />\n <ChevronDownIcon className=\"ndl-icon-svg ndl-timezone-picker-icon\" />\n </div>\n </label>\n {errorToShow !== undefined && (\n <div className=\"ndl-timezone-picker-error-wrapper\">\n <ExclamationCircleIconSolid className=\"ndl-timezone-picker-error-icon\" />\n <Typography\n variant={size === 'large' ? 'body-medium' : 'body-small'}\n className=\"ndl-timezone-picker-error-text\"\n >\n {errorToShow}\n </Typography>\n </div>\n )}\n </div>\n\n {context.open && (\n <ConditionalWrap\n shouldWrap={isPortaled}\n wrap={(wrapChildren) => (\n <FloatingPortal>{wrapChildren}</FloatingPortal>\n )}\n >\n <FloatingFocusManager\n context={context}\n modal={false}\n initialFocus={-1}\n >\n <div\n ref={refs.setFloating}\n className={cn(themeClassName, 'ndl-timezone-picker-popover', {\n ...classes,\n })}\n style={{\n ...transitionStyles,\n ...floatingStyles,\n width:\n isFluid === true\n ? containerRef.current?.clientWidth\n : undefined,\n }}\n {...getFloatingProps()}\n >\n <ul ref={listRef} tabIndex={-1}>\n {mode === 'both' ? (\n <>\n {utcOptions.length > 0 && (\n <>\n <li className=\"ndl-timezone-picker-section-header\">\n UTC Offsets\n </li>\n {utcOptions.map((option, i) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={value === option.value}\n value={option.value}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n handleOnClick(option.value);\n setIsPopoverOpen(false);\n }}\n onMouseDown={(e) => {\n e.stopPropagation();\n e.preventDefault();\n }}\n tabIndex={-1}\n className={cn(\n focusedIndex === i ? 'focused' : '',\n 'ndl-timezone-picker-popover-item',\n )}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n handleOnClick(option.value);\n }\n }}\n >\n {option.label}\n </li>\n ))}\n </>\n )}\n {cityOptions.length > 0 && (\n <>\n <li className=\"ndl-timezone-picker-section-header\">\n City-Based Timezones\n </li>\n {cityOptions.map((option, i) => {\n const adjustedIndex = i + utcOptions.length;\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={value === option.value}\n value={option.value}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n handleOnClick(option.value);\n setIsPopoverOpen(false);\n }}\n onMouseDown={(e) => {\n e.stopPropagation();\n e.preventDefault();\n }}\n tabIndex={-1}\n className={cn(\n focusedIndex === adjustedIndex ? 'focused' : '',\n 'ndl-timezone-picker-popover-item',\n )}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n handleOnClick(option.value);\n }\n }}\n >\n {option.label}\n </li>\n );\n })}\n </>\n )}\n {utcOptions.length === 0 && cityOptions.length === 0 && (\n <li className=\"ndl-timezone-picker-popover-item ndl-timezone-picker-no-results\">\n No timezones found\n </li>\n )}\n </>\n ) : (\n <>\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option, i) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={value === option.value}\n value={option.value}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n handleOnClick(option.value);\n setIsPopoverOpen(false);\n }}\n onMouseDown={(e) => {\n e.stopPropagation();\n e.preventDefault();\n }}\n tabIndex={-1}\n className={cn(\n focusedIndex === i ? 'focused' : '',\n 'ndl-timezone-picker-popover-item',\n )}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n handleOnClick(option.value);\n }\n }}\n >\n {option.label}\n </li>\n ))\n ) : (\n <li className=\"ndl-timezone-picker-popover-item ndl-timezone-picker-no-results\">\n No timezones found\n </li>\n )}\n </>\n )}\n </ul>\n </div>\n </FloatingFocusManager>\n </ConditionalWrap>\n )}\n </>\n );\n};\n"]}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Copyright (c) "Neo4j"
|
|
4
|
+
* Neo4j Sweden AB [http://neo4j.com]
|
|
5
|
+
*
|
|
6
|
+
* This file is part of Neo4j.
|
|
7
|
+
*
|
|
8
|
+
* Neo4j is free software: you can redistribute it and/or modify
|
|
9
|
+
* it under the terms of the GNU General Public License as published by
|
|
10
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
* (at your option) any later version.
|
|
12
|
+
*
|
|
13
|
+
* This program is distributed in the hope that it will be useful,
|
|
14
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
* GNU General Public License for more details.
|
|
17
|
+
*
|
|
18
|
+
* You should have received a copy of the GNU General Public License
|
|
19
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Gets the offset in minutes for a timezone at a specific date
|
|
23
|
+
*/
|
|
24
|
+
const getOffsetMinutes = (timezone, date) => {
|
|
25
|
+
try {
|
|
26
|
+
// Create dates in UTC and in the target timezone
|
|
27
|
+
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
|
|
28
|
+
const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
|
|
29
|
+
// Calculate the difference in minutes
|
|
30
|
+
const diffMs = tzDate.getTime() - utcDate.getTime();
|
|
31
|
+
return Math.round(diffMs / (1000 * 60));
|
|
32
|
+
}
|
|
33
|
+
catch (_a) {
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Formats a UTC offset using the Intl API for consistent formatting
|
|
39
|
+
* @param offsetHours - The offset in hours (can be fractional)
|
|
40
|
+
* @returns Formatted UTC offset string (e.g., "UTC+5:30", "UTC-3")
|
|
41
|
+
*/
|
|
42
|
+
const formatUTCOffset = (offsetHours) => {
|
|
43
|
+
if (offsetHours === 0) {
|
|
44
|
+
return 'UTC';
|
|
45
|
+
}
|
|
46
|
+
const sign = offsetHours >= 0 ? '+' : '-';
|
|
47
|
+
const absHours = Math.abs(offsetHours);
|
|
48
|
+
const hours = Math.floor(absHours);
|
|
49
|
+
const minutes = Math.round((absHours - hours) * 60);
|
|
50
|
+
// Use Intl.NumberFormat for consistent number formatting
|
|
51
|
+
const numberFormatter = new Intl.NumberFormat('en-US', {
|
|
52
|
+
maximumFractionDigits: 0,
|
|
53
|
+
minimumIntegerDigits: 1,
|
|
54
|
+
});
|
|
55
|
+
const formattedHours = numberFormatter.format(hours);
|
|
56
|
+
const formattedMinutes = minutes > 0 ? `:${String(minutes).padStart(2, '0')}` : '';
|
|
57
|
+
return `UTC${sign}${formattedHours}${formattedMinutes}`;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Extracts unique UTC offsets from all supported timezones using the Intl API
|
|
61
|
+
* @param referenceDate - The date to use for calculating timezone offsets (defaults to current date)
|
|
62
|
+
* @returns Set of unique offset values in minutes
|
|
63
|
+
*/
|
|
64
|
+
const getUniqueOffsetsFromAPI = (referenceDate) => {
|
|
65
|
+
const uniqueOffsets = new Set();
|
|
66
|
+
const now = referenceDate || new Date();
|
|
67
|
+
try {
|
|
68
|
+
// Get all supported timezones from the Intl API
|
|
69
|
+
const timezones = Intl.supportedValuesOf('timeZone');
|
|
70
|
+
// Extract unique offsets from each timezone
|
|
71
|
+
for (const tz of timezones) {
|
|
72
|
+
try {
|
|
73
|
+
const offsetMinutes = getOffsetMinutes(tz, now);
|
|
74
|
+
uniqueOffsets.add(offsetMinutes);
|
|
75
|
+
}
|
|
76
|
+
catch (_a) {
|
|
77
|
+
// Skip timezones that can't be processed
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (_b) {
|
|
83
|
+
// Fallback to empty set if API is not available
|
|
84
|
+
}
|
|
85
|
+
// Always include UTC (offset 0)
|
|
86
|
+
uniqueOffsets.add(0);
|
|
87
|
+
return uniqueOffsets;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Generates UTC-only timezone options derived from the Intl API
|
|
91
|
+
* Uses the Intl API to discover which UTC offsets are actually used by supported timezones
|
|
92
|
+
* @param referenceDate - The date to use for calculating timezone offsets (defaults to current date)
|
|
93
|
+
*/
|
|
94
|
+
export const generateUTCTimeZoneOptions = (referenceDate) => {
|
|
95
|
+
// Get unique offsets from all supported timezones via the Intl API
|
|
96
|
+
const uniqueOffsets = getUniqueOffsetsFromAPI(referenceDate);
|
|
97
|
+
// Convert offset minutes to hours (can be fractional)
|
|
98
|
+
const offsetHours = Array.from(uniqueOffsets)
|
|
99
|
+
.map((minutes) => minutes / 60)
|
|
100
|
+
.sort((a, b) => a - b);
|
|
101
|
+
// Convert to TimeZoneOption format
|
|
102
|
+
const options = offsetHours.map((offsetHours) => {
|
|
103
|
+
const offsetStr = formatUTCOffset(offsetHours);
|
|
104
|
+
return {
|
|
105
|
+
label: offsetStr,
|
|
106
|
+
offset: offsetStr,
|
|
107
|
+
offsetMinutes: Math.round(offsetHours * 60),
|
|
108
|
+
value: offsetStr,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
return options;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Generates a list of common timezone options with their UTC offsets
|
|
115
|
+
* @param referenceDate - The date to use for calculating timezone offsets (defaults to current date)
|
|
116
|
+
* @param excludeUTC - Whether to exclude the UTC timezone from the list (useful when combining with UTC-only options)
|
|
117
|
+
*/
|
|
118
|
+
export const generateTimeZoneOptions = (referenceDate, excludeUTC = false) => {
|
|
119
|
+
// Common timezones with their IANA identifiers
|
|
120
|
+
const timezones = Intl.supportedValuesOf('timeZone');
|
|
121
|
+
const now = referenceDate || new Date();
|
|
122
|
+
// Filter out UTC if requested
|
|
123
|
+
const filteredTimezones = excludeUTC
|
|
124
|
+
? timezones.filter((tz) => tz !== 'UTC')
|
|
125
|
+
: timezones;
|
|
126
|
+
return filteredTimezones
|
|
127
|
+
.map((tz) => {
|
|
128
|
+
var _a, _b, _c;
|
|
129
|
+
try {
|
|
130
|
+
// Get the offset for this timezone
|
|
131
|
+
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
132
|
+
timeZone: tz,
|
|
133
|
+
timeZoneName: 'shortOffset',
|
|
134
|
+
});
|
|
135
|
+
const parts = formatter.formatToParts(now);
|
|
136
|
+
const offsetPart = parts.find((part) => part.type === 'timeZoneName');
|
|
137
|
+
const offset = (_a = offsetPart === null || offsetPart === void 0 ? void 0 : offsetPart.value) !== null && _a !== void 0 ? _a : '';
|
|
138
|
+
// Calculate offset in minutes for proper sorting
|
|
139
|
+
const offsetMinutes = getOffsetMinutes(tz, now);
|
|
140
|
+
// Format the label
|
|
141
|
+
const cityName = (_c = (_b = tz.split('/').pop()) === null || _b === void 0 ? void 0 : _b.replace(/_/g, ' ')) !== null && _c !== void 0 ? _c : tz;
|
|
142
|
+
const label = `${cityName} (${offset})`;
|
|
143
|
+
return {
|
|
144
|
+
label,
|
|
145
|
+
offset,
|
|
146
|
+
offsetMinutes,
|
|
147
|
+
value: tz,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (_d) {
|
|
151
|
+
// Fallback if timezone is not supported
|
|
152
|
+
return {
|
|
153
|
+
label: tz.replace(/_/g, ' '),
|
|
154
|
+
offset: '',
|
|
155
|
+
offsetMinutes: 0,
|
|
156
|
+
value: tz,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
.sort((a, b) => {
|
|
161
|
+
// Sort by offset (negative to positive), then by label
|
|
162
|
+
if (a.offsetMinutes !== b.offsetMinutes) {
|
|
163
|
+
return a.offsetMinutes - b.offsetMinutes;
|
|
164
|
+
}
|
|
165
|
+
return a.label.localeCompare(b.label);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Gets the user's current timezone
|
|
170
|
+
*/
|
|
171
|
+
export const getUserTimeZone = () => {
|
|
172
|
+
try {
|
|
173
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
174
|
+
}
|
|
175
|
+
catch (_a) {
|
|
176
|
+
return 'UTC';
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Parses a custom UTC offset string (e.g., "UTC+5:30", "UTC-3:45")
|
|
181
|
+
* @param input - The input string to parse
|
|
182
|
+
* @returns The parsed timezone value or null if invalid
|
|
183
|
+
*/
|
|
184
|
+
export const parseCustomUTCOffset = (input) => {
|
|
185
|
+
// Match patterns like: UTC+5:30, UTC-3:45, UTC+1, UTC-12, etc.
|
|
186
|
+
// Also accepts single digit minutes which will be padded
|
|
187
|
+
const utcPattern = /^UTC([+-])?(\d{1,2})(?::(\d{1,2}))?$/i;
|
|
188
|
+
const match = input.trim().match(utcPattern);
|
|
189
|
+
if (!match)
|
|
190
|
+
return null;
|
|
191
|
+
const sign = match[1] === '-' ? '-' : '+';
|
|
192
|
+
const hours = parseInt(match[2], 10);
|
|
193
|
+
const minutes = match[3] ? parseInt(match[3], 10) : 0;
|
|
194
|
+
// Validate ranges based on sign
|
|
195
|
+
if (sign === '+') {
|
|
196
|
+
if (hours < 0 || hours > 14)
|
|
197
|
+
return null;
|
|
198
|
+
if (hours === 14 && minutes > 0)
|
|
199
|
+
return null; // UTC+14 is max
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// sign === '-'
|
|
203
|
+
if (hours < 0 || hours > 12)
|
|
204
|
+
return null; // UTC-12 is min
|
|
205
|
+
if (hours === 12 && minutes > 0)
|
|
206
|
+
return null; // UTC-12 is min
|
|
207
|
+
}
|
|
208
|
+
if (minutes < 0 || minutes > 59)
|
|
209
|
+
return null;
|
|
210
|
+
return `UTC${sign}${hours}${minutes > 0 ? `:${String(minutes).padStart(2, '0')}` : ''}`;
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Validates if a string is a valid UTC timezone format
|
|
214
|
+
* @param input - The input string to validate
|
|
215
|
+
* @returns True if valid UTC timezone format
|
|
216
|
+
*/
|
|
217
|
+
export const isValidUTCFormat = (input) => {
|
|
218
|
+
return parseCustomUTCOffset(input) !== null || input.toUpperCase() === 'UTC';
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Formats a timezone for display
|
|
222
|
+
* @param timezone - The IANA timezone identifier or UTC offset
|
|
223
|
+
* @param referenceDate - The date to use for calculating the offset (defaults to current date)
|
|
224
|
+
*/
|
|
225
|
+
export const formatTimeZone = (timezone, referenceDate) => {
|
|
226
|
+
var _a, _b, _c;
|
|
227
|
+
// If it's already a UTC format, return as-is
|
|
228
|
+
if (timezone.toUpperCase().startsWith('UTC')) {
|
|
229
|
+
return timezone;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const now = referenceDate || new Date();
|
|
233
|
+
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
234
|
+
timeZone: timezone,
|
|
235
|
+
timeZoneName: 'shortOffset',
|
|
236
|
+
});
|
|
237
|
+
const parts = formatter.formatToParts(now);
|
|
238
|
+
const offsetPart = parts.find((part) => part.type === 'timeZoneName');
|
|
239
|
+
const offset = (_a = offsetPart === null || offsetPart === void 0 ? void 0 : offsetPart.value) !== null && _a !== void 0 ? _a : '';
|
|
240
|
+
const cityName = (_c = (_b = timezone.split('/').pop()) === null || _b === void 0 ? void 0 : _b.replace(/_/g, ' ')) !== null && _c !== void 0 ? _c : timezone;
|
|
241
|
+
return `${cityName} (${offset})`;
|
|
242
|
+
}
|
|
243
|
+
catch (_d) {
|
|
244
|
+
return timezone.replace(/_/g, ' ');
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=generate-timezone-options.js.map
|