@gravity-ui/date-components 1.0.0 → 1.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.
Files changed (69) hide show
  1. package/dist/cjs/components/Calendar/Calendar.css +9 -1
  2. package/dist/cjs/components/Calendar/CalendarBase.js +9 -9
  3. package/dist/cjs/components/Calendar/hooks/types.d.ts +5 -2
  4. package/dist/cjs/components/Calendar/hooks/useCalendarCellProps.js +21 -16
  5. package/dist/cjs/components/Calendar/hooks/useCalendarGridProps.js +2 -7
  6. package/dist/cjs/components/Calendar/hooks/useCalendarProps.js +6 -3
  7. package/dist/cjs/components/Calendar/hooks/useCalendarState.js +69 -26
  8. package/dist/cjs/components/Calendar/hooks/useRangeCalendarState.js +16 -11
  9. package/dist/cjs/components/Calendar/utils.d.ts +3 -1
  10. package/dist/cjs/components/Calendar/utils.js +7 -1
  11. package/dist/cjs/components/DateField/hooks/useDateFieldProps.js +23 -16
  12. package/dist/cjs/components/DateField/hooks/useDateFieldState.d.ts +2 -0
  13. package/dist/cjs/components/DateField/hooks/useDateFieldState.js +49 -166
  14. package/dist/cjs/components/DateField/types.d.ts +1 -1
  15. package/dist/cjs/components/DateField/utils.d.ts +12 -9
  16. package/dist/cjs/components/DateField/utils.js +168 -25
  17. package/dist/cjs/components/DatePicker/DatePicker.css +6 -0
  18. package/dist/cjs/components/DatePicker/DatePicker.d.ts +4 -1
  19. package/dist/cjs/components/DatePicker/DatePicker.js +6 -33
  20. package/dist/cjs/components/DatePicker/MobileCalendar.js +4 -4
  21. package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.d.ts +20 -0
  22. package/dist/cjs/components/DatePicker/hooks/useDatePickerProps.js +134 -0
  23. package/dist/cjs/components/DatePicker/hooks/useDatePickerState.d.ts +4 -5
  24. package/dist/cjs/components/DatePicker/hooks/useDatePickerState.js +18 -8
  25. package/dist/cjs/components/DatePicker/index.d.ts +1 -0
  26. package/dist/cjs/components/DatePicker/index.js +1 -0
  27. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.css +1 -0
  28. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.d.ts +3 -0
  29. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.js +1 -1
  30. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +35 -27
  31. package/dist/cjs/components/utils/dates.d.ts +9 -0
  32. package/dist/cjs/components/utils/dates.js +28 -0
  33. package/dist/esm/components/Calendar/Calendar.css +9 -1
  34. package/dist/esm/components/Calendar/CalendarBase.js +10 -10
  35. package/dist/esm/components/Calendar/hooks/types.d.ts +5 -2
  36. package/dist/esm/components/Calendar/hooks/useCalendarCellProps.js +21 -16
  37. package/dist/esm/components/Calendar/hooks/useCalendarGridProps.js +2 -7
  38. package/dist/esm/components/Calendar/hooks/useCalendarProps.js +6 -3
  39. package/dist/esm/components/Calendar/hooks/useCalendarState.js +70 -27
  40. package/dist/esm/components/Calendar/hooks/useRangeCalendarState.js +16 -11
  41. package/dist/esm/components/Calendar/utils.d.ts +3 -1
  42. package/dist/esm/components/Calendar/utils.js +6 -0
  43. package/dist/esm/components/DateField/hooks/useDateFieldProps.js +23 -16
  44. package/dist/esm/components/DateField/hooks/useDateFieldState.d.ts +2 -0
  45. package/dist/esm/components/DateField/hooks/useDateFieldState.js +41 -158
  46. package/dist/esm/components/DateField/types.d.ts +1 -1
  47. package/dist/esm/components/DateField/utils.d.ts +12 -9
  48. package/dist/esm/components/DateField/utils.js +162 -21
  49. package/dist/esm/components/DatePicker/DatePicker.css +6 -0
  50. package/dist/esm/components/DatePicker/DatePicker.d.ts +4 -1
  51. package/dist/esm/components/DatePicker/DatePicker.js +8 -35
  52. package/dist/esm/components/DatePicker/MobileCalendar.js +1 -1
  53. package/dist/esm/components/DatePicker/hooks/useDatePickerProps.d.ts +20 -0
  54. package/dist/esm/components/DatePicker/hooks/useDatePickerProps.js +127 -0
  55. package/dist/esm/components/DatePicker/hooks/useDatePickerState.d.ts +4 -5
  56. package/dist/esm/components/DatePicker/hooks/useDatePickerState.js +17 -7
  57. package/dist/esm/components/DatePicker/index.d.ts +1 -0
  58. package/dist/esm/components/DatePicker/index.js +1 -0
  59. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.css +1 -0
  60. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.d.ts +3 -0
  61. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.js +1 -1
  62. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +35 -27
  63. package/dist/esm/components/utils/dates.d.ts +9 -0
  64. package/dist/esm/components/utils/dates.js +22 -0
  65. package/package.json +8 -7
  66. package/dist/cjs/components/DatePicker/DesktopCalendar.d.ts +0 -15
  67. package/dist/cjs/components/DatePicker/DesktopCalendar.js +0 -56
  68. package/dist/esm/components/DatePicker/DesktopCalendar.d.ts +0 -15
  69. package/dist/esm/components/DatePicker/DesktopCalendar.js +0 -48
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import { dateTime, isValid } from '@gravity-ui/date-utils';
3
3
  import { useControlledState } from '../../hooks/useControlledState';
