@invoicebox/ui 0.1.0-alpha.294 → 0.1.0-alpha.295

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 (26) hide show
  1. package/dist/components/common/Drawer/index.d.ts +2 -2
  2. package/dist/components/common/Drawer/index.js +83 -15
  3. package/dist/components/common/Drawer/styles.d.ts +16 -694
  4. package/dist/components/common/Drawer/styles.js +51 -35
  5. package/dist/components/common/Dropdown/index.d.ts +2 -2
  6. package/dist/components/common/Dropdown/index.js +63 -13
  7. package/dist/components/common/Dropdown/styles.d.ts +2 -0
  8. package/dist/components/common/Dropdown/styles.js +17 -3
  9. package/dist/components/form/Autocomplete/components/Autocomplete/index.js +3 -1
  10. package/dist/components/form/CountrySelect/index.js +3 -1
  11. package/dist/components/form/DateInput/index.js +3 -1
  12. package/dist/components/form/DateTimeInput/index.js +3 -1
  13. package/dist/components/form/Select/index.js +3 -1
  14. package/dist/esm/components/common/Drawer/index.js +84 -16
  15. package/dist/esm/components/common/Drawer/styles.js +50 -34
  16. package/dist/esm/components/common/Dropdown/index.js +64 -14
  17. package/dist/esm/components/common/Dropdown/styles.js +16 -2
  18. package/dist/esm/components/form/Autocomplete/components/Autocomplete/index.js +3 -1
  19. package/dist/esm/components/form/CountrySelect/index.js +4 -2
  20. package/dist/esm/components/form/DateInput/index.js +3 -1
  21. package/dist/esm/components/form/DateTimeInput/index.js +3 -1
  22. package/dist/esm/components/form/Select/index.js +3 -1
  23. package/dist/esm/hooks/useOutsideClick/index.js +15 -4
  24. package/dist/hooks/useOutsideClick/index.d.ts +2 -2
  25. package/dist/hooks/useOutsideClick/index.js +14 -4
  26. package/package.json +2 -2
@@ -4,10 +4,11 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
4
4
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
7
- import React, { useCallback, useRef, useState } from 'react';
7
+ import React, { forwardRef, useCallback, useLayoutEffect, useRef, useState } from 'react';
8
+ import { createPortal } from 'react-dom';
8
9
  import * as S from './styles';
9
10
  import { useComponentPalette } from '../../../palette';
