@via-profit/ui-kit 0.1.8 → 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 (157) hide show
  1. package/Accordion/index.d.ts +4 -4
  2. package/Autocomplete/AutocompleteContainer.d.ts +34 -7
  3. package/Autocomplete/AutocompleteContainer.js +28 -24
  4. package/Autocomplete/AutocompleteItem.d.ts +1 -1
  5. package/Autocomplete/AutocompleteTextField.d.ts +3 -0
  6. package/Autocomplete/AutocompleteTextField.js +63 -1
  7. package/Autocomplete/OverrideMenuList.d.ts +4 -0
  8. package/Autocomplete/OverrideMenuList.js +39 -0
  9. package/Autocomplete/context.d.ts +9 -1
  10. package/Autocomplete/context.js +15 -1
  11. package/Autocomplete/index.js +9 -10
  12. package/Autocomplete2/Autocomplete2.d.ts +12 -0
  13. package/Autocomplete2/Autocomplete2.js +83 -0
  14. package/Autocomplete2/Autocomplete2TextField.d.ts +7 -0
  15. package/Autocomplete2/Autocomplete2TextField.js +39 -0
  16. package/Autocomplete2/index.d.ts +2 -0
  17. package/Autocomplete2/index.js +7 -0
  18. package/Avatar/AvatarBase.d.ts +4 -4
  19. package/Avatar/index.js +1 -1
  20. package/Badge/BadgeBase.d.ts +4 -4
  21. package/Badge/BadgeDeleteButton.js +6 -6
  22. package/Badge/BadgeOutlined.js +2 -2
  23. package/Badge/BadgeStandard.js +1 -1
  24. package/Button/ButtonBase.d.ts +3 -3
  25. package/Button/ButtonOutlined.js +1 -1
  26. package/Button/ButtonPlain.js +1 -1
  27. package/Button/ButtonStandard.js +4 -11
  28. package/Calendar/CalendarCell.js +22 -4
  29. package/Calendar/CalendarComponent.d.ts +226 -0
  30. package/Calendar/CalendarComponent.js +572 -0
  31. package/Calendar/CalendarControlButton.js +16 -9
  32. package/Calendar/CalendarDayBadge.js +1 -1
  33. package/Calendar/CalendarEmptyCell.d.ts +2 -2
  34. package/Calendar/CalendarEmptyCell.js +8 -3
  35. package/Calendar/CalendarHeading.js +2 -1
  36. package/Calendar/CalendarMonthCell.js +0 -5
  37. package/Calendar/CalendarSubheading.js +3 -2
  38. package/Calendar/CalendarWeekDayCell.d.ts +10 -0
  39. package/Calendar/CalendarWeekDayCell.js +32 -0
  40. package/Calendar/CalendarWeekDayWeekNumber.d.ts +7 -0
  41. package/Calendar/CalendarWeekDayWeekNumber.js +38 -0
  42. package/Calendar/CalendarWeekRow.d.ts +2 -2
  43. package/Calendar/CalendarWeekRow.js +2 -1
  44. package/Calendar/CalendarWeekRowButton.d.ts +13 -0
  45. package/Calendar/CalendarWeekRowButton.js +31 -0
  46. package/Calendar/CalendarYearCell.js +5 -5
  47. package/Calendar/CalendarYearsSelector.d.ts +39 -1
  48. package/Calendar/CalendarYearsSelector.js +52 -16
  49. package/Calendar/IconChevronDown.d.ts +3 -0
  50. package/Calendar/IconChevronDown.js +31 -0
  51. package/Calendar/index.d.ts +7 -220
  52. package/Calendar/index.js +19 -274
  53. package/Calendar/use-calendar.d.ts +1 -1
  54. package/Calendar/use-calendar.js +102 -155
  55. package/Color/Color.d.ts +138 -0
  56. package/Color/Color.js +324 -0
  57. package/Color/ColorGenerator.d.ts +29 -0
  58. package/Color/ColorGenerator.js +141 -0
  59. package/Color/ColorParser.d.ts +20 -0
  60. package/Color/ColorParser.js +284 -0
  61. package/Color/index.d.ts +4 -122
  62. package/Color/index.js +18 -473
  63. package/DatePicker/index.d.ts +2 -17
  64. package/DatePicker/index.js +47 -61
  65. package/DatePicker/useDatePickerFormat.js +5 -5
  66. package/ErrorBoundary/Fallback.d.ts +3 -0
  67. package/ErrorBoundary/Fallback.js +51 -0
  68. package/ErrorBoundary/RenderError.d.ts +6 -0
  69. package/ErrorBoundary/RenderError.js +80 -0
  70. package/ErrorBoundary/index.d.ts +7 -11
  71. package/ErrorBoundary/index.js +4 -51
  72. package/Highlighted/index.d.ts +3 -3
  73. package/LoadingIndicator/Spinner.d.ts +5 -3
  74. package/LoadingIndicator/Spinner.js +61 -32
  75. package/LoadingIndicator/index.d.ts +3 -3
  76. package/LoadingIndicator/index.js +15 -2
  77. package/MaskedField/index.d.ts +1 -1
  78. package/MaskedField/index.js +11 -10
  79. package/Menu/AutoHeightWrapper.d.ts +6 -0
  80. package/Menu/AutoHeightWrapper.js +43 -0
  81. package/Menu/MeasuredItem.d.ts +10 -0
  82. package/Menu/MeasuredItem.js +51 -0
  83. package/Menu/MenuContainer.d.ts +53 -20
  84. package/Menu/MenuContainer.js +18 -11
  85. package/Menu/MenuItem.d.ts +24 -7
  86. package/Menu/MenuItem.js +13 -6
  87. package/Menu/MenuList.d.ts +4 -2
  88. package/Menu/MenuList.js +26 -6
  89. package/Menu/VirtualizedItem.d.ts +22 -0
  90. package/Menu/VirtualizedItem.js +69 -0
  91. package/Menu/VirtualizedList.d.ts +18 -0
  92. package/Menu/VirtualizedList.js +28 -0
  93. package/Menu/VirtualizedListComponent.d.ts +20 -0
  94. package/Menu/VirtualizedListComponent.js +305 -0
  95. package/Menu/index.d.ts +3 -1
  96. package/Menu/index.js +4 -7
  97. package/Modal/BaseModal/RenderModal.d.ts +3 -3
  98. package/Modal/BaseModal/index.d.ts +3 -3
  99. package/Modal/ConfirmBox/index.d.ts +5 -5
  100. package/Modal/Drawer/index.d.ts +5 -5
  101. package/Modal/MessageBox/index.d.ts +5 -5
  102. package/Popper/Popper.d.ts +148 -0
  103. package/Popper/Popper.js +113 -0
  104. package/Popper/PopperContainer.d.ts +2 -0
  105. package/Popper/PopperContainer.js +3 -6
  106. package/Popper/index.d.ts +5 -22
  107. package/Popper/index.js +18 -264
  108. package/Popper/usePopprer.d.ts +110 -0
  109. package/Popper/usePopprer.js +505 -0
  110. package/Selectbox/SelectboxButton.d.ts +5 -0
  111. package/Selectbox/SelectboxButton.js +48 -8
  112. package/Selectbox/SelectboxButtonWrapper.d.ts +2 -0
  113. package/Selectbox/SelectboxButtonWrapper.js +1 -17
  114. package/Selectbox/SelectboxMenu.d.ts +12 -0
  115. package/Selectbox/SelectboxMenu.js +50 -0
  116. package/Selectbox/index.d.ts +13 -13
  117. package/Selectbox/index.js +115 -97
  118. package/Surface/index.d.ts +5 -5
  119. package/Surface/index.js +1 -1
  120. package/Swiper/Swiper.d.ts +44 -0
  121. package/Swiper/Swiper.js +139 -0
  122. package/Swiper/SwiperContainer.d.ts +7 -0
  123. package/Swiper/SwiperContainer.js +14 -0
  124. package/Swiper/SwiperSlide.d.ts +7 -0
  125. package/Swiper/SwiperSlide.js +55 -0
  126. package/Swiper/SwiperTrack.d.ts +14 -0
  127. package/Swiper/SwiperTrack.js +58 -0
  128. package/Swiper/SwiperWrapper.d.ts +14 -0
  129. package/Swiper/SwiperWrapper.js +60 -0
  130. package/Swiper/index.d.ts +7 -0
  131. package/Swiper/index.js +26 -0
  132. package/Switch/SwitchDot.js +1 -1
  133. package/Switch/SwitchTrack.js +1 -1
  134. package/Switch/index.d.ts +8 -8
  135. package/TextArea/index.d.ts +7 -7
  136. package/TextField/TextFieldContainer.d.ts +2 -2
  137. package/TextField/TextFieldContainer.js +5 -3
  138. package/TextField/TextFieldErrorText.d.ts +2 -2
  139. package/TextField/TextFieldErrorText.js +5 -4
  140. package/TextField/TextFieldIconWrapper.d.ts +3 -3
  141. package/TextField/TextFieldIconWrapper.js +5 -3
  142. package/TextField/TextFieldInput.d.ts +2 -2
  143. package/TextField/TextFieldInput.js +6 -4
  144. package/TextField/TextFieldInputWrapper.d.ts +2 -2
  145. package/TextField/TextFieldInputWrapper.js +5 -3
  146. package/TextField/TextFieldLabel.d.ts +2 -2
  147. package/TextField/TextFieldLabel.js +5 -3
  148. package/TextField/TextFieldLabelAsterisk.d.ts +2 -2
  149. package/TextField/TextFieldLabelAsterisk.js +5 -3
  150. package/TextField/index.d.ts +15 -8
  151. package/TextField/index.js +49 -22
  152. package/ThemeProvider/createTheme.js +2 -2
  153. package/package.json +1 -1
  154. package/Calendar/reducer.d.ts +0 -14
  155. package/Calendar/reducer.js +0 -26
  156. /package/Calendar/{IconChevronRight.d.ts → CalendarIconNext.d.ts} +0 -0
  157. /package/Calendar/{IconChevronRight.js → CalendarIconNext.js} +0 -0
