@jetbrains/ring-ui 7.0.113 → 7.0.115

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.
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from 'react';
2
- import type { Locale } from 'date-fns';
2
+ import { type Locale } from 'date-fns';
3
3
  declare const units: {
4
4
  unit: number;
5
5
  cellSize: number;
@@ -24,10 +24,11 @@ export declare const yearDuration: number;
24
24
  export declare const yearScrollSpeed: number;
25
25
  export declare const DOUBLE = 2;
26
26
  export declare const HALF = 0.5;
27
- export declare function parseTime(time: string): string | null;
27
+ export declare function parseTime(time: string | null | undefined): string | null;
28
28
  export declare function shiftWeekArray<T>(arr: T[], startOfWeek: number): T[];
29
29
  export declare function getWeekStartsOn(locale: Locale | undefined): number;
30
30
  export declare function getDayNumInWeek(locale: Locale | undefined, day: number): number;
31
+ export declare function getDefaultScrollDate({ minDate, maxDate, parseDateInput, }: Pick<DatePopupBaseProps, 'minDate' | 'maxDate' | 'parseDateInput'>): Date;
31
32
  export interface DateInputTranslations {
32
33
  addFirstDate?: string;
33
34
  addSecondDate?: string;
@@ -1,4 +1,5 @@
1
1
  import { add } from 'date-fns/add';
2
+ import { endOfDay } from 'date-fns';
2
3
  import sniffer from '../global/sniffer';
3
4
  const unit = 8; // px;
4
5
  const units = {
@@ -29,14 +30,15 @@ export const yearScrollSpeed = yearDuration / (YEAR * units.cellSize);
29
30
  export const DOUBLE = 2;
30
31
  export const HALF = 0.5;
31
32
  export function parseTime(time) {
32
- let result = null;
33
+ if (!time)
34
+ return null;
33
35
  if (/^([01][0-9]|2[0-3]):[0-5][0-9]$/.test(time)) {
34
- result = time;
36
+ return time;
35
37
  }
36
- else if (/^([0-9]|2[0-3]):[0-5][0-9]$/.test(time)) {
37
- result = `0${time}`;
38
+ if (/^([0-9]|2[0-3]):[0-5][0-9]$/.test(time)) {
39
+ return `0${time}`;
38
40
  }
39
- return result;
41
+ return null;
40
42
  }
41
43
  export function shiftWeekArray(arr, startOfWeek) {
42
44
  const shiftTimes = startOfWeek - 1;
@@ -49,6 +51,25 @@ export function getDayNumInWeek(locale, day) {
49
51
  const weekDays = shiftWeekArray(Object.values(weekdays), getWeekStartsOn(locale));
50
52
  return weekDays.indexOf(day);
51
53
  }
54
+ export function getDefaultScrollDate({ minDate, maxDate, parseDateInput, }) {
55
+ const minDateParsed = parseDateInput(minDate);
56
+ const maxDateParsed = parseDateInput(maxDate);
57
+ const maxDateEndOfDayNum = maxDateParsed ? Number(endOfDay(maxDateParsed)) : null;
58
+ const now = Date.now();
59
+ if (minDateParsed != null && maxDateEndOfDayNum != null) {
60
+ if (minDateParsed.getTime() <= now && now <= maxDateEndOfDayNum) {
61
+ return new Date(now);
62
+ }
63
+ return minDateParsed;
64
+ }
65
+ if (minDateParsed != null && minDateParsed.getTime() > now) {
66
+ return minDateParsed;
67
+ }
68
+ if (maxDateParsed != null && maxDateEndOfDayNum != null && maxDateEndOfDayNum < now) {
69
+ return maxDateParsed;
70
+ }
71
+ return new Date(now);
72
+ }
52
73
  /**
53
74
  * Safari on iPhone doesn't allow setting scrollTop while a scroll is in progress.
54
75
  * If you do, the browser will overwrite it during the next scroll event.
@@ -97,8 +97,16 @@ export default class DatePicker extends PureComponent {
97
97
  static contextType = I18nContext;
98
98
  handleChange = (change) => {
99
99
  const { onChange, withTime, applyTimeInput } = this.props;
100
- const adjustedChange = withTime && !(change instanceof Date) && change?.date ? applyTimeInput(change.date, change.time) : change;
101
- onChange(adjustedChange);
100
+ if (withTime && !(change instanceof Date) && change && 'date' in change && 'time' in change) {
101
+ const { date, time } = change;
102
+ if (date) {
103
+ const dateWithTime = applyTimeInput(date, time);
104
+ onChange(dateWithTime);
105
+ }
106
+ }
107
+ else {
108
+ onChange(change);
109
+ }
102
110
  };
103
111
  clear = () => {
104
112
  let change = null;
@@ -116,14 +124,18 @@ export default class DatePicker extends PureComponent {
116
124
  this.popup?._onCloseAttempt();
117
125
  };
118
126
  parse = memoize((date) => {
119
- const { parseDateInput } = this.props;
127
+ let normalizedDate;
120
128
  if (date instanceof Date) {
121
- return date;
129
+ normalizedDate = date;
130
+ }
131
+ else if (typeof date === 'number') {
132
+ normalizedDate = new Date(date);
122
133
  }
123
- if (typeof date === 'number') {
124
- return new Date(date);
134
+ else {
135
+ const { parseDateInput } = this.props;
136
+ normalizedDate = parseDateInput(date);
125
137
  }
126
- return parseDateInput(date);
138
+ return normalizedDate && isValid(normalizedDate) ? normalizedDate : null;
127
139
  });
128
140
  formatTime() {
129
141
  const { displayTimeFormat, locale } = this.props;
@@ -7,13 +7,11 @@ export default class DatePopup extends Component<DatePopupProps, DatePopupState>
7
7
  onChange(): void;
8
8
  };
9
9
  constructor(props: DatePopupProps);
10
- componentDidUpdate(prevProps: DatePopupBaseProps, prevState: DatePopupState): void;
10
+ componentDidUpdate(_prevProps: DatePopupBaseProps, prevState: DatePopupState): void;
11
11
  componentWillUnmount(): void;
12
12
  private animationCleanup;
13
13
  isInTimeMode: () => boolean;
14
14
  componentRef: React.RefObject<HTMLDivElement | null>;
15
- parse(text: string | null | undefined, type: 'time'): string;
16
- parse(text: Date | number | string | null | undefined, type?: 'date' | 'from' | 'to'): Date;
17
15
  select(changes: DatePickerChange): void;
18
16
  confirm(name: Field): void;
19
17
  isValidDate: (parsedText: Date) => boolean;
@@ -9,7 +9,7 @@ import DateInput from './date-input';
9
9
  import Months from './months';
10
10
  import Years from './years';
11
11
  import Weekdays from './weekdays';
12
- import { parseTime, } from './consts';
12
+ import { parseTime, getDefaultScrollDate, } from './consts';
13
13
  import { animateDate } from './animate-date';
14
14
  import MonthNames from './month-names';
15
15
  import styles from './date-picker.css';
@@ -24,21 +24,25 @@ export default class DatePopup extends Component {
24
24
  hoverDate: null,
25
25
  scrollDate: null,
26
26
  };
27
- const { range, withTime } = props;
27
+ const { range, withTime, parseDateInput } = props;
28
28
  if (!range) {
29
- const parsedDate = this.parse(props.date, 'date');
29
+ const parsedDate = parseDateInput(props.date);
30
30
  const active = withTime && parsedDate && !props.time ? 'time' : 'date';
31
- this.state = { ...defaultState, active, scrollDate: { date: parsedDate, source: 'other' } };
31
+ this.state = {
32
+ ...defaultState,
33
+ active,
34
+ scrollDate: { date: parsedDate ?? getDefaultScrollDate(props), source: 'other' },
35
+ };
32
36
  }
33
37
  else {
34
38
  this.state = {
35
39
  ...defaultState,
36
40
  active: props.from && !props.to ? 'to' : 'from',
37
- scrollDate: { date: this.parse(props.from, 'from'), source: 'other' },
41
+ scrollDate: { date: parseDateInput(props.from) ?? getDefaultScrollDate(props), source: 'other' },
38
42
  };
39
43
  }
40
44
  }
41
- componentDidUpdate(prevProps, prevState) {
45
+ componentDidUpdate(_prevProps, prevState) {
42
46
  if (this.state.active !== prevState.active) {
43
47
  if (this.state.text && prevState.active) {
44
48
  this.confirm(prevState.active);
@@ -52,14 +56,8 @@ export default class DatePopup extends Component {
52
56
  animationCleanup = null;
53
57
  isInTimeMode = () => (!this.props.range && this.props.withTime) || false;
54
58
  componentRef = React.createRef();
55
- parse(text, type) {
56
- if (type === 'time') {
57
- return parseTime(String(text));
58
- }
59
- return this.props.parseDateInput(text);
60
- }
61
59
  select(changes) {
62
- const { range, withTime } = this.props;
60
+ const { range, withTime, parseDateInput } = this.props;
63
61
  const prevActive = this.state.active;
64
62
  if (!range && !withTime) {
65
63
  this.setState({
@@ -76,8 +74,8 @@ export default class DatePopup extends Component {
76
74
  this.props.onComplete();
77
75
  }
78
76
  else if (!range && withTime) {
79
- const date = this.parse(this.props.date, 'date');
80
- const time = this.parse(this.props.time, 'time');
77
+ const date = parseDateInput(this.props.date);
78
+ const time = parseTime(this.props.time);
81
79
  const changeToSubmit = {
82
80
  date: changes.date || date,
83
81
  time: changes.time || time,
@@ -97,8 +95,8 @@ export default class DatePopup extends Component {
97
95
  ...this.props,
98
96
  ...changes,
99
97
  };
100
- from = this.parse(from, 'from');
101
- to = this.parse(to, 'to');
98
+ from = parseDateInput(from);
99
+ to = parseDateInput(to);
102
100
  // proceed to setting the end by default
103
101
  let active = 'to';
104
102
  let complete = false;
@@ -133,15 +131,16 @@ export default class DatePopup extends Component {
133
131
  const text = this.state.text;
134
132
  let result;
135
133
  if (name === 'time') {
136
- result = this.parse(text, name);
137
- const time = this.parse('time' in this.props ? this.props.time : '', 'time');
134
+ result = parseTime(text);
135
+ const time = parseTime('time' in this.props ? this.props.time : '');
138
136
  const emptyCase = this.state.active === 'time' ? '00:00' : null;
139
137
  result = result || time || emptyCase;
140
138
  }
141
139
  else {
142
- result = this.parse(text, name);
143
- if (!this.isValidDate(result)) {
144
- result = this.parse(name in this.props ? this.props[name] : '', name);
140
+ const { parseDateInput } = this.props;
141
+ result = parseDateInput(text);
142
+ if (!result || !this.isValidDate(result)) {
143
+ result = parseDateInput(name in this.props ? this.props[name] : '');
145
144
  }
146
145
  }
147
146
  this.select({
@@ -149,10 +148,12 @@ export default class DatePopup extends Component {
149
148
  });
150
149
  }
151
150
  isValidDate = (parsedText) => {
152
- const minDate = this.parse(this.props.minDate, 'date');
153
- const maxDate = this.parse(this.props.maxDate, 'date');
151
+ const { parseDateInput, minDate, maxDate } = this.props;
152
+ const parsedMinDate = parseDateInput(minDate);
153
+ const parsedMaxDate = parseDateInput(maxDate);
154
154
  if (parsedText) {
155
- return !((minDate && isBefore(parsedText, minDate)) || (maxDate && isAfter(parsedText, maxDate)));
155
+ return !((parsedMinDate && isBefore(parsedText, parsedMinDate)) ||
156
+ (parsedMaxDate && isAfter(parsedText, parsedMaxDate)));
156
157
  }
157
158
  return false;
158
159
  };
@@ -160,8 +161,8 @@ export default class DatePopup extends Component {
160
161
  handleActivate = memoize((name) => () => this.setState({ active: name }));
161
162
  handleInput = (text, name) => {
162
163
  if (name !== 'time') {
163
- const parsed = this.parse(text, name);
164
- if (this.isValidDate(parsed)) {
164
+ const parsed = this.props.parseDateInput(text);
165
+ if (parsed && this.isValidDate(parsed)) {
165
166
  this.animationCleanup?.();
166
167
  const currentScrollDate = this.state.scrollDate?.date;
167
168
  if (currentScrollDate != null) {
@@ -217,20 +218,20 @@ export default class DatePopup extends Component {
217
218
  };
218
219
  // eslint-disable-next-line complexity
219
220
  render() {
220
- const { range, locale } = this.props;
221
+ const { range, locale, parseDateInput } = this.props;
221
222
  const { from, to, date, time, ...restProps } = this.props;
222
- const parsedDate = this.parse(this.props.date, 'date');
223
- const parsedTo = this.parse(this.props.to, 'to');
223
+ const parsedDate = parseDateInput(date);
224
+ const parsedTo = parseDateInput(to);
224
225
  const names = range ? ['from', 'to'] : ['date'];
225
226
  const dates = names.reduce((obj, key) => {
226
227
  const value = this.props[key];
227
228
  return {
228
229
  ...obj,
229
- [key]: this.parse(value, key),
230
+ [key]: parseDateInput(value),
230
231
  };
231
232
  }, {});
232
233
  const activeDate = this.state.active !== 'time'
233
- ? this.state.hoverDate || (this.state.text ? this.parse(this.state.text, 'date') : null)
234
+ ? this.state.hoverDate || (this.state.text ? parseDateInput(this.state.text) : null)
234
235
  : this.state.hoverDate || null;
235
236
  const currentRange = (range && dates.from && dates.to && [dates.from, dates.to]) || null;
236
237
  let activeRange = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.113",
3
+ "version": "7.0.115",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"