10
- export var Dropdown = function Dropdown(_ref) {
11
+ export var Dropdown = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
11
12
  var children = _ref.children,
12
13
  isOpen = _ref.isOpen,
13
14
  _ref$isAutoPosition = _ref.isAutoPosition,
@@ -37,6 +38,42 @@ export var Dropdown = function Dropdown(_ref) {
37
38
  _useState2 = _slicedToArray(_useState, 2),
38
39
  elRef = _useState2[0],
39
40
  setElRef = _useState2[1];
41
+
42
+ // Якорь остаётся в потоке на месте поля-триггера; по его родителю (само поле) считаем,
43
+ // куда поставить вынесенный порталом дропдаун.
44
+ var anchorRef = useRef(null);
45
+ var _useState3 = useState(null),
46
+ _useState4 = _slicedToArray(_useState3, 2),
47
+ layerRect = _useState4[0],
48
+ setLayerRect = _useState4[1];
49
+
50
+ // Дропдаун вынесён в body порталом и спозиционирован `fixed` → НЕ растит документ (баг с
51
+ // пустой полосой внизу) и не зависит от `overflow`/`transform` предков. Слой-обёртка
52
+ // (`PositionLayer`) `fixed` точно над полем: внутренний `Wrapper` остаётся `absolute`
53
+ // относительно него, поэтому ВСЯ старая логика позиционирования (top/bottom/left/right/
54
+ // translateY) и измерения работают без изменений. Пересчёт на скролл/resize, пока открыт.
55
+ useLayoutEffect(function () {
56
+ var updateRect = function updateRect() {
57
+ var _anchorRef$current;
58
+ var field = (_anchorRef$current = anchorRef.current) === null || _anchorRef$current === void 0 ? void 0 : _anchorRef$current.parentElement;
59
+ if (!field) return;
60
+ var rect = field.getBoundingClientRect();
61
+ setLayerRect({
62
+ top: rect.top,
63
+ left: rect.left,
64
+ width: rect.width,
65
+ height: rect.height
66
+ });
67
+ };
68
+ updateRect();
69
+ if (!isOpen) return undefined;
70
+ window.addEventListener('scroll', updateRect, true);
71
+ window.addEventListener('resize', updateRect);
72
+ return function () {
73
+ window.removeEventListener('scroll', updateRect, true);
74
+ window.removeEventListener('resize', updateRect);
75
+ };
76
+ }, [isOpen]);
40
77
  var handleCloseTransitionEnd = useCallback(function (event) {
41
78
  if (event.propertyName !== 'opacity') return;
42
79
  if (isOpen) return;
@@ -59,18 +96,17 @@ export var Dropdown = function Dropdown(_ref) {
59
96
  };
60
97
  var isMoreSpaceBelowThanAbove = belowHeight >= aboveHeight;
61
98
  if (isMoreSpaceBelowThanAbove) {
62
- var outOfViewPortHeight = dropdownHeight + dropdownIndent - belowHeight;
99
+ var _outOfViewPortHeight = dropdownHeight + dropdownIndent - belowHeight;
63
100
  return {
64
101
  isAbove: false,
65
- translateY: "-".concat(outOfViewPortHeight, "px")
66
- };
67
- } else {
68
- var _outOfViewPortHeight = dropdownHeight + dropdownIndent - aboveHeight;
69
- return {
70
- isAbove: true,
71
- translateY: "".concat(_outOfViewPortHeight, "px")
102
+ translateY: "-".concat(_outOfViewPortHeight, "px")
72
103
  };
73
104
  }
105
+ var outOfViewPortHeight = dropdownHeight + dropdownIndent - aboveHeight;
106
+ return {
107
+ isAbove: true,
108
+ translateY: "".concat(outOfViewPortHeight, "px")
109
+ };
74
110
  }, []);
75
111
  var openFlagCache = useRef(null);
76
112
  if (elRef) {
@@ -95,8 +131,21 @@ export var Dropdown = function Dropdown(_ref) {
95
131
  };
96
132
  }
97
133
  }
98
- return /*#__PURE__*/React.createElement(S.Wrapper, {
99
- ref: setElRef,
134
+ var setWrapperRef = useCallback(function (node) {
135
+ setElRef(node);
136
+ if (typeof forwardedRef === 'function') forwardedRef(node);else if (forwardedRef) forwardedRef.current = node;
137
+ }, [forwardedRef]);
138
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(S.Anchor, {
139
+ ref: anchorRef
140
+ }), layerRect && /*#__PURE__*/createPortal(/*#__PURE__*/React.createElement(S.PositionLayer, {
141
+ style: {
142
+ top: layerRect.top,
143
+ left: layerRect.left,
144
+ width: layerRect.width,
145
+ height: layerRect.height
146
+ }
147
+ }, /*#__PURE__*/React.createElement(S.Wrapper, {
148
+ ref: setWrapperRef,
100
149
  $isVisible: isOpen,
101
150
  $isAbove: position.current.isAbove,
102
151
  $translateY: position.current.translateY,
@@ -109,5 +158,6 @@ export var Dropdown = function Dropdown(_ref) {
109
158
  onTransitionEnd: handleCloseTransitionEnd,
110
159
  $palette: palette,
111
160
  $usePadding: usePadding
112
- }, children);
113
- };
161
+ }, children)), document.body));
162
+ });
163
+ Dropdown.displayName = 'Dropdown';
@@ -1,10 +1,24 @@
1
1
  import styled, { css } from 'styled-components';