4
- import { createPlaceholderValue, isInvalid, mergeDateTime, splitFormatIntoSections } from '../utils';
4
+ import { createPlaceholderValue, isInvalid, mergeDateTime } from '../../utils/dates';
5
+ import { addSegment, getDurationUnitFromSectionType, getSectionLimits, getSectionValue, setSegment, splitFormatIntoSections, } from '../utils';
5
6
  const EDITABLE_SEGMENTS = {
6
7
  year: true,
7
8
  month: true,
@@ -12,11 +13,6 @@ const EDITABLE_SEGMENTS = {
12
13
  dayPeriod: true,
13
14
  weekday: true,
14
15
  };
15
- const TYPE_MAPPING = {
16
- weekday: 'day',
17
- day: 'date',
18
- dayPeriod: 'hour',
19
- };
20
16
  const PAGE_STEP = {
21
17
  year: 5,
22
18
  month: 2,
@@ -62,10 +58,7 @@ export function useDateFieldState(props) {
62
58
  ? value
63
59
  : placeholderDate;
64
60
  const sectionsState = useSectionsState(sections, displayValue, validSegments);
65
- const [selectedSections, setSelectedSections] = React.useState(() => {
66
- var _a, _b;
67
- return (_b = (_a = sectionsState.editableSections[0]) === null || _a === void 0 ? void 0 : _a.previousEditableSection) !== null && _b !== void 0 ? _b : -1;
68
- });
61
+ const [selectedSections, setSelectedSections] = React.useState(-1);
69
62
  const selectedSectionIndexes = React.useMemo(() => {
70
63
  if (selectedSections === -1) {
71
64
  return null;
@@ -90,9 +83,14 @@ export function useDateFieldState(props) {
90
83
  return;
91
84
  }
92
85
  if (Object.keys(validSegments).length >= Object.keys(allSegments).length) {
93
- setDate(newValue);
86
+ if (!value || !newValue.isSame(value)) {
87
+ setDate(newValue);
88
+ }
94
89
  }
95
90
  else {
91
+ if (value) {
92
+ setDate(null);
93
+ }
96
94
  setPlaceholderDate(newValue);
97
95
  }
98
96
  }
@@ -219,6 +217,32 @@ export function useDateFieldState(props) {
219
217
  adjustSection(sectionIndex, -(PAGE_STEP[this.sections[sectionIndex].type] || 1));
220
218
  }
221
219
  },
220
+ incrementToMax() {
221
+ if (this.readOnly || this.disabled) {
222
+ return;
223
+ }
224
+ enteredKeys.current = '';
225
+ const sectionIndex = getCurrentEditableSectionIndex(this.sections, selectedSections);
226
+ if (sectionIndex !== -1) {
227
+ const section = this.sections[sectionIndex];
228
+ if (typeof section.maxValue === 'number') {
229
+ setSection(section, section.maxValue);
230
+ }
231
+ }
232
+ },
233
+ decrementToMin() {
234
+ if (this.readOnly || this.disabled) {
235
+ return;
236
+ }
237
+ enteredKeys.current = '';
238
+ const sectionIndex = getCurrentEditableSectionIndex(this.sections, selectedSections);
239
+ if (sectionIndex !== -1) {
240
+ const section = this.sections[sectionIndex];
241
+ if (typeof section.minValue === 'number') {
242
+ setSection(section, section.minValue);
243
+ }
244
+ }
245
+ },
222
246
  clearSection() {
223
247
  if (this.readOnly || this.disabled) {
224
248
  return;
@@ -255,9 +279,6 @@ export function useDateFieldState(props) {
255
279
  const type = getDurationUnitFromSectionType(section.type);
256
280
  currentValue = displayValue.set(type, placeholder[type]());
257
281
  }
258
- if (value) {
259
- setDate(null);
260
- }
261
282
  setValue(currentValue);
262
283
  },
263
284
  clearAll() {
@@ -384,6 +405,7 @@ export function useDateFieldState(props) {
384
405
  }
385
406
  },
386
407
  setValueFromString(str) {
408
+ enteredKeys.current = '';
387
409
  let date = parseDate({ input: str, format, timeZone: props.timeZone });
388
410
  if (isValid(date)) {
389
411
  if (props.timeZone && !isDateStringWithTimeZone(str)) {
@@ -407,64 +429,6 @@ function parseDate(options) {
407
429
  function isDateStringWithTimeZone(str) {
408
430
  return /z$/i.test(str) || /[+-]\d\d:\d\d$/.test(str);
409
431
  }
410
- function addSegment(section, date, amount) {
411
- var _a;
412
- let val = (_a = section.value) !== null && _a !== void 0 ? _a : 0;
413
- if (section.type === 'dayPeriod') {
414
- val = date.hour() + (date.hour() > 12 ? -12 : 12);
415
- }
416
- else {
417
- val = val + amount;
418
- const min = section.minValue;
419
- const max = section.maxValue;
420
- if (typeof min === 'number' && typeof max === 'number') {
421
- const length = max - min + 1;
422
- val = ((val - min + length) % length) + min;
423
- }
424
- }
425
- const type = getDurationUnitFromSectionType(section.type);
426
- return date.set(type, val);
427
- }
428
- function setSegment(section, date, amount) {
429
- const type = section.type;
430
- switch (type) {
431
- case 'day':
432
- case 'weekday':
433
- case 'month':
434
- case 'year': {
435
- return date.set(getDurationUnitFromSectionType(type), amount);
436
- }
437
- case 'dayPeriod': {
438
- const hours = date.hour();
439
- const wasPM = hours >= 12;
440
- const isPM = amount >= 12;
441
- if (isPM === wasPM) {
442
- return date;
443
- }
444
- return date.set('hour', wasPM ? hours - 12 : hours + 12);
445
- }
446
- case 'hour': {
447
- // In 12 hour time, ensure that AM/PM does not change
448
- let sectionAmount = amount;
449
- if (section.minValue === 12 || section.maxValue === 11) {
450
- const hours = date.hour();
451
- const wasPM = hours >= 12;
452
- if (!wasPM && sectionAmount === 12) {
453
- sectionAmount = 0;
454
- }
455
- if (wasPM && sectionAmount < 12) {
456
- sectionAmount += 12;
457
- }
458
- }
459
- return date.set('hour', sectionAmount);
460
- }
461
- case 'minute':
462
- case 'second': {
463
- return date.set(type, amount);
464
- }
465
- }
466
- return date;
467
- }
468
432
  function getCurrentEditableSectionIndex(sections, selectedSections) {
469
433
  const currentIndex = selectedSections === 'all' || selectedSections === -1 ? 0 : selectedSections;
470
434
  const section = sections[currentIndex];
@@ -514,9 +478,13 @@ function getEditableSections(sections, value, validSegments) {
514
478
  let renderedValue = section.placeholder;
515
479
  if ((isEditable && validSegments[section.type]) || section.type === 'timeZoneName') {
516
480
  renderedValue = value.format(section.format);
481
+ if (section.contentType === 'digit' &&
482
+ renderedValue.length < section.placeholder.length) {
483
+ renderedValue = renderedValue.padStart(section.placeholder.length, '0');
484
+ }
517
485
  }
518
486
  const sectionLength = renderedValue.length;
519
- const newSection = Object.assign(Object.assign(Object.assign({}, section), { value: getValue(value, section.type), textValue: renderedValue, start: position, end: position + sectionLength, modified: false, previousEditableSection, nextEditableSection: previousEditableSection }), getSectionLimits(value, section.type, { hour12: isHour12(section) }));
487
+ const newSection = Object.assign(Object.assign(Object.assign({}, section), { value: getSectionValue(section, value), textValue: renderedValue, start: position, end: position + sectionLength, modified: false, previousEditableSection, nextEditableSection: previousEditableSection }), getSectionLimits(section, value));
520
488
  newSections.push(newSection);
521
489
  if (isEditable) {
522
490
  for (let j = Math.max(0, previousEditableSection); j <= i; j++) {
@@ -534,88 +502,3 @@ function getEditableSections(sections, value, validSegments) {
534
502
  }
535
503
  return newSections;
536
504
  }
537
- function getValue(date, type) {
538
- switch (type) {
539
- case 'year':
540
- case 'month':
541
- case 'hour':
542
- case 'minute':
543
- case 'second': {
544
- return date[type]();
545
- }
546
- case 'day': {
547
- return date.date();
548
- }
549
- case 'weekday': {
550
- return date.day();
551
- }
552
- case 'dayPeriod': {
553
- return date.hour() >= 12 ? 12 : 0;
554
- }
555
- }
556
- return undefined;
557
- }
558
- function isHour12(section) {
559
- if (section.type === 'hour') {
560
- return dateTime().set('hour', 15).format(section.format) !== '15';
561
- }
562
- return false;
563
- }
564
- function getSectionLimits(date, type, options) {
565
- switch (type) {
566
- case 'year': {
567
- return {
568
- minValue: 1,
569
- maxValue: 9999,
570
- };
571
- }
572
- case 'month': {
573
- return {
574
- minValue: 0,
575
- maxValue: 11,
576
- };
577
- }
578
- case 'weekday': {
579
- return {
580
- minValue: 0,
581
- maxValue: 6,
582
- };
583
- }
584
- case 'day': {
585
- return {
586
- minValue: 1,
587
- maxValue: date ? date.daysInMonth() : 31,
588
- };
589
- }
590
- case 'hour': {
591
- if (options.hour12) {
592
- const isPM = date.hour() >= 12;
593
- return {
594
- minValue: isPM ? 12 : 0,
595
- maxValue: isPM ? 23 : 11,
596
- };
597
- }
598
- return {
599
- minValue: 0,
600
- maxValue: 23,
601
- };
602
- }
603
- case 'minute':
604
- case 'second': {
605
- return {
606
- minValue: 0,
607
- maxValue: 59,
608
- };
609
- }
610
- }
611
- return {};
612
- }
613
- function getDurationUnitFromSectionType(type) {
614
- if (type === 'literal' || type === 'timeZoneName' || type === 'unknown') {
615
- throw new Error(`${type} section does not have duration unit.`);
616
- }
617
- if (type in TYPE_MAPPING) {
618
- return TYPE_MAPPING[type];
619
- }
620
- return type;
621
- }
@@ -1,4 +1,4 @@
1
- export type DateFieldSectionType = Exclude<Intl.DateTimeFormatPartTypes, 'era'>;
1
+ export type DateFieldSectionType = Extract<Intl.DateTimeFormatPartTypes, 'day' | 'dayPeriod' | 'hour' | 'literal' | 'minute' | 'month' | 'second' | 'timeZoneName' | 'weekday' | 'year' | 'unknown'>;
2
2
  export type DateFormatTokenMap = {
3
3
  [formatToken: string]: DateFieldSectionType | {
4
4
  sectionType: DateFieldSectionType;
@@ -1,14 +1,17 @@
1
1
  import type { DateTime } from '@gravity-ui/date-utils';
2
- import type { DateFieldSectionWithoutPosition } from './types';
2
+ import type { DateFieldSection, DateFieldSectionType, DateFieldSectionWithoutPosition } from './types';
3
3
  export declare const isMac: boolean;
4
4
  export declare const CtrlCmd: string;
5
5
  export declare function expandFormat(format: string): string;
6
+ export declare function getSectionLimits(section: DateFieldSectionWithoutPosition, date: DateTime): {
7
+ minValue: number;
8
+ maxValue: number;
9
+ } | {
10
+ minValue?: undefined;
11
+ maxValue?: undefined;
12
+ };
13
+ export declare function getSectionValue(sections: DateFieldSectionWithoutPosition, date: DateTime): number | undefined;
14
+ export declare function getDurationUnitFromSectionType(type: DateFieldSectionType): "year" | "month" | "day" | "date" | "hour" | "minute" | "second";
15
+ export declare function addSegment(section: DateFieldSection, date: DateTime, amount: number): DateTime;
16
+ export declare function setSegment(section: DateFieldSection, date: DateTime, amount: number): DateTime;
6
17
  export declare function splitFormatIntoSections(format: string): DateFieldSectionWithoutPosition[];
7
- interface PlaceholderValueOptions {
8
- placeholderValue?: DateTime;
9
- timeZone?: string;
10
- }
11
- export declare function createPlaceholderValue({ placeholderValue, timeZone }: PlaceholderValueOptions): DateTime;
12
- export declare function isInvalid(value: DateTime | null | undefined, minValue: DateTime | undefined, maxValue: DateTime | undefined): boolean;
13
- export declare function mergeDateTime(date: DateTime, time: DateTime): DateTime;
14
- export {};
@@ -77,6 +77,168 @@ function getDateSectionConfigFromFormatToken(formatToken) {
77
77
  function isFourDigitYearFormat(format) {
78
78
  return dateTime().format(format).length === 4;
79
79
  }
80
+ function isHour12(format) {
81
+ return dateTime().set('hour', 15).format(format) !== '15';
82
+ }
83
+ export function getSectionLimits(section, date) {
84
+ const { type, format } = section;
85
+ switch (type) {
86
+ case 'year': {
87
+ const isFourDigit = isFourDigitYearFormat(format);
88
+ return {
89
+ minValue: isFourDigit ? 1 : 0,
90
+ maxValue: isFourDigit ? 9999 : 99,
91
+ };
92
+ }
93
+ case 'month': {
94
+ return {
95
+ minValue: 0,
96
+ maxValue: 11,
97
+ };
98
+ }
99
+ case 'weekday': {
100
+ return {
101
+ minValue: 0,
102
+ maxValue: 6,
103
+ };
104
+ }
105
+ case 'day': {
106
+ return {
107
+ minValue: 1,
108
+ maxValue: date ? date.daysInMonth() : 31,
109
+ };
110
+ }
111
+ case 'hour': {
112
+ if (isHour12(format)) {
113
+ const isPM = date.hour() >= 12;
114
+ return {
115
+ minValue: isPM ? 12 : 0,
116
+ maxValue: isPM ? 23 : 11,
117
+ };
118
+ }
119
+ return {
120
+ minValue: 0,
121
+ maxValue: 23,
122
+ };
123
+ }
124
+ case 'minute':
125
+ case 'second': {
126
+ return {
127
+ minValue: 0,
128
+ maxValue: 59,
129
+ };
130
+ }
131
+ }
132
+ return {};
133
+ }
134
+ export function getSectionValue(sections, date) {
135
+ const type = sections.type;
136
+ switch (type) {
137
+ case 'year': {
138
+ return isFourDigitYearFormat(sections.format)
139
+ ? date.year()
140
+ : Number(date.format(sections.format));
141
+ }
142
+ case 'month':
143
+ case 'hour':
144
+ case 'minute':
145
+ case 'second': {
146
+ return date[type]();
147
+ }
148
+ case 'day': {
149
+ return date.date();
150
+ }
151
+ case 'weekday': {
152
+ return date.day();
153
+ }
154
+ case 'dayPeriod': {
155
+ return date.hour() >= 12 ? 12 : 0;
156
+ }
157
+ }
158
+ return undefined;
159
+ }
160
+ const TYPE_MAPPING = {
161
+ weekday: 'day',
162
+ day: 'date',
163
+ dayPeriod: 'hour',
164
+ };
165
+ export function getDurationUnitFromSectionType(type) {
166
+ if (type === 'literal' || type === 'timeZoneName' || type === 'unknown') {
167
+ throw new Error(`${type} section does not have duration unit.`);
168
+ }
169
+ if (type in TYPE_MAPPING) {
170
+ return TYPE_MAPPING[type];
171
+ }
172
+ return type;
173
+ }
174
+ export function addSegment(section, date, amount) {
175
+ var _a;
176
+ let val = (_a = section.value) !== null && _a !== void 0 ? _a : 0;
177
+ if (section.type === 'dayPeriod') {
178
+ val = date.hour() + (date.hour() > 12 ? -12 : 12);
179
+ }
180
+ else {
181
+ val = val + amount;
182
+ const min = section.minValue;
183
+ const max = section.maxValue;
184
+ if (typeof min === 'number' && typeof max === 'number') {
185
+ const length = max - min + 1;
186
+ val = ((val - min + length) % length) + min;
187
+ }
188
+ }
189
+ if (section.type === 'year' && !isFourDigitYearFormat(section.format)) {
190
+ val = dateTime({ input: `${val}`.padStart(2, '0'), format: section.format }).year();
191
+ }
192
+ const type = getDurationUnitFromSectionType(section.type);
193
+ return date.set(type, val);
194
+ }
195
+ export function setSegment(section, date, amount) {
196
+ const type = section.type;
197
+ switch (type) {
198
+ case 'year': {
199
+ return date.set('year', isFourDigitYearFormat(section.format)
200
+ ? amount
201
+ : dateTime({
202
+ input: `${amount}`.padStart(2, '0'),
203
+ format: section.format,
204
+ }).year());
205
+ }
206
+ case 'day':
207
+ case 'weekday':
208
+ case 'month': {
209
+ return date.set(getDurationUnitFromSectionType(type), amount);
210
+ }
211
+ case 'dayPeriod': {
212
+ const hours = date.hour();
213
+ const wasPM = hours >= 12;
214
+ const isPM = amount >= 12;
215
+ if (isPM === wasPM) {
216
+ return date;
217
+ }
218
+ return date.set('hour', wasPM ? hours - 12 : hours + 12);
219
+ }
220
+ case 'hour': {
221
+ // In 12 hour time, ensure that AM/PM does not change
222
+ let sectionAmount = amount;
223
+ if (section.minValue === 12 || section.maxValue === 11) {
224
+ const hours = date.hour();
225
+ const wasPM = hours >= 12;
226
+ if (!wasPM && sectionAmount === 12) {
227
+ sectionAmount = 0;
228
+ }
229
+ if (wasPM && sectionAmount < 12) {
230
+ sectionAmount += 12;
231
+ }
232
+ }
233
+ return date.set('hour', sectionAmount);
234
+ }
235
+ case 'minute':
236
+ case 'second': {
237
+ return date.set(type, amount);
238
+ }
239
+ }
240
+ return date;
241
+ }
80
242
  function doesSectionHaveLeadingZeros(contentType, sectionType, format) {
81
243
  if (contentType !== 'digit') {
82
244
  return false;
@@ -245,24 +407,3 @@ function getSectionOptions(section, token) {
245
407
  }
246
408
  return undefined;
247
409
  }
248
- export function createPlaceholderValue({ placeholderValue, timeZone }) {
249
- return (placeholderValue !== null && placeholderValue !== void 0 ? placeholderValue : dateTime({ timeZone }).set('hour', 0).set('minute', 0).set('second', 0));
250
- }
251
- export function isInvalid(value, minValue, maxValue) {
252
- if (!value) {
253
- return false;
254
- }
255
- if (minValue && value.isBefore(minValue)) {
256
- return true;
257
- }
258
- if (maxValue && maxValue.isBefore(value)) {
259
- return true;
260
- }
261
- return false;
262
- }
263
- export function mergeDateTime(date, time) {
264
- return date
265
- .set('hours', time.hour())
266
- .set('minutes', time.minute())
267
- .set('seconds', time.second());
268
- }
@@ -1,6 +1,7 @@
1
1
  .g-date-date-picker {
2
2
  position: relative;
3
3
  display: inline-block;
4
+ outline: none;
4
5
  }
5
6
  .g-date-date-picker__field {
6
7
  width: 100%;
@@ -8,6 +9,11 @@
8
9
  .g-date-date-picker__field_mobile {
9
10
  pointer-events: none;
10
11
  }
12
+ .g-date-date-picker__popup-anchor {
13
+ position: absolute;
14
+ z-index: -1;
15
+ inset: 0;
16
+ }
11
17
  .g-date-date-picker__popup-content {
12
18
  outline: none;
13
19
  }
@@ -1,5 +1,8 @@
1
+ import React from 'react';
2
+ import { type CalendarProps } from '../Calendar';
1
3
  import type { AccessibilityProps, DateFieldBase, DomProps, FocusableProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
2
4
  import './DatePicker.css';
3
5
  export interface DatePickerProps extends DateFieldBase, TextInputProps, FocusableProps, KeyboardEvents, DomProps, StyleProps, AccessibilityProps {
6
+ children?: (props: CalendarProps) => React.ReactNode;
4
7
  }
5
- export declare function DatePicker({ value, defaultValue, onUpdate, className, onFocus, onBlur, ...props }: DatePickerProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function DatePicker({ value, defaultValue, onUpdate, className, ...props }: DatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -11,49 +11,22 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import React from 'react';
14
- import { TextInput, useFocusWithin, useMobile } from '@gravity-ui/uikit';
15
- import { useDateFieldProps, useDateFieldState } from '../DateField';
16
- import { DesktopCalendar, DesktopCalendarButton } from './DesktopCalendar';
14
+ import { Calendar as CalendarIcon } from '@gravity-ui/icons';
15
+ import { Button, Icon, Popup, TextInput, useMobile } from '@gravity-ui/uikit';
16
+ import { Calendar } from '../Calendar';
17
+ import { DateField } from '../DateField';
17
18
  import { MobileCalendar, MobileCalendarIcon } from './MobileCalendar';
19
+ import { useDatePickerProps } from './hooks/useDatePickerProps';
18
20
  import { useDatePickerState } from './hooks/useDatePickerState';
19
21
  import { b } from './utils';
20
22
  import './DatePicker.css';
21
23
  export function DatePicker(_a) {
22
- var { value, defaultValue, onUpdate, className, onFocus, onBlur } = _a, props = __rest(_a, ["value", "defaultValue", "onUpdate", "className", "onFocus", "onBlur"]);
24
+ var { value, defaultValue, onUpdate, className } = _a, props = __rest(_a, ["value", "defaultValue", "onUpdate", "className"]);
23
25
  const anchorRef = React.useRef(null);
24
26
  const state = useDatePickerState(Object.assign(Object.assign({}, props), { value,
25
27
  defaultValue,
26
28
  onUpdate }));
29
+ const { groupProps, fieldProps, calendarButtonProps, popupProps, calendarProps, timeInputProps } = useDatePickerProps(state, props);
27
30
  const [isMobile] = useMobile();
28
- const [isActive, setActive] = React.useState(false);
29
- const { focusWithinProps } = useFocusWithin({
30
- onFocusWithin: onFocus,
31
- onBlurWithin: onBlur,
32
- onFocusWithinChange(isFocusWithin) {
33
- setActive(isFocusWithin);
34
- },
35
- });
36
- const fieldState = useDateFieldState({
37
- value: state.value,
38
- onUpdate: state.setValue,
39
- disabled: state.disabled,
40
- readOnly: state.readOnly,
41
- validationState: props.validationState,
42
- minValue: props.minValue,
43
- maxValue: props.maxValue,
44
- isDateUnavailable: props.isDateUnavailable,
45
- format: state.format,
46
- placeholderValue: props.placeholderValue,
47
- timeZone: props.timeZone,
48
- });
49
- const { inputProps } = useDateFieldProps(fieldState, props);
50
- return (
51
- // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
52
- _jsxs("div", Object.assign({ ref: anchorRef, className: b(null, className), style: props.style }, focusWithinProps, { role: "group", "aria-disabled": state.disabled || undefined, onKeyDown: (e) => {
53
- if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
54
- e.preventDefault();
55
- e.stopPropagation();
56
- state.setOpen(true);
57
- }
58
- }, children: [isMobile ? (_jsx(MobileCalendar, { props: props, state: state })) : (_jsx(DesktopCalendar, { anchorRef: anchorRef, props: props, state: state })), _jsx(TextInput, Object.assign({}, inputProps, { value: fieldState.isEmpty && !isActive && props.placeholder ? '' : inputProps.value, className: b('field', { mobile: isMobile }), hasClear: !isMobile && inputProps.hasClear, rightContent: isMobile ? (_jsx(MobileCalendarIcon, { props: props, state: state })) : (_jsx(DesktopCalendarButton, { props: props, state: state })) }))] })));
31
+ return (_jsxs("div", Object.assign({ className: b(null, className) }, groupProps, { children: [isMobile ? (_jsx(MobileCalendar, { props: props, state: state })) : (_jsx("div", { ref: anchorRef, className: b('popup-anchor'), children: _jsx(Popup, Object.assign({ anchorRef: anchorRef }, popupProps, { children: _jsxs("div", { className: b('popup-content'), children: [typeof props.children === 'function' ? (props.children(calendarProps)) : (_jsx(Calendar, Object.assign({}, calendarProps))), state.hasTime && (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, Object.assign({}, timeInputProps)) }))] }) })) })), _jsx(TextInput, Object.assign({}, fieldProps, { className: b('field', { mobile: isMobile }), hasClear: !isMobile && fieldProps.hasClear, rightContent: isMobile ? (_jsx(MobileCalendarIcon, { props: props, state: state })) : (_jsx(Button, Object.assign({}, calendarButtonProps, { children: _jsx(Icon, { data: CalendarIcon }) }))) }))] })));
59
32
  }
@@ -3,7 +3,7 @@ import { dateTime } from '@gravity-ui/date-utils';
3
3
  import { Calendar as CalendarIcon } from '@gravity-ui/icons';
4
4
  import { Icon } from '@gravity-ui/uikit';
5
5
  import { block } from '../../utils/cn';
6
- import { createPlaceholderValue, mergeDateTime } from '../DateField/utils';
6
+ import { createPlaceholderValue, mergeDateTime } from '../utils/dates';
7
7
  import { getButtonSizeForInput } from '../utils/getButtonSizeForInput';
8
8
  import './MobileCalendar.css';
9
9
  const b = block('mobile-calendar');
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import type { ButtonProps, PopupProps, TextInputProps } from '@gravity-ui/uikit';
3
+ import type { Calendar } from '../../Calendar';
4
+ import type { DateFieldProps } from '../../DateField';
5
+ import type { DatePickerProps } from '../DatePicker';
6
+ import type { DatePickerState } from './useDatePickerState';
7
+ interface InnerRelativeDatePickerProps {
8
+ groupProps: React.HTMLAttributes<unknown> & {
9
+ ref: React.Ref<any>;
10
+ };
11
+ fieldProps: TextInputProps;
12
+ calendarButtonProps: ButtonProps & {
13
+ ref: React.Ref<HTMLButtonElement>;
14
+ };
15
+ popupProps: PopupProps;
16
+ calendarProps: React.ComponentProps<typeof Calendar>;
17
+ timeInputProps: DateFieldProps;
18
+ }
19
+ export declare function useDatePickerProps(state: DatePickerState, { onFocus, onBlur, ...props }: DatePickerProps): InnerRelativeDatePickerProps;
20
+ export {};