@@ -0,0 +1,505 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.usePopper = exports.findScrollableAncestor = void 0;
7
+ const react_1 = __importDefault(require("react"));
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, }) => {
48
+ const [actualPlacement, setActualPlacement] = react_1.default.useState(anchorPos);
49
+ const [style, setStyle] = react_1.default.useState(null);
50
+ const [isVisible, setIsVisible] = react_1.default.useState(false);
51
+ const popperRef = react_1.default.useRef(null);
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
60
+ react_1.default.useEffect(() => {
61
+ if (actualPlacement !== anchorPos) {
62
+ setActualPlacement(anchorPos);
63
+ }
64
+ }, [actualPlacement, anchorPos]);
65
+ const getPositionStyle = react_1.default.useCallback((props) => {
66
+ const { place, anchorRect, popperRect, forViewportCheck = false } = props;
67
+ const [direction, modifier = ''] = place.split('-');
68
+ let left = anchorRect.left;
69
+ let top = anchorRect.top;
70
+ let width = undefined;
71
+ // Calculating the position relative to the viewport
72
+ switch (direction) {
73
+ case 'top':
74
+ top = anchorRect.top - popperRect.height - offset;
75
+ if (modifier === 'fill') {
76
+ left = anchorRect.left;
77
+ width = anchorRect.width;
78
+ }
79
+ else if (modifier === 'left' || modifier === 'start') {
80
+ left = anchorRect.left;
81
+ }
82
+ else if (modifier === 'right' || modifier === 'end') {
83
+ left = anchorRect.right - popperRect.width;
84
+ }
85
+ else {
86
+ left = anchorRect.left + (anchorRect.width - popperRect.width) / 2;
87
+ }
88
+ break;
89
+ case 'bottom':
90
+ top = anchorRect.bottom + offset;
91
+ if (modifier === 'fill') {
92
+ left = anchorRect.left;
93
+ width = anchorRect.width;
94
+ }
95
+ else if (modifier === 'left' || modifier === 'start') {
96
+ left = anchorRect.left;
97
+ }
98
+ else if (modifier === 'right' || modifier === 'end') {
99
+ left = anchorRect.right - popperRect.width;
100
+ }
101
+ else {
102
+ left = anchorRect.left + (anchorRect.width - popperRect.width) / 2;
103
+ }
104
+ break;
105
+ case 'left':
106
+ left = anchorRect.left - popperRect.width - offset;
107
+ if (modifier === 'top' || modifier === 'start') {
108
+ top = anchorRect.top;
109
+ }
110
+ else if (modifier === 'bottom' || modifier === 'end') {
111
+ top = anchorRect.bottom - popperRect.height;
112
+ }
113
+ else {
114
+ top = anchorRect.top + (anchorRect.height - popperRect.height) / 2;
115
+ }
116
+ break;
117
+ case 'right':
118
+ left = anchorRect.right + offset;
119
+ if (modifier === 'top' || modifier === 'start') {
120
+ top = anchorRect.top;
121
+ }
122
+ else if (modifier === 'bottom' || modifier === 'end') {
123
+ top = anchorRect.bottom - popperRect.height;
124
+ }
125
+ else {
126
+ top = anchorRect.top + (anchorRect.height - popperRect.height) / 2;
127
+ }
128
+ break;
129
+ default:
130
+ left = anchorRect.left + (anchorRect.width - popperRect.width) / 2;
131
+ top = anchorRect.bottom + offset;
132
+ }
133
+ // Returning the viewport coordinates for verification or the final coordinates
134
+ if (forViewportCheck) {
135
+ return { left, top, width };
136
+ }
137
+ // For the final positioning, we take into account the strategy
138
+ if (positionStrategy === 'absolute') {
139
+ return {
140
+ left: left + window.scrollX,
141
+ top: top + window.scrollY,
142
+ width,
143
+ };
144
+ }
145
+ return { left, top, width };
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]);
187
+ const checkIfViewportFits = react_1.default.useCallback((style, popperRect) => {
188
+ const left = style.left;
189
+ const top = style.top;
190
+ const right = left + popperRect.width;
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
+ }
202
+ const viewportWidth = window.innerWidth;
203
+ const viewportHeight = window.innerHeight;
204
+ const isWithinHorizontal = left >= viewportMargin && right <= viewportWidth - viewportMargin;
205
+ const isWithinVertical = top >= viewportMargin && bottom <= viewportHeight - viewportMargin;
206
+ const hasValidPosition = left >= 0 && top >= 0;
207
+ return hasValidPosition && isWithinHorizontal && isWithinVertical;
208
+ }, [viewportMargin, scrollableAncestor, positionStrategy]);
209
+ const placementsOrder = react_1.default.useMemo(() => {
210
+ const basePlacements = {
211
+ top: ['top', 'top-left', 'top-right', 'top-start', 'top-end'],
212
+ bottom: ['bottom', 'bottom-left', 'bottom-right', 'bottom-start', 'bottom-end'],
213
+ left: ['left', 'left-top', 'left-bottom'],
214
+ right: ['right', 'right-top', 'right-bottom'],
215
+ };
216
+ const allPlacements = [
217
+ ...basePlacements.top,
218
+ ...basePlacements.bottom,
219
+ ...basePlacements.left,
220
+ ...basePlacements.right,
221
+ ];
222
+ const order = {};
223
+ allPlacements.forEach(placement => {
224
+ const [direction] = placement.split('-');
225
+ // Priority: same direction > opposite > the rest
226
+ const opposite = direction === 'top'
227
+ ? 'bottom'
228
+ : direction === 'bottom'
229
+ ? 'top'
230
+ : direction === 'left'
231
+ ? 'right'
232
+ : 'left';
233
+ order[placement] = [
234
+ placement,
235
+ ...basePlacements[direction].filter(p => p !== placement),
236
+ ...(basePlacements[opposite] || []),
237
+ ...(direction !== 'left' && direction !== 'right' ? basePlacements.left : []),
238
+ ...(direction !== 'left' && direction !== 'right' ? basePlacements.right : []),
239
+ ];
240
+ });
241
+ // console.log(order);
242
+ return order;
243
+ }, []);
244
+ const getPreferredPlacements = react_1.default.useCallback((preferredPlacement, anchorRect, popperRect) => {
245
+ // Check the auto placements
246
+ if (preferredPlacement.startsWith('auto')) {
247
+ const [, preferredDirection = 'bottom'] = preferredPlacement.split('-');
248
+ const allPlacements = [
249
+ 'bottom',
250
+ 'bottom-start',
251
+ 'bottom-end',
252
+ 'bottom-left',
253
+ 'bottom-right',
254
+ 'top',
255
+ 'top-left',
256
+ 'top-right',
257
+ 'top-start',
258
+ 'top-end',
259
+ 'left',
260
+ 'left-top',
261
+ 'left-bottom',
262
+ 'right',
263
+ 'right-top',
264
+ 'right-bottom',
265
+ ];
266
+ // Sorting placements
267
+ const sortedPlacements = [...allPlacements].sort((a, b) => {
268
+ const aDir = a.split('-')[0];
269
+ const bDir = b.split('-')[0];
270
+ if (aDir === preferredDirection && bDir !== preferredDirection)
271
+ return -1;
272
+ if (aDir !== preferredDirection && bDir === preferredDirection)
273
+ return 1;
274
+ return 0;
275
+ });
276
+ for (const place of sortedPlacements) {
277
+ const viewportStyle = getPositionStyle({
278
+ place,
279
+ anchorRect,
280
+ popperRect,
281
+ forViewportCheck: true,
282
+ });
283
+ const fits = checkIfViewportFits(viewportStyle, popperRect);
284
+ if (fits) {
285
+ const finalStyle = getPositionStyle({
286
+ place,
287
+ anchorRect,
288
+ popperRect,
289
+ forViewportCheck: false,
290
+ });
291
+ return {
292
+ found: true,
293
+ placement: place,
294
+ style: clampPopperToContainer(finalStyle, popperRect),
295
+ };
296
+ }
297
+ }
298
+ }
299
+ if (!autoFlip) {
300
+ const finalStyle = getPositionStyle({
301
+ place: preferredPlacement,
302
+ anchorRect,
303
+ popperRect,
304
+ forViewportCheck: false,
305
+ });
306
+ return {
307
+ found: false,
308
+ placement: preferredPlacement,
309
+ style: clampPopperToContainer(finalStyle, popperRect),
310
+ };
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
+ */
353
+ const order = placementsOrder[preferredPlacement] || placementsOrder.bottom;
354
+ for (const place of order) {
355
+ const viewportStyle = getPositionStyle({
356
+ place,
357
+ anchorRect,
358
+ popperRect,
359
+ forViewportCheck: true,
360
+ });
361
+ const fits = checkIfViewportFits(viewportStyle, popperRect);
362
+ if (fits) {
363
+ const finalStyle = getPositionStyle({
364
+ place,
365
+ anchorRect,
366
+ popperRect,
367
+ forViewportCheck: false,
368
+ });
369
+ return {
370
+ found: true,
371
+ placement: place,
372
+ style: clampPopperToContainer(finalStyle, popperRect),
373
+ };
374
+ }
375
+ }
376
+ const finalStyle = getPositionStyle({
377
+ place: preferredPlacement,
378
+ anchorRect,
379
+ popperRect,
380
+ forViewportCheck: false,
381
+ });
382
+ return {
383
+ found: false,
384
+ placement: preferredPlacement,
385
+ style: clampPopperToContainer(finalStyle, popperRect),
386
+ };
387
+ }, [autoFlip, getPositionStyle, checkIfViewportFits, clampPopperToContainer, placementsOrder]);
388
+ const calculatePosition = react_1.default.useCallback(() => {
389
+ if (!anchorElement || !popperRef.current) {
390
+ return;
391
+ }
392
+ const anchorRect = anchorElement.getBoundingClientRect();
393
+ const popperRect = popperRef.current.getBoundingClientRect();
394
+ const { style, placement, found } = getPreferredPlacements(anchorPos, anchorRect, popperRect);
395
+ if (found) {
396
+ setActualPlacement(placement);
397
+ }
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
+ */
411
+ react_1.default.useEffect(() => {
412
+ if (!isOpen) {
413
+ setIsVisible(false);
414
+ setStyle(null);
415
+ }
416
+ else if (anchorElement && isOpen && popperRef.current) {
417
+ calculatePosition();
418
+ setIsVisible(true);
419
+ }
420
+ }, [actualPlacement, anchorElement, getPreferredPlacements, isOpen, calculatePosition]);
421
+ /**
422
+ * The anchor element changes
423
+ */
424
+ react_1.default.useEffect(() => {
425
+ if (!isOpen || !anchorElement) {
426
+ return;
427
+ }
428
+ const observer = new MutationObserver(() => {
429
+ window.requestAnimationFrame(() => {
430
+ // console.log('The anchor element was change');
431
+ calculatePosition();
432
+ });
433
+ });
434
+ observer.observe(anchorElement, {
435
+ attributes: true,
436
+ childList: true,
437
+ subtree: true,
438
+ });
439
+ // eslint-disable-next-line consistent-return
440
+ return () => {
441
+ observer.disconnect();
442
+ };
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]);
492
+ return {
493
+ actualPlacement,
494
+ style,
495
+ popperRef,
496
+ isVisible,
497
+ scrollableAncestor,
498
+ calculatePosition,
499
+ setActualPlacement,
500
+ setStyle,
501
+ setIsVisible,
502
+ };
503
+ };
504
+ exports.usePopper = usePopper;
505
+ exports.default = exports.usePopper;
@@ -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;