2
+ // Невидимый якорь в потоке: по его родителю (поле-триггер) считаем позицию портал-дропдауна.
3
+ // display:none — не влияет на вёрстку, но parentElement (само поле) доступен для измерений.
4
+ export var Anchor = styled.span.withConfig({
5
+ displayName: "styles__Anchor",
6
+ componentId: "sc-1f1enhf-0"
7
+ })(["display:none;"]);
8
+
9
+ // Слой-обёртка дропдауна: вынесена порталом в body и спозиционирована fixed точно над полем
10
+ // (через inline top/left/width/height). pointer-events:none — сам слой не перехватывает клики
11
+ // (его площадь = полю), кликабелен только внутренний Wrapper.
12
+ export var PositionLayer = styled.div.withConfig({
13
+ displayName: "styles__PositionLayer",
14
+ componentId: "sc-1f1enhf-1"
15
+ })(["position:fixed;z-index:1000;pointer-events:none;"]);
2
16
  var hidden = css(["visibility:hidden;opacity:0;transition:visibility 0ms 200ms,opacity 200ms 0ms ease;"]);
3
17
  var visible = css(["visibility:visible;opacity:1;transition:visibility 0ms 0ms,opacity 200ms 0ms ease;"]);
4
18
  export var Wrapper = styled.div.withConfig({
5
19
  displayName: "styles__Wrapper",
6
- componentId: "sc-1f1enhf-0"
7
- })(["padding:", ";overflow:hidden;border-radius:20px;box-shadow:0 4px 50px 0 ", ";background:", ";outline:1px solid ", ";min-width:", ";width:", ";position:absolute;left:", ";right:", ";z-index:", ";", ";", ";", ";"], function (_ref) {
20
+ componentId: "sc-1f1enhf-2"
21
+ })(["pointer-events:auto;padding:", ";overflow:hidden;border-radius:20px;box-shadow:0 4px 50px 0 ", ";background:", ";outline:1px solid ", ";min-width:", ";width:", ";position:absolute;left:", ";right:", ";z-index:", ";", ";", ";", ";"], function (_ref) {
8
22
  var $usePadding = _ref.$usePadding;
9
23
  return $usePadding ? '8px 0;' : 'none';
10
24
  }, function (_ref2) {
@@ -87,7 +87,8 @@ export var Autocomplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
87
87
  setIsOpen(false);
88
88
  setActiveIndex(-1);
89
89
  }, []);
90
- var wrapperRef = useOutsideClick(handleClose);
90
+ var dropdownRef = useRef(null);
91
+ var wrapperRef = useOutsideClick(handleClose, [dropdownRef]);
91
92
  var handleChange = useCallback(function (event) {
92
93
  // Ввод нового запроса сбрасывает клавиатурную подсветку (результаты сменятся).
93
94
  setActiveIndex(-1);
@@ -223,6 +224,7 @@ export var Autocomplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
223
224
  "aria-autocomplete": "list",
224
225
  "aria-activedescendant": isDropdownOpen && activeIndex >= 0 ? getOptionId(activeIndex) : undefined
225
226
  }, paddingOptions)))), /*#__PURE__*/React.createElement(Dropdown, {
227
+ ref: dropdownRef,
226
228
  isOpen: isOpen && (!!options.length || !!isLoading),
227
229
  isAutoPosition: true,
228
230
  width: dropdownWidth || '100%',
@@ -12,7 +12,7 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
12
12
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
13
13
  function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var s = Object.getOwnPropertySymbols(e); for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
14
14
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; }
15
- import React, { useCallback, useMemo, useState } from 'react';
15
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
16
16
  import * as S from './stlyes';
17
17
  import { CountryItem } from './components/CountryItem';
18
18
  import { SearchInput } from '../SearchInput';
@@ -57,7 +57,8 @@ export var CountrySelect = function CountrySelect(_ref) {
57
57
  return !flag;
58
58
  });
59
59
  }, []);
