@via-profit/ui-kit 0.2.1-alpha.1 → 0.2.1-alpha.3

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 (138) hide show
  1. package/Autocomplete/AutocompleteContainer.d.ts +33 -6
  2. package/Autocomplete/AutocompleteContainer.js +28 -24
  3. package/Autocomplete/AutocompleteItem.d.ts +1 -1
  4. package/Autocomplete/AutocompleteTextField.d.ts +3 -0
  5. package/Autocomplete/AutocompleteTextField.js +63 -1
  6. package/Autocomplete/OverrideMenuList.d.ts +4 -0
  7. package/Autocomplete/OverrideMenuList.js +39 -0
  8. package/Autocomplete/context.d.ts +9 -1
  9. package/Autocomplete/context.js +15 -1
  10. package/Autocomplete/index.js +9 -10
  11. package/Autocomplete2/Autocomplete2.d.ts +12 -0
  12. package/Autocomplete2/Autocomplete2.js +83 -0
  13. package/Autocomplete2/Autocomplete2TextField.d.ts +7 -0
  14. package/Autocomplete2/Autocomplete2TextField.js +39 -0
  15. package/Autocomplete2/index.d.ts +2 -0
  16. package/Autocomplete2/index.js +7 -0
  17. package/Avatar/index.js +1 -1
  18. package/Badge/BadgeDeleteButton.js +6 -6
  19. package/Badge/BadgeOutlined.js +2 -2
  20. package/Badge/BadgeStandard.js +1 -1
  21. package/Button/ButtonOutlined.js +1 -1
  22. package/Button/ButtonPlain.js +1 -1
  23. package/Button/ButtonStandard.js +4 -11
  24. package/Calendar/CalendarCell.js +6 -2
  25. package/Calendar/CalendarComponent.d.ts +226 -0
  26. package/Calendar/CalendarComponent.js +572 -0
  27. package/Calendar/CalendarControlButton.js +16 -9
  28. package/Calendar/CalendarDayBadge.js +1 -1
  29. package/Calendar/CalendarEmptyCell.d.ts +2 -2
  30. package/Calendar/CalendarEmptyCell.js +8 -3
  31. package/Calendar/CalendarHeading.js +2 -1
  32. package/Calendar/CalendarSubheading.js +3 -2
  33. package/Calendar/CalendarWeekDayCell.d.ts +10 -0
  34. package/Calendar/CalendarWeekDayCell.js +32 -0
  35. package/Calendar/CalendarWeekDayWeekNumber.d.ts +7 -0
  36. package/Calendar/CalendarWeekDayWeekNumber.js +38 -0
  37. package/Calendar/CalendarWeekRow.d.ts +2 -2
  38. package/Calendar/CalendarWeekRow.js +2 -1
  39. package/Calendar/CalendarWeekRowButton.d.ts +13 -0
  40. package/Calendar/CalendarWeekRowButton.js +31 -0
  41. package/Calendar/CalendarYearsSelector.d.ts +39 -1
  42. package/Calendar/CalendarYearsSelector.js +52 -16
  43. package/Calendar/IconChevronDown.d.ts +3 -0
  44. package/Calendar/IconChevronDown.js +31 -0
  45. package/Calendar/index.d.ts +7 -224
  46. package/Calendar/index.js +19 -281
  47. package/Calendar/use-calendar.d.ts +1 -1
  48. package/Color/Color.d.ts +138 -0
  49. package/Color/Color.js +324 -0
  50. package/Color/ColorGenerator.d.ts +29 -0
  51. package/Color/ColorGenerator.js +141 -0
  52. package/Color/ColorParser.d.ts +20 -0
  53. package/Color/ColorParser.js +284 -0
  54. package/Color/index.d.ts +4 -122
  55. package/Color/index.js +18 -473
  56. package/DatePicker/index.d.ts +2 -17
  57. package/DatePicker/index.js +47 -61
  58. package/DatePicker/useDatePickerFormat.js +5 -5
  59. package/ErrorBoundary/Fallback.d.ts +3 -0
  60. package/ErrorBoundary/Fallback.js +51 -0
  61. package/ErrorBoundary/RenderError.d.ts +6 -0
  62. package/ErrorBoundary/RenderError.js +80 -0
  63. package/ErrorBoundary/index.d.ts +7 -11
  64. package/ErrorBoundary/index.js +4 -51
  65. package/LoadingIndicator/Spinner.d.ts +5 -3
  66. package/LoadingIndicator/Spinner.js +61 -32
  67. package/LoadingIndicator/index.d.ts +3 -3
  68. package/LoadingIndicator/index.js +15 -2
  69. package/MaskedField/index.d.ts +1 -1
  70. package/MaskedField/index.js +11 -10
  71. package/Menu/AutoHeightWrapper.d.ts +6 -0
  72. package/Menu/AutoHeightWrapper.js +43 -0
  73. package/Menu/MeasuredItem.d.ts +10 -0
  74. package/Menu/MeasuredItem.js +51 -0
  75. package/Menu/MenuContainer.d.ts +51 -18
  76. package/Menu/MenuContainer.js +18 -11
  77. package/Menu/MenuItem.d.ts +24 -7
  78. package/Menu/MenuItem.js +13 -6
  79. package/Menu/MenuList.d.ts +4 -2
  80. package/Menu/MenuList.js +26 -6
  81. package/Menu/VirtualizedItem.d.ts +22 -0
  82. package/Menu/VirtualizedItem.js +69 -0
  83. package/Menu/VirtualizedList.d.ts +18 -0
  84. package/Menu/VirtualizedList.js +28 -0
  85. package/Menu/VirtualizedListComponent.d.ts +20 -0
  86. package/Menu/VirtualizedListComponent.js +305 -0
  87. package/Menu/index.d.ts +3 -1
  88. package/Menu/index.js +2 -0
  89. package/Popper/Popper.d.ts +7 -80
  90. package/Popper/Popper.js +26 -12
  91. package/Popper/PopperContainer.js +1 -1
  92. package/Popper/usePopprer.d.ts +88 -3
  93. package/Popper/usePopprer.js +240 -65
  94. package/Selectbox/SelectboxButton.d.ts +5 -0
  95. package/Selectbox/SelectboxButton.js +48 -8
  96. package/Selectbox/SelectboxButtonWrapper.d.ts +2 -0
  97. package/Selectbox/SelectboxButtonWrapper.js +1 -17
  98. package/Selectbox/SelectboxMenu.d.ts +12 -0
  99. package/Selectbox/SelectboxMenu.js +50 -0
  100. package/Selectbox/index.d.ts +5 -5
  101. package/Selectbox/index.js +115 -97
  102. package/Surface/index.js +1 -1
  103. package/Swiper/Swiper.d.ts +44 -0
  104. package/Swiper/Swiper.js +139 -0
  105. package/Swiper/SwiperContainer.d.ts +7 -0
  106. package/Swiper/SwiperContainer.js +14 -0
  107. package/Swiper/SwiperSlide.d.ts +7 -0
  108. package/Swiper/SwiperSlide.js +55 -0
  109. package/Swiper/SwiperTrack.d.ts +14 -0
  110. package/Swiper/SwiperTrack.js +58 -0
  111. package/Swiper/SwiperWrapper.d.ts +14 -0
  112. package/Swiper/SwiperWrapper.js +60 -0
  113. package/Swiper/index.d.ts +7 -0
  114. package/Swiper/index.js +26 -0
  115. package/Switch/SwitchDot.js +1 -1
  116. package/Switch/SwitchTrack.js +1 -1
  117. package/TextField/TextFieldContainer.d.ts +2 -2
  118. package/TextField/TextFieldContainer.js +5 -3
  119. package/TextField/TextFieldErrorText.d.ts +2 -2
  120. package/TextField/TextFieldErrorText.js +5 -4
  121. package/TextField/TextFieldIconWrapper.d.ts +3 -3
  122. package/TextField/TextFieldIconWrapper.js +5 -3
  123. package/TextField/TextFieldInput.d.ts +2 -2
  124. package/TextField/TextFieldInput.js +6 -4
  125. package/TextField/TextFieldInputWrapper.d.ts +2 -2
  126. package/TextField/TextFieldInputWrapper.js +5 -3
  127. package/TextField/TextFieldLabel.d.ts +2 -2
  128. package/TextField/TextFieldLabel.js +5 -3
  129. package/TextField/TextFieldLabelAsterisk.d.ts +2 -2
  130. package/TextField/TextFieldLabelAsterisk.js +5 -3
  131. package/TextField/index.d.ts +8 -1
  132. package/TextField/index.js +49 -22
  133. package/ThemeProvider/createTheme.js +2 -2
  134. package/package.json +1 -1
  135. package/Calendar/reducer.d.ts +0 -17
  136. package/Calendar/reducer.js +0 -50
  137. /package/Calendar/{IconChevronRight.d.ts → CalendarIconNext.d.ts} +0 -0
  138. /package/Calendar/{IconChevronRight.js → CalendarIconNext.js} +0 -0
