@neo4j-ndl/react 4.0.19 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/lib/cjs/date-picker/DatePicker.js +11 -3
  2. package/lib/cjs/date-picker/DatePicker.js.map +1 -1
  3. package/lib/cjs/date-picker/stories/date-picker-with-timezone-both-mode.story.js +57 -0
  4. package/lib/cjs/date-picker/stories/date-picker-with-timezone-both-mode.story.js.map +1 -0
  5. package/lib/cjs/date-picker/stories/date-picker-with-timezone.story.js +52 -0
  6. package/lib/cjs/date-picker/stories/date-picker-with-timezone.story.js.map +1 -0
  7. package/lib/cjs/date-picker/stories/date-picker.stories.js +27 -1
  8. package/lib/cjs/date-picker/stories/date-picker.stories.js.map +1 -1
  9. package/lib/cjs/date-picker/stories/index.js +9 -1
  10. package/lib/cjs/date-picker/stories/index.js.map +1 -1
  11. package/lib/cjs/dropzone/Dropzone.js +1 -1
  12. package/lib/cjs/dropzone/Dropzone.js.map +1 -1
  13. package/lib/cjs/index.js +4 -2
  14. package/lib/cjs/index.js.map +1 -1
  15. package/lib/cjs/time-picker/TimePicker.js.map +1 -1
  16. package/lib/cjs/timezone-picker/TimeZonePicker.js +330 -0
  17. package/lib/cjs/timezone-picker/TimeZonePicker.js.map +1 -0
  18. package/lib/cjs/timezone-picker/generate-timezone-options.js +256 -0
  19. package/lib/cjs/timezone-picker/generate-timezone-options.js.map +1 -0
  20. package/lib/cjs/timezone-picker/index.js +39 -0
  21. package/lib/cjs/timezone-picker/index.js.map +1 -0
  22. package/lib/cjs/timezone-picker/stories/index.js +60 -0
  23. package/lib/cjs/timezone-picker/stories/index.js.map +1 -0
  24. package/lib/cjs/timezone-picker/stories/timezone-picker-both-mode.story.js +41 -0
  25. package/lib/cjs/timezone-picker/stories/timezone-picker-both-mode.story.js.map +1 -0
  26. package/lib/cjs/timezone-picker/stories/timezone-picker-default.story.js +35 -0
  27. package/lib/cjs/timezone-picker/stories/timezone-picker-default.story.js.map +1 -0
  28. package/lib/cjs/timezone-picker/stories/timezone-picker-disabled.story.js +30 -0
  29. package/lib/cjs/timezone-picker/stories/timezone-picker-disabled.story.js.map +1 -0
  30. package/lib/cjs/timezone-picker/stories/timezone-picker-dst-aware.story.js +54 -0
  31. package/lib/cjs/timezone-picker/stories/timezone-picker-dst-aware.story.js.map +1 -0
  32. package/lib/cjs/timezone-picker/stories/timezone-picker-fluid.story.js +30 -0
  33. package/lib/cjs/timezone-picker/stories/timezone-picker-fluid.story.js.map +1 -0
  34. package/lib/cjs/timezone-picker/stories/timezone-picker-in-dialog.story.js +47 -0
  35. package/lib/cjs/timezone-picker/stories/timezone-picker-in-dialog.story.js.map +1 -0
  36. package/lib/cjs/timezone-picker/stories/timezone-picker-sizes.story.js +32 -0
  37. package/lib/cjs/timezone-picker/stories/timezone-picker-sizes.story.js.map +1 -0
  38. package/lib/cjs/timezone-picker/stories/timezone-picker-utc-only.story.js +49 -0
  39. package/lib/cjs/timezone-picker/stories/timezone-picker-utc-only.story.js.map +1 -0
  40. package/lib/cjs/timezone-picker/stories/timezone-picker.stories.js +145 -0
  41. package/lib/cjs/timezone-picker/stories/timezone-picker.stories.js.map +1 -0
  42. package/lib/cjs/timezone-picker/timezone-picker-hooks.js +52 -0
  43. package/lib/cjs/timezone-picker/timezone-picker-hooks.js.map +1 -0
  44. package/lib/esm/date-picker/DatePicker.js +11 -3
  45. package/lib/esm/date-picker/DatePicker.js.map +1 -1
  46. package/lib/esm/date-picker/stories/date-picker-with-timezone-both-mode.story.js +53 -0
  47. package/lib/esm/date-picker/stories/date-picker-with-timezone-both-mode.story.js.map +1 -0
  48. package/lib/esm/date-picker/stories/date-picker-with-timezone.story.js +50 -0
  49. package/lib/esm/date-picker/stories/date-picker-with-timezone.story.js.map +1 -0
  50. package/lib/esm/date-picker/stories/date-picker.stories.js +27 -1
  51. package/lib/esm/date-picker/stories/date-picker.stories.js.map +1 -1
  52. package/lib/esm/date-picker/stories/index.js +6 -0
  53. package/lib/esm/date-picker/stories/index.js.map +1 -1
  54. package/lib/esm/dropzone/Dropzone.js +1 -1
  55. package/lib/esm/dropzone/Dropzone.js.map +1 -1
  56. package/lib/esm/index.js +1 -0
  57. package/lib/esm/index.js.map +1 -1
  58. package/lib/esm/time-picker/TimePicker.js +4 -4
  59. package/lib/esm/time-picker/TimePicker.js.map +1 -1
  60. package/lib/esm/timezone-picker/TimeZonePicker.js +323 -0
  61. package/lib/esm/timezone-picker/TimeZonePicker.js.map +1 -0
  62. package/lib/esm/timezone-picker/generate-timezone-options.js +247 -0
  63. package/lib/esm/timezone-picker/generate-timezone-options.js.map +1 -0
  64. package/lib/esm/timezone-picker/index.js +23 -0
  65. package/lib/esm/timezone-picker/index.js.map +1 -0
  66. package/lib/esm/timezone-picker/stories/index.js +46 -0
  67. package/lib/esm/timezone-picker/stories/index.js.map +1 -0
  68. package/lib/esm/timezone-picker/stories/timezone-picker-both-mode.story.js +37 -0
  69. package/lib/esm/timezone-picker/stories/timezone-picker-both-mode.story.js.map +1 -0
  70. package/lib/esm/timezone-picker/stories/timezone-picker-default.story.js +33 -0
  71. package/lib/esm/timezone-picker/stories/timezone-picker-default.story.js.map +1 -0
  72. package/lib/esm/timezone-picker/stories/timezone-picker-disabled.story.js +28 -0
  73. package/lib/esm/timezone-picker/stories/timezone-picker-disabled.story.js.map +1 -0
  74. package/lib/esm/timezone-picker/stories/timezone-picker-dst-aware.story.js +52 -0
  75. package/lib/esm/timezone-picker/stories/timezone-picker-dst-aware.story.js.map +1 -0
  76. package/lib/esm/timezone-picker/stories/timezone-picker-fluid.story.js +28 -0
  77. package/lib/esm/timezone-picker/stories/timezone-picker-fluid.story.js.map +1 -0
  78. package/lib/esm/timezone-picker/stories/timezone-picker-in-dialog.story.js +45 -0
  79. package/lib/esm/timezone-picker/stories/timezone-picker-in-dialog.story.js.map +1 -0
  80. package/lib/esm/timezone-picker/stories/timezone-picker-sizes.story.js +30 -0
  81. package/lib/esm/timezone-picker/stories/timezone-picker-sizes.story.js.map +1 -0
  82. package/lib/esm/timezone-picker/stories/timezone-picker-utc-only.story.js +47 -0
  83. package/lib/esm/timezone-picker/stories/timezone-picker-utc-only.story.js.map +1 -0
  84. package/lib/esm/timezone-picker/stories/timezone-picker.stories.js +142 -0
  85. package/lib/esm/timezone-picker/stories/timezone-picker.stories.js.map +1 -0
  86. package/lib/esm/timezone-picker/timezone-picker-hooks.js +47 -0
  87. package/lib/esm/timezone-picker/timezone-picker-hooks.js.map +1 -0
  88. package/lib/types/date-picker/DatePicker.d.ts +4 -1
  89. package/lib/types/date-picker/DatePicker.d.ts.map +1 -1
  90. package/lib/types/date-picker/stories/date-picker-with-timezone-both-mode.story.d.ts +23 -0
  91. package/lib/types/date-picker/stories/date-picker-with-timezone-both-mode.story.d.ts.map +1 -0
  92. package/lib/types/date-picker/stories/date-picker-with-timezone.story.d.ts +24 -0
  93. package/lib/types/date-picker/stories/date-picker-with-timezone.story.d.ts.map +1 -0
  94. package/lib/types/date-picker/stories/date-picker.stories.d.ts +2 -0
  95. package/lib/types/date-picker/stories/date-picker.stories.d.ts.map +1 -1
  96. package/lib/types/date-picker/stories/index.d.ts +4 -0
  97. package/lib/types/date-picker/stories/index.d.ts.map +1 -1
  98. package/lib/types/dropzone/Dropzone.d.ts.map +1 -1
  99. package/lib/types/index.d.ts +1 -0
  100. package/lib/types/index.d.ts.map +1 -1
  101. package/lib/types/timezone-picker/TimeZonePicker.d.ts +68 -0
  102. package/lib/types/timezone-picker/TimeZonePicker.d.ts.map +1 -0
  103. package/lib/types/timezone-picker/generate-timezone-options.d.ts +61 -0
  104. package/lib/types/timezone-picker/generate-timezone-options.d.ts.map +1 -0
  105. package/lib/types/timezone-picker/index.d.ts +23 -0
  106. package/lib/types/timezone-picker/index.d.ts.map +1 -0
  107. package/lib/types/timezone-picker/stories/index.d.ts +37 -0
  108. package/lib/types/timezone-picker/stories/index.d.ts.map +1 -0
  109. package/lib/types/timezone-picker/stories/timezone-picker-both-mode.story.d.ts +23 -0
  110. package/lib/types/timezone-picker/stories/timezone-picker-both-mode.story.d.ts.map +1 -0
  111. package/lib/types/timezone-picker/stories/timezone-picker-default.story.d.ts +24 -0
  112. package/lib/types/timezone-picker/stories/timezone-picker-default.story.d.ts.map +1 -0
  113. package/lib/types/timezone-picker/stories/timezone-picker-disabled.story.d.ts +24 -0
  114. package/lib/types/timezone-picker/stories/timezone-picker-disabled.story.d.ts.map +1 -0
  115. package/lib/types/timezone-picker/stories/timezone-picker-dst-aware.story.d.ts +24 -0
  116. package/lib/types/timezone-picker/stories/timezone-picker-dst-aware.story.d.ts.map +1 -0
  117. package/lib/types/timezone-picker/stories/timezone-picker-fluid.story.d.ts +24 -0
  118. package/lib/types/timezone-picker/stories/timezone-picker-fluid.story.d.ts.map +1 -0
  119. package/lib/types/timezone-picker/stories/timezone-picker-in-dialog.story.d.ts +24 -0
  120. package/lib/types/timezone-picker/stories/timezone-picker-in-dialog.story.d.ts.map +1 -0
  121. package/lib/types/timezone-picker/stories/timezone-picker-sizes.story.d.ts +24 -0
  122. package/lib/types/timezone-picker/stories/timezone-picker-sizes.story.d.ts.map +1 -0
  123. package/lib/types/timezone-picker/stories/timezone-picker-utc-only.story.d.ts +24 -0
  124. package/lib/types/timezone-picker/stories/timezone-picker-utc-only.story.d.ts.map +1 -0
  125. package/lib/types/timezone-picker/stories/timezone-picker.stories.d.ts +34 -0
  126. package/lib/types/timezone-picker/stories/timezone-picker.stories.d.ts.map +1 -0
  127. package/lib/types/timezone-picker/timezone-picker-hooks.d.ts +31 -0
  128. package/lib/types/timezone-picker/timezone-picker-hooks.d.ts.map +1 -0
  129. 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 classNames 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: classNames('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: classNames(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: classNames(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: classNames(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: classNames(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 classNames 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={classNames('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={classNames(\n themeClassName,\n 'ndl-timezone-picker-popover',\n {\n ...classes,\n },\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={classNames(\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={classNames(\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={classNames(\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