60
- var wrapperRef = useOutsideClick(handleDropdownClose);
60
+ var dropdownRef = useRef(null);
61
+ var wrapperRef = useOutsideClick(handleDropdownClose, [dropdownRef]);
61
62
  var sortedOptions = useMemo(function () {
62
63
  return [selectedOption].concat(_toConsumableArray(options.filter(function (option) {
63
64
  return option !== selectedOption;
@@ -84,6 +85,7 @@ export var CountrySelect = function CountrySelect(_ref) {
84
85
  }), !disabled && /*#__PURE__*/React.createElement(Arrow, {
85
86
  isOpen: isOpen
86
87
  })), /*#__PURE__*/React.createElement(Dropdown, _extends({
88
+ ref: dropdownRef,
87
89
  isOpen: isOpen,
88
90
  onCloseTransitionEnd: handleClear,
89
91
  zIndex: 6,
@@ -54,7 +54,8 @@ export var DateInput = function DateInput(_ref) {
54
54
  var handleClose = useCallback(function () {
55
55
  return setOpenFlag(false);
56
56
  }, []);
57
- var elRef = useOutsideClick(handleClose);
57
+ var dropdownRef = useRef(null);
58
+ var elRef = useOutsideClick(handleClose, [dropdownRef]);
58
59
  var _useInputFocus = useInputFocus({
59
60
  onFocus: onFocus,
60
61
  onBlur: onBlur
@@ -160,6 +161,7 @@ export var DateInput = function DateInput(_ref) {
160
161
  minDate: minDate,
161
162
  maxDate: maxDate
162
163
  }) : /*#__PURE__*/React.createElement(Dropdown, _extends({
164
+ ref: dropdownRef,
163
165
  isOpen: isOpen,
164
166
  isAutoPosition: true
165
167
  }, dropdownProps), /*#__PURE__*/React.createElement(S.CalendarWrapper, null, /*#__PURE__*/React.createElement(Calendar, {
@@ -69,7 +69,8 @@ export var DateTimeInput = function DateTimeInput(_ref) {
69
69
  var handleTrigger = useCallback(function () {
70
70
  return isOpen ? handleCloseAndSkipDropdownValue() : setOpenFlag(true);
71
71
  }, [handleCloseAndSkipDropdownValue, isOpen]);
72
- var elRef = useOutsideClick(handleCloseAndSkipDropdownValue);
72
+ var dropdownRef = useRef(null);
73
+ var elRef = useOutsideClick(handleCloseAndSkipDropdownValue, [dropdownRef]);
73
74
  var _useTimePickerHeight = useTimePickerHeight(isOpen),
74
75
  calendarRef = _useTimePickerHeight.calendarRef,
75
76
  handleTimePickerSetHeight = _useTimePickerHeight.handleTimePickerSetHeight,
@@ -180,6 +181,7 @@ export var DateTimeInput = function DateTimeInput(_ref) {
180
181
  onClick: handleTrigger,
181
182
  $color: palette.icon
182
183
  }, /*#__PURE__*/React.createElement(CalendarIcon, null)))), !isMobile ? /*#__PURE__*/React.createElement(Dropdown, _extends({
184
+ ref: dropdownRef,
183
185
  isOpen: isOpen,
184
186
  isAutoPosition: true
185
187
  }, dropdownProps), /*#__PURE__*/React.createElement(S.CalendarWithTimePicker, {
@@ -85,7 +85,8 @@ export var Select = function Select(_ref) {
85
85
  var handleHide = useCallback(function () {
86
86
  return setIsOpen(false);
87
87
  }, []);
88
- var wrapperRef = useOutsideClick(handleHide);
88
+ var dropdownRef = useRef(null);
89
+ var wrapperRef = useOutsideClick(handleHide, [dropdownRef]);
89
90
  var handleFocus = useCallback(function (event) {
90
91
  handleShow();
91
92
  focusHandler(event);
@@ -234,6 +235,7 @@ export var Select = function Select(_ref) {
234
235
  isOpen: isOpen,
235
236
  isPadding: false
236
237
  }, /*#__PURE__*/React.createElement(S.DrawerContent, null, handleDrawerContentRender())) : /*#__PURE__*/React.createElement(Dropdown, {
238
+ ref: dropdownRef,
237
239
  isOpen: isOpen,
238
240
  isAutoPosition: true,
239
241
  width: dropdownWidth || '100%',
@@ -1,16 +1,27 @@
1
1
  import { useEffect, useRef } from 'react';
2
+
3
+ // extraRefs — узлы, которые физически вне основного ref (например, дропдаун, вынесенный порталом
4
+ // в document.body), но логически ему принадлежат: клик по ним НЕ считается «снаружи». Без этого
5
+ // портал-дропдаун ловил бы клик как outside и схлопывался. По умолчанию пусто — обратная
6
+ // совместимость со старыми вызовами `const ref = useOutsideClick(onClick)`.
2
7
  export var useOutsideClick = function useOutsideClick(onClick) {
8
+ var extraRefs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
3
9
  var ref = useRef(null);
10
+ var extraRefsRef = useRef(extraRefs);
11
+ extraRefsRef.current = extraRefs;
4
12
  useEffect(function () {
5
13
  var handleOutsideClick = function handleOutsideClick(event) {
6
- if (ref.current && !ref.current.contains(event.target)) {
7
- onClick();
8
- }
14
+ var target = event.target;
15
+ if (ref.current && ref.current.contains(target)) return;
16
+ if (extraRefsRef.current.some(function (extra) {
17
+ return extra.current && extra.current.contains(target);
18
+ })) return;
19
+ onClick();
9
20
  };
10
21
  document.addEventListener('mousedown', handleOutsideClick);
11
22
  return function () {
12
23
  document.removeEventListener('mousedown', handleOutsideClick);
13
24
  };
14
- }, [ref, onClick]);
25
+ }, [onClick]);
15
26
  return ref;
16
27
  };
@@ -1,2 +1,2 @@
1
- /// <reference types="react" />
2
- export declare const useOutsideClick: (onClick: () => void) => import("react").RefObject<HTMLDivElement>;
1
+ import { RefObject } from 'react';
2
+ export declare const useOutsideClick: (onClick: () => void, extraRefs?: ReadonlyArray<RefObject<HTMLElement>>) => RefObject<HTMLDivElement>;
@@ -5,18 +5,28 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useOutsideClick = void 0;
7
7
  var _react = require("react");
8
+ // extraRefs — узлы, которые физически вне основного ref (например, дропдаун, вынесенный порталом
9
+ // в document.body), но логически ему принадлежат: клик по ним НЕ считается «снаружи». Без этого
10
+ // портал-дропдаун ловил бы клик как outside и схлопывался. По умолчанию пусто — обратная
11
+ // совместимость со старыми вызовами `const ref = useOutsideClick(onClick)`.
8
12
  var useOutsideClick = exports.useOutsideClick = function useOutsideClick(onClick) {
13
+ var extraRefs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
9
14
  var ref = (0, _react.useRef)(null);
15
+ var extraRefsRef = (0, _react.useRef)(extraRefs);
16
+ extraRefsRef.current = extraRefs;
10
17
  (0, _react.useEffect)(function () {
11
18
  var handleOutsideClick = function handleOutsideClick(event) {
12
- if (ref.current && !ref.current.contains(event.target)) {
13
- onClick();
14
- }
19
+ var target = event.target;
20
+ if (ref.current && ref.current.contains(target)) return;
21
+ if (extraRefsRef.current.some(function (extra) {
22
+ return extra.current && extra.current.contains(target);
23
+ })) return;
24
+ onClick();
15
25
  };
16
26
  document.addEventListener('mousedown', handleOutsideClick);
17
27
  return function () {
18
28
  document.removeEventListener('mousedown', handleOutsideClick);
19
29
  };
20
- }, [ref, onClick]);
30
+ }, [onClick]);
21
31
  return ref;
22
32
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invoicebox/ui",
3
- "version": "0.1.0-alpha.294",
3
+ "version": "0.1.0-alpha.295",
4
4
  "description": "Invoicebox UI",
5
5
  "license": "Apache License 2.0",
6
6
  "scripts": {
@@ -32,6 +32,7 @@
32
32
  "@storybook/react-webpack5": "^7.6.13",
33
33
  "@storybook/test": "^7.6.13",
34
34
  "@types/jest": "^29.5.8",
35
+ "@types/react-dom": "^18.3.7",
35
36
  "@typescript-eslint/eslint-plugin": "^5.62.0",
36
37
  "@typescript-eslint/parser": "^5.62.0",
37
38
  "babel-plugin-inline-json-import": "^0.3.2",
@@ -56,7 +57,6 @@
56
57
  "qrcode.react": "^3.1.0",
57
58
  "react-calendar": "^4.8.0",
58
59
  "react-currency-input-field": "^3.10.0",
59
- "react-modal-sheet": "^5.6.0",
60
60
  "react-toastify": "^9.1.3"
61
61
  },
62
62
  "peerDependencies": {