@@ -3,14 +3,60 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.usePopper = void 0;
6
+ exports.usePopper = exports.findScrollableAncestor = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
- const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'absolute', autoFlip = true, offset = 0, viewportMargin = 30, isOpen = false, }) => {
8
+ /**
9
+ * Finds the closest scrollable ancestor of an element.
10
+ * A scrollable element is one that has overflow set to auto, scroll, or overlay
11
+ * and has content that overflows, allowing scrolling.
12
+ *
13
+ * @param element - The element to start searching from
14
+ * @param includeHidden - Whether to consider elements with overflow: hidden
15
+ * @returns The closest scrollable ancestor or window if none found
16
+ */
17
+ const findScrollableAncestor = (element, includeHidden = false) => {
18
+ if (!element) {
19
+ return null;
20
+ }
21
+ // Helper to check if an element is scrollable
22
+ const isScrollable = (el) => {
23
+ const style = window.getComputedStyle(el);
24
+ const overflow = style.overflow + style.overflowY + style.overflowX;
25
+ // Check if element has overflow that enables scrolling
26
+ const hasOverflow = overflow.includes('auto') ||
27
+ overflow.includes('scroll') ||
28
+ (includeHidden && overflow.includes('hidden'));
29
+ if (!hasOverflow) {
30
+ return false;
31
+ }
32
+ // Check if element actually has scrollable content
33
+ return el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth;
34
+ };
35
+ // Start from parent (skip the element itself)
36
+ let parent = element.parentElement;
37
+ while (parent) {
38
+ if (isScrollable(parent)) {
39
+ return parent;
40
+ }
41
+ parent = parent.parentElement;
42
+ }
43
+ // If no scrollable ancestor found, return window (which is scrollable)
44
+ return window;
45
+ };
46
+ exports.findScrollableAncestor = findScrollableAncestor;
47
+ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'fixed', autoFlip = true, offset = 0, viewportMargin = 10, isOpen = false, }) => {
9
48
  const [actualPlacement, setActualPlacement] = react_1.default.useState(anchorPos);
10
49
  const [style, setStyle] = react_1.default.useState(null);
11
50
  const [isVisible, setIsVisible] = react_1.default.useState(false);
12
51
  const popperRef = react_1.default.useRef(null);
13
- // Синхронизация actualPlacement с anchorPos
52
+ // Finding the nearest scrollable ancestor
53
+ const scrollableAncestor = react_1.default.useMemo(() => {
54
+ if (!anchorElement || typeof window === 'undefined') {
55
+ return null;
56
+ }
57
+ return (0, exports.findScrollableAncestor)(anchorElement);
58
+ }, [anchorElement]);
59
+ // Synchronizing actualPlacement with anchorPos
14
60
  react_1.default.useEffect(() => {
15
61
  if (actualPlacement !== anchorPos) {
16
62
  setActualPlacement(anchorPos);
@@ -21,11 +67,16 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
21
67
  const [direction, modifier = ''] = place.split('-');
22
68
  let left = anchorRect.left;
23
69
  let top = anchorRect.top;
24
- // Вычисляем позицию относительно viewport
70
+ let width = undefined;
71
+ // Calculating the position relative to the viewport
25
72
  switch (direction) {
26
73
  case 'top':
27
74
  top = anchorRect.top - popperRect.height - offset;
28
- if (modifier === 'left' || modifier === 'start') {
75
+ if (modifier === 'fill') {
76
+ left = anchorRect.left;
77
+ width = anchorRect.width;
78
+ }
79
+ else if (modifier === 'left' || modifier === 'start') {
29
80
  left = anchorRect.left;
30
81
  }
31
82
  else if (modifier === 'right' || modifier === 'end') {
@@ -37,7 +88,11 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
37
88
  break;
38
89
  case 'bottom':
39
90
  top = anchorRect.bottom + offset;
40
- if (modifier === 'left' || modifier === 'start') {
91
+ if (modifier === 'fill') {
92
+ left = anchorRect.left;
93
+ width = anchorRect.width;
94
+ }
95
+ else if (modifier === 'left' || modifier === 'start') {
41
96
  left = anchorRect.left;
42
97
  }
43
98
  else if (modifier === 'right' || modifier === 'end') {
@@ -75,31 +130,82 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
75
130
  left = anchorRect.left + (anchorRect.width - popperRect.width) / 2;
76
131
  top = anchorRect.bottom + offset;
77
132
  }
78
- // Возвращаем viewport координаты для проверки или финальные координаты
133
+ // Returning the viewport coordinates for verification or the final coordinates
79
134
  if (forViewportCheck) {
80
- return { left, top };
135
+ return { left, top, width };
81
136
  }
82
- // Для финального позиционирования учитываем стратегию
137
+ // For the final positioning, we take into account the strategy
83
138
  if (positionStrategy === 'absolute') {
84
139
  return {
85
140
  left: left + window.scrollX,
86
141
  top: top + window.scrollY,
142
+ width,
87
143
  };
88
144
  }
89
- return { left, top };
145
+ return { left, top, width };
90
146
  }, [offset, positionStrategy]);
147
+ // const clampPopperToContainer = React.useCallback(
148
+ // (style: { left: number; top: number; width?: number }, _popperRect: DOMRect) => style,
149
+ // [],
150
+ // );
151
+ const clampPopperToContainer = react_1.default.useCallback((style, popperRect) => {
152
+ // Если нет scrollableAncestor, используем document.documentElement как fallback
153
+ const container = scrollableAncestor instanceof HTMLElement ? scrollableAncestor : document.documentElement;
154
+ const containerRect = container.getBoundingClientRect();
155
+ // Базовые границы контейнера с учетом отступа
156
+ let minLeft = containerRect.left + viewportMargin;
157
+ let maxLeft = containerRect.right - popperRect.width - viewportMargin;
158
+ let minTop = containerRect.top + viewportMargin;
159
+ let maxTop = containerRect.bottom - popperRect.height - viewportMargin;
160
+ // Для fixed позиционирования добавляем ограничения viewport
161
+ if (positionStrategy === 'fixed') {
162
+ // Границы viewport с учетом отступа
163
+ const viewportMinLeft = viewportMargin;
164
+ const viewportMaxLeft = window.innerWidth - popperRect.width - viewportMargin;
165
+ const viewportMinTop = viewportMargin;
166
+ const viewportMaxTop = window.innerHeight - popperRect.height - viewportMargin;
167
+ // Берем пересечение границ контейнера и viewport
168
+ minLeft = Math.max(minLeft, viewportMinLeft);
169
+ maxLeft = Math.min(maxLeft, viewportMaxLeft);
170
+ minTop = Math.max(minTop, viewportMinTop);
171
+ maxTop = Math.min(maxTop, viewportMaxTop);
172
+ }
173
+ // Убеждаемся, что границы валидны (min не больше max)
174
+ const validMinLeft = Math.min(minLeft, maxLeft);
175
+ const validMaxLeft = Math.max(minLeft, maxLeft);
176
+ const validMinTop = Math.min(minTop, maxTop);
177
+ const validMaxTop = Math.max(minTop, maxTop);
178
+ // Ограничиваем позицию
179
+ const clampedLeft = Math.max(validMinLeft, Math.min(style.left, validMaxLeft));
180
+ const clampedTop = Math.max(validMinTop, Math.min(style.top, validMaxTop));
181
+ return {
182
+ left: clampedLeft,
183
+ top: clampedTop,
184
+ width: style.width,
185
+ };
186
+ }, [positionStrategy, scrollableAncestor, viewportMargin]);
91
187
  const checkIfViewportFits = react_1.default.useCallback((style, popperRect) => {
92
188
  const left = style.left;
93
189
  const top = style.top;
94
190
  const right = left + popperRect.width;
95
191
  const bottom = top + popperRect.height;
192
+ if (positionStrategy === 'absolute' && scrollableAncestor instanceof HTMLElement) {
193
+ const containerRect = scrollableAncestor.getBoundingClientRect();
194
+ const relativeLeft = left - containerRect.left;
195
+ const relativeTop = top - containerRect.top;
196
+ const relativeRight = relativeLeft + popperRect.width;
197
+ const relativeBottom = relativeTop + popperRect.height;
198
+ const isWithinHorizontal = relativeLeft >= viewportMargin && relativeRight <= containerRect.width - viewportMargin;
199
+ const isWithinVertical = relativeTop >= viewportMargin && relativeBottom <= containerRect.height - viewportMargin;
200
+ return isWithinHorizontal && isWithinVertical;
201
+ }
96
202
  const viewportWidth = window.innerWidth;
97
203
  const viewportHeight = window.innerHeight;
98
204
  const isWithinHorizontal = left >= viewportMargin && right <= viewportWidth - viewportMargin;
99
205
  const isWithinVertical = top >= viewportMargin && bottom <= viewportHeight - viewportMargin;
100
206
  const hasValidPosition = left >= 0 && top >= 0;
101
207
  return hasValidPosition && isWithinHorizontal && isWithinVertical;
102
- }, [viewportMargin]);
208
+ }, [viewportMargin, scrollableAncestor, positionStrategy]);
103
209
  const placementsOrder = react_1.default.useMemo(() => {
104
210
  const basePlacements = {
105
211
  top: ['top', 'top-left', 'top-right', 'top-start', 'top-end'],
@@ -116,7 +222,7 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
116
222
  const order = {};
117
223
  allPlacements.forEach(placement => {
118
224
  const [direction] = placement.split('-');
119
- // Приоритет: то же направление > противоположное > остальные
225
+ // Priority: same direction > opposite > the rest
120
226
  const opposite = direction === 'top'
121
227
  ? 'bottom'
122
228
  : direction === 'bottom'
@@ -132,10 +238,11 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
132
238
  ...(direction !== 'left' && direction !== 'right' ? basePlacements.right : []),
133
239
  ];
134
240
  });
241
+ // console.log(order);
135
242
  return order;
136
243
  }, []);
137
244
  const getPreferredPlacements = react_1.default.useCallback((preferredPlacement, anchorRect, popperRect) => {
138
- // Обработка auto placement
245
+ // Check the auto placements
139
246
  if (preferredPlacement.startsWith('auto')) {
140
247
  const [, preferredDirection = 'bottom'] = preferredPlacement.split('-');
141
248
  const allPlacements = [
@@ -156,7 +263,7 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
156
263
  'right-top',
157
264
  'right-bottom',
158
265
  ];
159
- // Сортируем: сначала указанное направление
266
+ // Sorting placements
160
267
  const sortedPlacements = [...allPlacements].sort((a, b) => {
161
268
  const aDir = a.split('-')[0];
162
269
  const bDir = b.split('-')[0];
@@ -184,7 +291,7 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
184
291
  return {
185
292
  found: true,
186
293
  placement: place,
187
- style: finalStyle,
294
+ style: clampPopperToContainer(finalStyle, popperRect),
188
295
  };
189
296
  }
190
297
  }
@@ -199,9 +306,50 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
199
306
  return {
200
307
  found: false,
201
308
  placement: preferredPlacement,
202
- style: finalStyle,
309
+ style: clampPopperToContainer(finalStyle, popperRect),
203
310
  };
204
311
  }
312
+ /**
313
+ * bottom-fill, top-fill
314
+ */
315
+ if (preferredPlacement.endsWith('fill')) {
316
+ const [preferredDirection = 'bottom'] = preferredPlacement.split('-');
317
+ const allPlacements = ['top-fill', 'bottom-fill'];
318
+ const sortedPlacements = [...allPlacements].sort((a, b) => {
319
+ const aDir = a.split('-')[0];
320
+ const bDir = b.split('-')[0];
321
+ if (aDir === preferredDirection && bDir !== preferredDirection)
322
+ return -1;
323
+ if (aDir !== preferredDirection && bDir === preferredDirection)
324
+ return 1;
325
+ return 0;
326
+ });
327
+ for (const place of sortedPlacements) {
328
+ const viewportStyle = getPositionStyle({
329
+ place,
330
+ anchorRect,
331
+ popperRect,
332
+ forViewportCheck: true,
333
+ });
334
+ const fits = checkIfViewportFits(viewportStyle, popperRect);
335
+ if (fits) {
336
+ const finalStyle = getPositionStyle({
337
+ place,
338
+ anchorRect,
339
+ popperRect,
340
+ forViewportCheck: false,
341
+ });
342
+ return {
343
+ found: true,
344
+ placement: place,
345
+ style: clampPopperToContainer(finalStyle, popperRect),
346
+ };
347
+ }
348
+ }
349
+ }
350
+ /**
351
+ * Other
352
+ */
205
353
  const order = placementsOrder[preferredPlacement] || placementsOrder.bottom;
206
354
  for (const place of order) {
207
355
  const viewportStyle = getPositionStyle({
@@ -221,7 +369,7 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
221
369
  return {
222
370
  found: true,
223
371
  placement: place,
224
- style: finalStyle,
372
+ style: clampPopperToContainer(finalStyle, popperRect),
225
373
  };
226
374
  }
227
375
  }
@@ -234,73 +382,52 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
234
382
  return {
235
383
  found: false,
236
384
  placement: preferredPlacement,
237
- style: finalStyle,
385
+ style: clampPopperToContainer(finalStyle, popperRect),
238
386
  };
239
- }, [autoFlip, getPositionStyle, checkIfViewportFits, placementsOrder]);
387
+ }, [autoFlip, getPositionStyle, checkIfViewportFits, clampPopperToContainer, placementsOrder]);
240
388
  const calculatePosition = react_1.default.useCallback(() => {
241
389
  if (!anchorElement || !popperRef.current) {
242
390
  return;
243
391
  }
244
392
  const anchorRect = anchorElement.getBoundingClientRect();
245
393
  const popperRect = popperRef.current.getBoundingClientRect();
246
- const placements = getPreferredPlacements(actualPlacement, anchorRect, popperRect);
247
- if (placements.found) {
248
- setActualPlacement(placements.placement);
249
- setStyle(placements.style);
394
+ const { style, placement, found } = getPreferredPlacements(anchorPos, anchorRect, popperRect);
395
+ if (found) {
396
+ setActualPlacement(placement);
250
397
  }
251
- else {
252
- setStyle(placements.style);
253
- }
254
- }, [actualPlacement, anchorElement, getPreferredPlacements]);
255
- const getTransformOrigin = react_1.default.useCallback((placement) => {
256
- if (placement.startsWith('top'))
257
- return 'bottom center';
258
- if (placement.startsWith('bottom'))
259
- return 'top center';
260
- if (placement.startsWith('left'))
261
- return 'right center';
262
- if (placement.startsWith('right'))
263
- return 'left center';
264
- return 'top center';
265
- }, []);
266
- // Очистка при закрытии
398
+ setStyle(style);
399
+ }, [anchorElement, anchorPos, getPreferredPlacements]);
400
+ // const getTransformOrigin = React.useCallback((placement: AnchorPos): string => {
401
+ // if (placement.startsWith('top')) return 'bottom center';
402
+ // if (placement.startsWith('bottom')) return 'top center';
403
+ // if (placement.startsWith('left')) return 'right center';
404
+ // if (placement.startsWith('right')) return 'left center';
405
+ //
406
+ // return 'top center';
407
+ // }, []);
408
+ /**
409
+ * Open / Close popper logic
410
+ */
267
411
  react_1.default.useEffect(() => {
268
412
  if (!isOpen) {
269
413
  setIsVisible(false);
270
414
  setStyle(null);
271
415
  }
272
416
  else if (anchorElement && isOpen && popperRef.current) {
273
- const anchorRect = anchorElement.getBoundingClientRect();
274
- const popperRect = popperRef.current.getBoundingClientRect();
275
- const placements = getPreferredPlacements(actualPlacement, anchorRect, popperRect);
276
- if (placements.found) {
277
- setActualPlacement(placements.placement);
278
- setStyle(placements.style);
279
- }
280
- else {
281
- setStyle(placements.style);
282
- }
417
+ calculatePosition();
283
418
  setIsVisible(true);
284
419
  }
285
- }, [actualPlacement, anchorElement, getPreferredPlacements, isOpen]);
286
- // Отслеживание скролла
420
+ }, [actualPlacement, anchorElement, getPreferredPlacements, isOpen, calculatePosition]);
421
+ /**
422
+ * The anchor element changes
423
+ */
287
424
  react_1.default.useEffect(() => {
288
- const handleEvent = () => {
289
- if (isOpen) {
290
- calculatePosition();
291
- }
292
- };
293
- window.addEventListener('scroll', handleEvent);
294
- return () => {
295
- window.removeEventListener('scroll', handleEvent);
296
- };
297
- }, [calculatePosition, isOpen]);
298
- // Отслеживание мутаций anchor элемента
299
- react_1.default.useEffect(() => {
300
- if (!isOpen || !anchorElement)
425
+ if (!isOpen || !anchorElement) {
301
426
  return;
427
+ }
302
428
  const observer = new MutationObserver(() => {
303
429
  window.requestAnimationFrame(() => {
430
+ // console.log('The anchor element was change');
304
431
  calculatePosition();
305
432
  });
306
433
  });
@@ -314,13 +441,61 @@ const usePopper = ({ anchorElement, anchorPos = 'auto', positionStrategy = 'abso
314
441
  observer.disconnect();
315
442
  };
316
443
  }, [isOpen, anchorElement, calculatePosition]);
444
+ /**
445
+ * On scroll - recalculate position
446
+ */
447
+ react_1.default.useEffect(() => {
448
+ if (!isOpen || !scrollableAncestor) {
449
+ return;
450
+ }
451
+ const handleScroll = () => {
452
+ window.requestAnimationFrame(() => {
453
+ calculatePosition();
454
+ });
455
+ };
456
+ // parent scroll listener
457
+ if (scrollableAncestor instanceof HTMLElement) {
458
+ scrollableAncestor.addEventListener('scroll', handleScroll, { passive: true });
459
+ }
460
+ else if (scrollableAncestor === window) {
461
+ window.addEventListener('scroll', handleScroll, { passive: true });
462
+ }
463
+ // eslint-disable-next-line consistent-return
464
+ return () => {
465
+ if (scrollableAncestor instanceof HTMLElement) {
466
+ scrollableAncestor.removeEventListener('scroll', handleScroll);
467
+ }
468
+ else if (scrollableAncestor === window) {
469
+ window.removeEventListener('scroll', handleScroll);
470
+ }
471
+ };
472
+ }, [isOpen, scrollableAncestor, calculatePosition]);
473
+ /**
474
+ * On window resize - recalculate position
475
+ */
476
+ react_1.default.useEffect(() => {
477
+ if (!isOpen) {
478
+ return;
479
+ }
480
+ const handleResize = () => {
481
+ window.requestAnimationFrame(() => {
482
+ calculatePosition();
483
+ });
484
+ };
485
+ // parent scroll listener
486
+ window.addEventListener('resize', handleResize);
487
+ // eslint-disable-next-line consistent-return
488
+ return () => {
489
+ window.removeEventListener('resize', handleResize);
490
+ };
491
+ }, [calculatePosition, isOpen]);
317
492
  return {
318
493
  actualPlacement,
319
494
  style,
320
495
  popperRef,
321
496
  isVisible,
497
+ scrollableAncestor,
322
498
  calculatePosition,
323
- getTransformOrigin,
324
499
  setActualPlacement,
325
500
  setStyle,
326
501
  setIsVisible,
@@ -1,11 +1,16 @@
1
1
  import * as React from 'react';
2
2
  import { ButtonProps } from '../Button';
3
+ import { AnchorPos } from '../Menu';
3
4
  export type SelectboxButtonProps = Omit<ButtonProps, 'type'> & {
4
5
  readonly fullWidth?: boolean;
5
6
  readonly error?: boolean;
7
+ readonly anchorPos?: AnchorPos;
8
+ readonly isOpen?: boolean;
6
9
  };
7
10
  declare const _default: React.ForwardRefExoticComponent<Omit<ButtonProps, "type"> & {
8
11
  readonly fullWidth?: boolean | undefined;
9
12
  readonly error?: boolean | undefined;
13
+ readonly anchorPos?: import("../Popper").AnchorPos | undefined;
14
+ readonly isOpen?: boolean | undefined;
10
15
  } & React.RefAttributes<HTMLButtonElement>>;
11
16
  export default _default;
@@ -41,25 +41,65 @@ const React = __importStar(require("react"));
41
41
  const styled_1 = __importDefault(require("@emotion/styled"));
42
42
  const Button_1 = __importDefault(require("../Button"));
43
43
  const ButtonTextWrapper_1 = __importDefault(require("../Button/ButtonTextWrapper"));
44
+ const react_1 = require("@emotion/react");
44
45
  const StyledSelectboxButton = (0, styled_1.default)(Button_1.default) `
45
46
  flex: 1;
46
47
  padding: 1em 1.2em;
47
48
  font-size: 1em;
48
49
  background: none;
49
- border-radius: inherit;
50
- margin: 0;
51
- border: 0;
52
50
  width: 100%;
51
+ border-width: 1px;
52
+ border-style: solid;
53
+ box-shadow: none;
54
+ &:hover {
55
+ box-shadow: none;
56
+ }
57
+ border-color: ${({ theme }) => theme.isDark
58
+ ? theme.color.textPrimary.darken(100).toString()
59
+ : theme.color.textPrimary.lighten(150).toString()};
60
+ ${props => props.$error &&
61
+ (0, react_1.css) `
62
+ border-color: ${props.theme.color.error.toString()};
63
+ color: ${props.theme.color.error.toString()};
64
+ &:focus {
65
+ border-color: ${props.theme.color.error.lighten(0.6).toString()};
66
+ }
67
+ `}
68
+ transition: none;
69
+ ${({ theme, $isOpen, $anchorPos }) => $isOpen &&
70
+ $anchorPos &&
71
+ ['bottom-fill', 'top-fill'].includes($anchorPos) &&
72
+ (0, react_1.css) `
73
+ border-width: 1px;
74
+ border-style: solid;
75
+ border-color: ${theme.isDark
76
+ ? theme.color.textPrimary.darken(100).toString()
77
+ : theme.color.textPrimary.lighten(150).toString()};
78
+ `};
79
+
80
+ ${({ $anchorPos, $isOpen }) => $isOpen &&
81
+ $anchorPos === 'bottom-fill' &&
82
+ (0, react_1.css) `
83
+ border-bottom-left-radius: 0;
84
+ border-bottom-right-radius: 0;
85
+ border-bottom: 0;
86
+ `};
87
+
88
+ ${({ $anchorPos, $isOpen }) => $isOpen &&
89
+ $anchorPos === 'top-fill' &&
90
+ (0, react_1.css) `
91
+ border-top-left-radius: 0;
92
+ border-top-right-radius: 0;
93
+ border-top: 0;
94
+ `};
53
95
  `;
54
96
  const StyledButtonTextWrapper = (0, styled_1.default)(ButtonTextWrapper_1.default) `
55
97
  flex: 1;
56
98
  `;
57
99
  const SelectboxButton = (props, ref) => {
58
- const { children, fullWidth, error } = props, nativeProps = __rest(props, ["children", "fullWidth", "error"]);
59
- return (React.createElement(StyledSelectboxButton, Object.assign({ "$fullWidth": fullWidth, "$error": error }, nativeProps, { overrides: {
60
- TextWrapper: React.forwardRef(function SelectboxButtonTextWrapper(p, r) {
61
- return React.createElement(StyledButtonTextWrapper, Object.assign({}, p, { ref: r }));
62
- }),
100
+ const { children, fullWidth, error, anchorPos, isOpen } = props, nativeProps = __rest(props, ["children", "fullWidth", "error", "anchorPos", "isOpen"]);
101
+ return (React.createElement(StyledSelectboxButton, Object.assign({ "$fullWidth": fullWidth, "$error": error }, nativeProps, { "$isOpen": isOpen, "$anchorPos": anchorPos, overrides: {
102
+ TextWrapper: StyledButtonTextWrapper,
63
103
  }, ref: ref }), children));
64
104
  };
65
105
  exports.default = React.forwardRef(SelectboxButton);
@@ -2,9 +2,11 @@ import * as React from 'react';
2
2
  export type SelectboxButtonWrapperProps = React.HTMLAttributes<HTMLDivElement> & {
3
3
  readonly fullWidth?: boolean;
4
4
  readonly error?: boolean;
5
+ readonly isOpen: boolean;
5
6
  };
6
7
  declare const _default: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
7
8
  readonly fullWidth?: boolean | undefined;
8
9
  readonly error?: boolean | undefined;
10
+ readonly isOpen: boolean;
9
11
  } & React.RefAttributes<HTMLDivElement>>;
10
12
  export default _default;
@@ -39,7 +39,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  const React = __importStar(require("react"));
41
41
  const styled_1 = __importDefault(require("@emotion/styled"));
42
- const react_1 = require("@emotion/react");
43
42
  const Wrapper = styled_1.default.div `
44
43
  min-width: 16em;
45
44
  font-size: 0.9em;
@@ -48,28 +47,13 @@ const Wrapper = styled_1.default.div `
48
47
  display: flex;
49
48
  align-items: stretch;
50
49
  border-radius: ${({ theme }) => theme.shape.radiusFactor * 2}em;
51
- border: 1px solid transparent;
52
-
53
- border: 1px solid;
54
50
  outline: 1px solid transparent;
55
-
56
- border-color: ${({ theme }) => theme.isDark
57
- ? theme.color.textPrimary.darken(100).toString()
58
- : theme.color.textPrimary.lighten(150).toString()};
59
51
  width: ${({ $fullWidth }) => ($fullWidth ? '100%' : 'auto')};
60
-
61
52
  background-color: ${({ theme }) => theme.color.surface.toString()};
62
53
  color: ${({ theme }) => theme.color.textPrimary.toString()};
63
54
  transition: all 180ms ease-out 0s;
64
55
 
65
- ${props => props.$error &&
66
- (0, react_1.css) `
67
- border-color: ${props.theme.color.error.toString()};
68
- color: ${props.theme.color.error.toString()};
69
- &:focus {
70
- border-color: ${props.theme.color.error.lighten(0.6).toString()};
71
- }
72
- `}
56
+
73
57
  `;
74
58
  const SelectboxButtonWrapper = (props, ref) => {
75
59
  const { fullWidth, children, error } = props, nativeProps = __rest(props, ["fullWidth", "children", "error"]);
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import { MenuProps, MenuRef } from '../Menu';
3
+ import { AnchorPos } from '../Popper';
4
+ import { OnChange } from 'Selectbox';
5
+ export interface SelectbosMenuProps<T, Multiple extends boolean | undefined = undefined> extends MenuProps<T, Multiple> {
6
+ readonly actualPlacement: AnchorPos;
7
+ readonly onChange?: OnChange<T, Multiple>;
8
+ }
9
+ declare const _default: <T, Multiple extends boolean | undefined = undefined>(props: SelectbosMenuProps<T, Multiple> & {
10
+ ref?: React.Ref<MenuRef> | undefined;
11
+ }) => JSX.Element;
12
+ export default _default;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const React = __importStar(require("react"));
30
+ const Menu_1 = __importDefault(require("../Menu"));
31
+ const OverrideMenuList_1 = __importDefault(require("../Autocomplete/OverrideMenuList"));
32
+ const SelectboxMenu = React.forwardRef((props, ref) => {
33
+ const { actualPlacement, multiple, items, value, isOpen, anchorElement, getOptionSelected, onChange, onRequestClose, children, onAnchorPosChanged, } = props;
34
+ const handleSelectItem = React.useCallback((item) => {
35
+ onChange === null || onChange === void 0 ? void 0 : onChange(item);
36
+ }, [onChange]);
37
+ const handleRequestClose = React.useCallback(evt => {
38
+ if ((evt === null || evt === void 0 ? void 0 : evt.target) !== anchorElement) {
39
+ if (onRequestClose) {
40
+ onRequestClose(evt);
41
+ }
42
+ anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.focus();
43
+ }
44
+ }, [onRequestClose, anchorElement]);
45
+ return (React.createElement(Menu_1.default, { ref: ref, offset: 0, anchorPos: actualPlacement, onAnchorPosChanged: onAnchorPosChanged, multiple: multiple, items: items, value: value, isOpen: isOpen && items.length > 0, autofocus: true, anchorElement: anchorElement, closeOutsideClick: false, getOptionSelected: getOptionSelected, onSelectItem: handleSelectItem, overrides: {
46
+ List: OverrideMenuList_1.default,
47
+ }, closeOnSelect: !multiple, onRequestClose: handleRequestClose }, ({ index, item }, itemProps) => children({ index, item: item }, itemProps)));
48
+ });
49
+ SelectboxMenu.displayName = 'SelectboxMenu';
50
+ exports.default = React.memo(SelectboxMenu);