@jetbrains/ring-ui 7.0.112 → 7.0.114

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.
@@ -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;
@@ -51,11 +51,11 @@ export const setCSSAnchorPositioning = ({ popup, anchor, uid, minWidth, top, lef
51
51
  if (calculatedMinWidth !== null) {
52
52
  popup.style.minWidth = `${calculatedMinWidth}px`;
53
53
  }
54
- if (top) {
55
- popup.style.transform = `translateY(${top}px)`;
54
+ if (top || left) {
55
+ popup.style.transform = [top && `translateY(${top}px)`, left && `translateX(${left}px)`].filter(Boolean).join(' ');
56
56
  }
57
- if (left) {
58
- popup.style.left = `${left}px`;
57
+ else {
58
+ popup.style.removeProperty('transform');
59
59
  }
60
60
  // When all directions are BOTTOM, the `max-height: 100%` from CSS should stay applied so popup doesn't overflow the anchor RG-2754
61
61
  const SHOULD_AUTO_SHRINK = directions.every(d => d.startsWith('BOTTOM'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.112",
3
+ "version": "7.0.114",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"
@@ -112,16 +112,16 @@
112
112
  "@jetbrains/rollup-css-plugin": "./packages/rollup-css-plugin",
113
113
  "@jetbrains/stylelint-config": "^4.0.2",
114
114
  "@jetbrains/typescript-plugin-css-modules": "^5.3.1",
115
- "@primer/octicons": "^19.25.0",
115
+ "@primer/octicons": "^19.27.0",
116
116
  "@rollup/plugin-babel": "^7.0.0",
117
117
  "@rollup/plugin-json": "^6.1.0",
118
118
  "@rollup/plugin-node-resolve": "^16.0.3",
119
119
  "@rollup/plugin-replace": "^6.0.3",
120
- "@storybook/addon-a11y": "10.4.0",
121
- "@storybook/addon-docs": "^10.4.0",
122
- "@storybook/addon-themes": "^10.4.0",
120
+ "@storybook/addon-a11y": "10.4.1",
121
+ "@storybook/addon-docs": "^10.4.1",
122
+ "@storybook/addon-themes": "^10.4.1",
123
123
  "@storybook/csf": "^0.1.13",
124
- "@storybook/react-webpack5": "10.4.0",
124
+ "@storybook/react-webpack5": "10.4.1",
125
125
  "@storybook/test-runner": "^0.24.4",
126
126
  "@testing-library/dom": "^10.4.1",
127
127
  "@testing-library/react": "^16.3.2",
@@ -129,14 +129,14 @@
129
129
  "@types/chai-as-promised": "^8.0.2",
130
130
  "@types/chai-dom": "1.11.3",
131
131
  "@types/markdown-it": "^14.1.2",
132
- "@types/react": "^19.2.14",
132
+ "@types/react": "^19.2.15",
133
133
  "@types/react-dom": "^19.2.3",
134
134
  "@types/webpack-env": "^1.18.8",
135
135
  "@vitejs/plugin-react": "^6.0.2",
136
136
  "@vitest/eslint-plugin": "^1.6.17",
137
137
  "acorn": "^8.16.0",
138
138
  "babel-plugin-require-context-hook": "^1.0.0",
139
- "caniuse-lite": "^1.0.30001777",
139
+ "caniuse-lite": "^1.0.30001793",
140
140
  "chai-as-promised": "^8.0.2",
141
141
  "chai-dom": "^1.12.1",
142
142
  "cheerio": "^1.2.0",
@@ -154,7 +154,7 @@
154
154
  "eslint-plugin-prettier": "^5.5.5",
155
155
  "eslint-plugin-react": "^7.37.5",
156
156
  "eslint-plugin-react-hooks": "^7.1.1",
157
- "eslint-plugin-storybook": "^10.4.0",
157
+ "eslint-plugin-storybook": "^10.4.1",
158
158
  "eslint-plugin-unicorn": "^64.0.0",
159
159
  "events": "^3.3.0",
160
160
  "glob": "^13.0.6",
@@ -167,7 +167,7 @@
167
167
  "jest-environment-jsdom": "^30.4.1",
168
168
  "jest-teamcity": "^1.12.0",
169
169
  "lint-staged": "^17.0.5",
170
- "markdown-it": "^14.1.1",
170
+ "markdown-it": "^14.2.0",
171
171
  "merge-options": "^3.0.4",
172
172
  "pinst": "^3.0.0",
173
173
  "prettier": "^3.8.3",
@@ -180,18 +180,18 @@
180
180
  "rollup-plugin-clear": "^2.0.7",
181
181
  "storage-mock": "^2.1.0",
182
182
  "storybook": "10.4.1",
183
- "stylelint": "^17.11.1",
183
+ "stylelint": "^17.12.0",
184
184
  "stylelint-config-sass-guidelines": "^13.0.0",
185
185
  "svg-inline-loader": "^0.8.2",
186
186
  "teamcity-service-messages": "^0.1.14",
187
187
  "terser-webpack-plugin": "^5.6.0",
188
188
  "typed-css-modules": "^0.9.1",
189
189
  "typescript": "~6.0.3",
190
- "typescript-eslint": "^8.59.3",
191
- "vite": "^8.0.13",
192
- "vitest": "^4.1.6",
190
+ "typescript-eslint": "^8.60.0",
191
+ "vite": "^8.0.14",
192
+ "vitest": "^4.1.7",
193
193
  "vitest-teamcity-reporter": "^0.4.1",
194
- "webpack": "^5.106.2",
194
+ "webpack": "^5.107.1",
195
195
  "webpack-cli": "^7.0.2",
196
196
  "xmlappend": "^1.0.4"
197
197
  },
@@ -233,7 +233,7 @@
233
233
  "combokeys": "^3.0.1",
234
234
  "css-loader": "^7.1.4",
235
235
  "csstype": "^3.2.1",
236
- "date-fns": "^4.1.0",
236
+ "date-fns": "^4.3.0",
237
237
  "dequal": "^2.0.3",
238
238
  "element-resize-detector": "^1.2.4",
239
239
  "fastdom": "^1.0.12",
@@ -242,7 +242,7 @@
242
242
  "highlight.js": "^10.7.2",
243
243
  "just-debounce-it": "^3.2.0",
244
244
  "memoize-one": "^6.0.0",
245
- "postcss": "^8.5.14",
245
+ "postcss": "^8.5.15",
246
246
  "postcss-calc": "^10.1.1",
247
247
  "postcss-font-family-system-ui": "^5.0.0",
248
248
  "postcss-loader": "^8.2.1",