@invoicebox/ui 0.1.0-alpha.322 → 0.1.0-alpha.326
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.
- package/dist/components/common/AppCardLink/index.js +1 -2
- package/dist/components/common/BgCard/index.js +1 -2
- package/dist/components/common/BgCard/styles.js +1 -2
- package/dist/components/common/Calendar/index.js +1 -2
- package/dist/components/common/Calendar/index.stories.js +1 -2
- package/dist/components/common/Calendar/styles.js +1 -2
- package/dist/components/common/Divider/index.js +1 -2
- package/dist/components/common/Drawer/index.js +1 -2
- package/dist/components/common/Drawer/index.stories.js +1 -2
- package/dist/components/common/Drawer/styles.js +1 -2
- package/dist/components/common/Dropdown/index.js +1 -2
- package/dist/components/common/Dropdown/styles.js +8 -9
- package/dist/components/common/EntityAutocompleteOptionsDrawer/index.js +1 -2
- package/dist/components/common/EntityAutocompleteOptionsDrawer/index.stories.js +1 -2
- package/dist/components/common/Flag/index.js +1 -2
- package/dist/components/common/Flag/styles.js +1 -2
- package/dist/components/common/InvoiceboxSpinner/index.js +1 -2
- package/dist/components/common/InvoiceboxSpinner/styles.js +1 -2
- package/dist/components/common/PointsLoader/index.js +1 -2
- package/dist/components/common/QRCode/index.js +2 -2
- package/dist/components/common/ReadOnlyLabelValueField/index.js +1 -2
- package/dist/components/common/Scrollbar/index.js +1 -2
- package/dist/components/common/Scrollbar/styles.js +1 -2
- package/dist/components/common/SecondaryButton/index.js +3 -4
- package/dist/components/common/SecondaryButton/styles.js +1 -2
- package/dist/components/common/Skeleton/index.js +1 -2
- package/dist/components/common/Skeleton/styles.js +1 -2
- package/dist/components/common/Toast/index.js +1 -2
- package/dist/components/common/Tooltip/index.js +1 -2
- package/dist/components/common/Typography/index.js +3 -4
- package/dist/components/common/WarningIcon/index.js +1 -2
- package/dist/components/form/Autocomplete/components/Autocomplete/index.js +1 -2
- package/dist/components/form/Autocomplete/components/Autocomplete/index.stories.js +1 -2
- package/dist/components/form/Autocomplete/components/OrganizationAutocompleteItem/index.js +1 -2
- package/dist/components/form/Checkbox/index.js +3 -4
- package/dist/components/form/Checkbox/index.stories.js +1 -2
- package/dist/components/form/CodeInput/index.js +1 -2
- package/dist/components/form/CodeInput/index.stories.js +1 -2
- package/dist/components/form/CountrySelect/components/CountryItem/index.js +4 -3
- package/dist/components/form/CountrySelect/index.d.ts +6 -0
- package/dist/components/form/CountrySelect/index.js +18 -8
- package/dist/components/form/CountrySelect/index.test.d.ts +1 -0
- package/dist/components/form/CountrySelect/index.test.js +85 -0
- package/dist/components/form/CurrencyInput/CoreCurrencyInput/index.js +1 -2
- package/dist/components/form/CurrencyInput/CoreCurrencyInput/index.stories.js +1 -2
- package/dist/components/form/CurrencyInput/CurrencyInputNumber/index.js +2 -2
- package/dist/components/form/CurrencyInput/CurrencyInputNumber/index.stories.js +1 -2
- package/dist/components/form/CurrencyInput/CurrencyInputString/index.js +2 -2
- package/dist/components/form/CurrencyInput/CurrencyInputString/index.stories.js +1 -2
- package/dist/components/form/DateInput/components/CalendarIcon/index.js +1 -2
- package/dist/components/form/DateInput/components/MobileDrawerDateCalendar/index.js +1 -2
- package/dist/components/form/DateInput/components/MobileDrawerDateCalendar/index.stories.js +1 -2
- package/dist/components/form/DateInput/index.d.ts +1 -1
- package/dist/components/form/DateInput/index.js +8 -3
- package/dist/components/form/DateInput/index.stories.js +1 -2
- package/dist/components/form/DateInput/index.test.d.ts +1 -0
- package/dist/components/form/DateInput/index.test.js +71 -0
- package/dist/components/form/DateTimeInput/components/MobileDatePicker/index.js +1 -2
- package/dist/components/form/DateTimeInput/components/MobileDatePicker/index.stories.js +1 -2
- package/dist/components/form/DateTimeInput/components/MobileDrawerDateTimeCalendar/index.js +1 -2
- package/dist/components/form/DateTimeInput/components/MobileDrawerDateTimeCalendar/index.stories.js +1 -2
- package/dist/components/form/DateTimeInput/components/MobileWheelPicker/components/WheelColumn/index.js +1 -2
- package/dist/components/form/DateTimeInput/components/MobileWheelPicker/index.js +1 -2
- package/dist/components/form/DateTimeInput/components/MobileWheelPicker/index.stories.js +1 -2
- package/dist/components/form/DateTimeInput/components/TimePicker/index.js +1 -2
- package/dist/components/form/DateTimeInput/components/TimePicker/index.stories.js +1 -2
- package/dist/components/form/DateTimeInput/index.js +1 -2
- package/dist/components/form/DateTimeInput/index.stories.js +1 -2
- package/dist/components/form/InputLabel/index.js +1 -2
- package/dist/components/form/InputLabel/styles.js +1 -2
- package/dist/components/form/ModernPlaceholder/index.js +1 -2
- package/dist/components/form/PhoneInput/index.d.ts +1 -1
- package/dist/components/form/PhoneInput/index.js +3 -3
- package/dist/components/form/PhoneInput/index.stories.js +1 -2
- package/dist/components/form/PositiveIntegerInput/index.d.ts +4 -0
- package/dist/components/form/PositiveIntegerInput/index.js +47 -8
- package/dist/components/form/PositiveIntegerInput/index.stories.js +1 -2
- package/dist/components/form/PositiveIntegerInput/index.test.d.ts +1 -0
- package/dist/components/form/PositiveIntegerInput/index.test.js +81 -0
- package/dist/components/form/PositiveIntegerInput/styles.d.ts +1 -3
- package/dist/components/form/PositiveIntegerInput/styles.js +8 -10
- package/dist/components/form/PureInput/index.js +3 -4
- package/dist/components/form/PureInput/styles.js +2 -2
- package/dist/components/form/ResetButton/index.js +1 -2
- package/dist/components/form/SearchInput/index.js +1 -2
- package/dist/components/form/SearchInput/index.stories.js +1 -2
- package/dist/components/form/Select/components/Input/index.js +1 -2
- package/dist/components/form/Select/components/Loader/index.js +1 -2
- package/dist/components/form/Select/components/MultipleValue/index.js +1 -2
- package/dist/components/form/Select/components/MultipleValue/styles.js +1 -2
- package/dist/components/form/Select/index.js +24 -8
- package/dist/components/form/Select/index.stories.js +1 -2
- package/dist/components/form/Select/styles.js +1 -2
- package/dist/components/form/TagsInput/index.js +1 -2
- package/dist/components/form/TagsInput/index.stories.js +1 -2
- package/dist/components/form/TagsInput/styles.js +2 -2
- package/dist/components/form/TextInput/index.js +1 -2
- package/dist/components/form/TextInput/index.stories.js +1 -2
- package/dist/components/form/Toggle/index.js +3 -4
- package/dist/components/form/Toggle/index.stories.js +1 -2
- package/dist/components/form/Toggle/styles.js +10 -10
- package/dist/components/form/_hooks/useInputStyles.js +1 -2
- package/dist/components/form/common/DrawerHeader/index.js +1 -2
- package/dist/esm/components/common/Dropdown/styles.js +7 -7
- package/dist/esm/components/common/QRCode/index.js +2 -2
- package/dist/esm/components/common/SecondaryButton/index.js +2 -2
- package/dist/esm/components/common/Typography/index.js +2 -2
- package/dist/esm/components/form/Checkbox/index.js +2 -2
- package/dist/esm/components/form/CountrySelect/components/CountryItem/index.js +3 -1
- package/dist/esm/components/form/CountrySelect/index.js +18 -7
- package/dist/esm/components/form/CountrySelect/index.test.js +82 -0
- package/dist/esm/components/form/CurrencyInput/CurrencyInputNumber/index.js +2 -2
- package/dist/esm/components/form/CurrencyInput/CurrencyInputString/index.js +2 -2
- package/dist/esm/components/form/DateInput/index.js +7 -1
- package/dist/esm/components/form/DateInput/index.test.js +69 -0
- package/dist/esm/components/form/PhoneInput/index.js +2 -1
- package/dist/esm/components/form/PositiveIntegerInput/index.js +46 -6
- package/dist/esm/components/form/PositiveIntegerInput/index.test.js +78 -0
- package/dist/esm/components/form/PositiveIntegerInput/styles.js +7 -7
- package/dist/esm/components/form/PureInput/index.js +2 -2
- package/dist/esm/components/form/PureInput/styles.js +2 -2
- package/dist/esm/components/form/Select/index.js +23 -6
- package/dist/esm/components/form/TagsInput/styles.js +2 -2
- package/dist/esm/components/form/Toggle/index.js +2 -2
- package/dist/esm/components/form/Toggle/styles.js +10 -10
- package/dist/esm/hooks/useQueryFilter/context.js +40 -0
- package/dist/esm/hooks/useQueryFilter/helpers.js +58 -0
- package/dist/esm/hooks/useQueryFilter/index.js +123 -0
- package/dist/esm/hooks/useQueryFilter/index.stories.js +71 -0
- package/dist/esm/hooks/useQueryFilter/index.test.js +180 -0
- package/dist/esm/index.js +1 -0
- package/dist/hooks/useQueryFilter/context.d.ts +35 -0
- package/dist/hooks/useQueryFilter/context.js +48 -0
- package/dist/hooks/useQueryFilter/helpers.d.ts +17 -0
- package/dist/hooks/useQueryFilter/helpers.js +64 -0
- package/dist/hooks/useQueryFilter/index.d.ts +19 -0
- package/dist/hooks/useQueryFilter/index.js +163 -0
- package/dist/hooks/useQueryFilter/index.stories.d.ts +4 -0
- package/dist/hooks/useQueryFilter/index.stories.js +75 -0
- package/dist/hooks/useQueryFilter/index.test.d.ts +1 -0
- package/dist/hooks/useQueryFilter/index.test.js +183 -0
- package/dist/hooks/useUnupdatableHandler/index.stories.js +1 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +97 -0
- package/dist/palette/context.js +48 -48
- package/dist/palette/provider.js +49 -50
- package/package.json +2 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
4
|
+
import { DateInput } from '.';
|
|
5
|
+
|
|
6
|
+
// Серверный рендер: предупреждения про useLayoutEffect (Dropdown) ожидаемы и к проверке не относятся
|
|
7
|
+
// eslint-disable-next-line no-console
|
|
8
|
+
var originalError = console.error;
|
|
9
|
+
beforeAll(function () {
|
|
10
|
+
jest.spyOn(console, 'error').mockImplementation(function (message) {
|
|
11
|
+
if (typeof message === 'string' && message.includes('useLayoutEffect does nothing on the server')) return;
|
|
12
|
+
// eslint-disable-next-line no-console
|
|
13
|
+
for (var _len = arguments.length, rest = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
14
|
+
rest[_key - 1] = arguments[_key];
|
|
15
|
+
}
|
|
16
|
+
originalError.apply(void 0, [message].concat(rest));
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
afterAll(function () {
|
|
20
|
+
return jest.restoreAllMocks();
|
|
21
|
+
});
|
|
22
|
+
var noop = jest.fn();
|
|
23
|
+
|
|
24
|
+
// useMobile читает window.matchMedia при первом рендере
|
|
25
|
+
var withViewport = function withViewport(isMobile, render) {
|
|
26
|
+
var globalWithWindow = globalThis;
|
|
27
|
+
var previous = globalWithWindow.window;
|
|
28
|
+
globalWithWindow.window = {
|
|
29
|
+
matchMedia: function matchMedia() {
|
|
30
|
+
return {
|
|
31
|
+
matches: isMobile
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
return render();
|
|
37
|
+
} finally {
|
|
38
|
+
globalWithWindow.window = previous;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var renderInput = function renderInput(isMobile) {
|
|
42
|
+
var _markup$match$, _markup$match;
|
|
43
|
+
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
44
|
+
var markup = withViewport(isMobile, function () {
|
|
45
|
+
return renderToStaticMarkup(/*#__PURE__*/React.createElement(DateInput, _extends({
|
|
46
|
+
id: "date",
|
|
47
|
+
label: "\u0414\u0430\u0442\u0430",
|
|
48
|
+
value: null,
|
|
49
|
+
onChange: noop
|
|
50
|
+
}, props)));
|
|
51
|
+
});
|
|
52
|
+
return (_markup$match$ = (_markup$match = markup.match(/<input[^>]*>/)) === null || _markup$match === void 0 ? void 0 : _markup$match[0]) !== null && _markup$match$ !== void 0 ? _markup$match$ : '';
|
|
53
|
+
};
|
|
54
|
+
describe('DateInput: экранная клавиатура (MOB-8)', function () {
|
|
55
|
+
test('на мобильном — inputmode="none": тап открывает только календарь', function () {
|
|
56
|
+
expect(renderInput(true)).toContain('inputMode="none"');
|
|
57
|
+
});
|
|
58
|
+
test('на десктопе inputmode не задан', function () {
|
|
59
|
+
expect(renderInput(false)).not.toContain('inputMode');
|
|
60
|
+
});
|
|
61
|
+
test('проп inputMode переопределяет значение по умолчанию', function () {
|
|
62
|
+
expect(renderInput(false, {
|
|
63
|
+
inputMode: 'none'
|
|
64
|
+
})).toContain('inputMode="none"');
|
|
65
|
+
expect(renderInput(true, {
|
|
66
|
+
inputMode: 'numeric'
|
|
67
|
+
})).toContain('inputMode="numeric"');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -202,7 +202,8 @@ export var PhoneInput = function PhoneInput(_ref) {
|
|
|
202
202
|
positionRight: "0",
|
|
203
203
|
positionLeft: "-20px",
|
|
204
204
|
disabled: disabled,
|
|
205
|
-
selectedLabel: (countrySelectProps === null || countrySelectProps === void 0 ? void 0 : countrySelectProps.selectedLabel) || ''
|
|
205
|
+
selectedLabel: (countrySelectProps === null || countrySelectProps === void 0 ? void 0 : countrySelectProps.selectedLabel) || '',
|
|
206
|
+
buttonAriaLabelPrefix: countrySelectProps === null || countrySelectProps === void 0 ? void 0 : countrySelectProps.buttonAriaLabelPrefix
|
|
206
207
|
}))), /*#__PURE__*/React.createElement(InputMask, {
|
|
207
208
|
value: inputValue,
|
|
208
209
|
onFocus: focusHandler,
|
|
@@ -26,7 +26,13 @@ export var PositiveIntegerInput = function PositiveIntegerInput(_ref) {
|
|
|
26
26
|
size = _ref.size,
|
|
27
27
|
_ref$useModernStyles = _ref.useModernStyles,
|
|
28
28
|
useModernStyles = _ref$useModernStyles === void 0 ? false : _ref$useModernStyles,
|
|
29
|
-
id = _ref.id
|
|
29
|
+
id = _ref.id,
|
|
30
|
+
_ref$disabled = _ref.disabled,
|
|
31
|
+
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
|
|
32
|
+
_ref$incrementButtonA = _ref.incrementButtonAriaLabel,
|
|
33
|
+
incrementButtonAriaLabel = _ref$incrementButtonA === void 0 ? 'Увеличить' : _ref$incrementButtonA,
|
|
34
|
+
_ref$decrementButtonA = _ref.decrementButtonAriaLabel,
|
|
35
|
+
decrementButtonAriaLabel = _ref$decrementButtonA === void 0 ? 'Уменьшить' : _ref$decrementButtonA;
|
|
30
36
|
var _useInputFocus = useInputFocus({
|
|
31
37
|
onFocus: onFocus,
|
|
32
38
|
onBlur: onBlur
|
|
@@ -76,12 +82,32 @@ export var PositiveIntegerInput = function PositiveIntegerInput(_ref) {
|
|
|
76
82
|
var handleDown = useCallback(function () {
|
|
77
83
|
onChange(decrement(value));
|
|
78
84
|
}, [decrement, value, onChange]);
|
|
85
|
+
var isUpDisabled = disabled || isIncrementDisabled;
|
|
86
|
+
var isDownDisabled = disabled || isDecrementDisabled;
|
|
87
|
+
|
|
88
|
+
// Стрелки клавиатуры в поле — как кнопки «вверх»/«вниз» (кнопки вне порядка Tab)
|
|
89
|
+
var handleKeyDown = useCallback(function (event) {
|
|
90
|
+
if (!upAndDown) return;
|
|
91
|
+
if (event.key === 'ArrowUp') {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
if (!isUpDisabled) handleUp();
|
|
94
|
+
} else if (event.key === 'ArrowDown') {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
if (!isDownDisabled) handleDown();
|
|
97
|
+
}
|
|
98
|
+
}, [handleDown, handleUp, isDownDisabled, isUpDisabled, upAndDown]);
|
|
99
|
+
|
|
100
|
+
// Клик по кнопке не забирает фокус у поля: иначе срабатывал бы onBlur поля
|
|
101
|
+
var preventFocusLoss = useCallback(function (event) {
|
|
102
|
+
return event.preventDefault();
|
|
103
|
+
}, []);
|
|
79
104
|
return /*#__PURE__*/React.createElement(InputLabel, {
|
|
80
105
|
inFocus: inFocus,
|
|
81
106
|
label: inputLabel,
|
|
82
107
|
useModernStyles: useModernStyles,
|
|
83
108
|
size: fieldSize,
|
|
84
|
-
htmlFor: inputId
|
|
109
|
+
htmlFor: inputId,
|
|
110
|
+
disabled: disabled
|
|
85
111
|
}, /*#__PURE__*/React.createElement(S.ControlWrapper, null, modernPlaceholder, /*#__PURE__*/React.createElement(PureInput, _extends({
|
|
86
112
|
id: inputId,
|
|
87
113
|
"aria-label": label && !inputLabel ? label : undefined,
|
|
@@ -93,18 +119,32 @@ export var PositiveIntegerInput = function PositiveIntegerInput(_ref) {
|
|
|
93
119
|
onBlur: handleBlur,
|
|
94
120
|
value: normalizeFrom(value),
|
|
95
121
|
onChange: handleChange,
|
|
122
|
+
onKeyDown: handleKeyDown,
|
|
123
|
+
disabled: disabled,
|
|
124
|
+
inputMode: "numeric",
|
|
125
|
+
pattern: "[0-9]*",
|
|
96
126
|
paddingRight: upAndDown ? 44 : undefined
|
|
97
127
|
}, paddingAndVariantOptions, {
|
|
98
128
|
useModernStyles: useModernStyles
|
|
99
129
|
})), upAndDown && /*#__PURE__*/React.createElement(S.Arrows, null, /*#__PURE__*/React.createElement(S.Arrow, {
|
|
100
|
-
|
|
101
|
-
|
|
130
|
+
type: "button",
|
|
131
|
+
tabIndex: -1,
|
|
132
|
+
"aria-label": incrementButtonAriaLabel,
|
|
133
|
+
"aria-controls": inputId,
|
|
134
|
+
disabled: isUpDisabled,
|
|
135
|
+
onMouseDown: preventFocusLoss,
|
|
136
|
+
onClick: handleUp
|
|
102
137
|
}, /*#__PURE__*/React.createElement(Arrow, {
|
|
103
138
|
isOpen: true,
|
|
104
139
|
outterSize: 12
|
|
105
140
|
})), /*#__PURE__*/React.createElement(S.Arrow, {
|
|
106
|
-
|
|
107
|
-
|
|
141
|
+
type: "button",
|
|
142
|
+
tabIndex: -1,
|
|
143
|
+
"aria-label": decrementButtonAriaLabel,
|
|
144
|
+
"aria-controls": inputId,
|
|
145
|
+
disabled: isDownDisabled,
|
|
146
|
+
onMouseDown: preventFocusLoss,
|
|
147
|
+
onClick: handleDown
|
|
108
148
|
}, /*#__PURE__*/React.createElement(Arrow, {
|
|
109
149
|
isOpen: false,
|
|
110
150
|
outterSize: 12
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
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
|
+
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
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
10
|
+
import { PositiveIntegerInput } from '.';
|
|
11
|
+
var noop = jest.fn();
|
|
12
|
+
var render = function render() {
|
|
13
|
+
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
14
|
+
return renderToStaticMarkup(/*#__PURE__*/React.createElement(PositiveIntegerInput, _extends({
|
|
15
|
+
id: "count",
|
|
16
|
+
label: "\u041A\u043E\u043B-\u0432\u043E",
|
|
17
|
+
value: 2,
|
|
18
|
+
onChange: noop,
|
|
19
|
+
max: 5,
|
|
20
|
+
upAndDown: true
|
|
21
|
+
}, props)));
|
|
22
|
+
};
|
|
23
|
+
var buttons = function buttons(markup) {
|
|
24
|
+
var _markup$match;
|
|
25
|
+
return (_markup$match = markup.match(/<button[^>]*>/g)) !== null && _markup$match !== void 0 ? _markup$match : [];
|
|
26
|
+
};
|
|
27
|
+
var input = function input(markup) {
|
|
28
|
+
var _markup$match$, _markup$match2;
|
|
29
|
+
return (_markup$match$ = (_markup$match2 = markup.match(/<input[^>]*>/)) === null || _markup$match2 === void 0 ? void 0 : _markup$match2[0]) !== null && _markup$match$ !== void 0 ? _markup$match$ : '';
|
|
30
|
+
};
|
|
31
|
+
describe('PositiveIntegerInput: стрелки «вверх/вниз» (A11Y-16)', function () {
|
|
32
|
+
test('стрелки — кнопки с именами вне порядка Tab', function () {
|
|
33
|
+
var _buttons = buttons(render()),
|
|
34
|
+
_buttons2 = _slicedToArray(_buttons, 2),
|
|
35
|
+
up = _buttons2[0],
|
|
36
|
+
down = _buttons2[1];
|
|
37
|
+
expect(up).toContain('type="button"');
|
|
38
|
+
expect(up).toContain('tabindex="-1"');
|
|
39
|
+
expect(up).toContain('aria-label="Увеличить"');
|
|
40
|
+
expect(up).toContain('aria-controls="count"');
|
|
41
|
+
expect(up).not.toContain('disabled');
|
|
42
|
+
expect(down).toContain('aria-label="Уменьшить"');
|
|
43
|
+
expect(down).not.toContain('disabled');
|
|
44
|
+
});
|
|
45
|
+
test('имена кнопок задаются пропами', function () {
|
|
46
|
+
var _buttons3 = buttons(render({
|
|
47
|
+
incrementButtonAriaLabel: 'Увеличить количество',
|
|
48
|
+
decrementButtonAriaLabel: 'Уменьшить количество'
|
|
49
|
+
})),
|
|
50
|
+
_buttons4 = _slicedToArray(_buttons3, 2),
|
|
51
|
+
up = _buttons4[0],
|
|
52
|
+
down = _buttons4[1];
|
|
53
|
+
expect(up).toContain('aria-label="Увеличить количество"');
|
|
54
|
+
expect(down).toContain('aria-label="Уменьшить количество"');
|
|
55
|
+
});
|
|
56
|
+
test('на границах недоступна соответствующая кнопка', function () {
|
|
57
|
+
expect(buttons(render({
|
|
58
|
+
value: 5
|
|
59
|
+
}))[0]).toContain('disabled');
|
|
60
|
+
expect(buttons(render({
|
|
61
|
+
value: 0
|
|
62
|
+
}))[1]).toContain('disabled');
|
|
63
|
+
});
|
|
64
|
+
test('disabled блокирует поле и обе кнопки', function () {
|
|
65
|
+
var markup = render({
|
|
66
|
+
disabled: true
|
|
67
|
+
});
|
|
68
|
+
expect(input(markup)).toContain('disabled');
|
|
69
|
+
buttons(markup).forEach(function (button) {
|
|
70
|
+
return expect(button).toContain('disabled');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
test('цифровая клавиатура на телефоне', function () {
|
|
74
|
+
var field = input(render());
|
|
75
|
+
expect(field).toContain('inputMode="numeric"');
|
|
76
|
+
expect(field).toContain('pattern="[0-9]*"');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import styled
|
|
1
|
+
import styled from 'styled-components';
|
|
2
2
|
export var Arrows = styled.div.withConfig({
|
|
3
3
|
displayName: "styles__Arrows",
|
|
4
4
|
componentId: "sc-ctutr8-0"
|
|
5
|
-
})(["display:flex;flex-direction:column;position:absolute;height:100%;justify-content:center;top:0;right:
|
|
6
|
-
|
|
5
|
+
})(["display:flex;flex-direction:column;position:absolute;height:100%;justify-content:center;top:0;right:12px;"]);
|
|
6
|
+
|
|
7
|
+
// Кнопка 24×24 (WCAG 2.5.8) со стрелкой 12 px по центру; в низком поле кнопки сжимаются по высоте,
|
|
8
|
+
// чтобы не вылезать за его границы
|
|
9
|
+
export var Arrow = styled.button.withConfig({
|
|
7
10
|
displayName: "styles__Arrow",
|
|
8
11
|
componentId: "sc-ctutr8-1"
|
|
9
|
-
})(["
|
|
10
|
-
var $disabled = _ref.$disabled;
|
|
11
|
-
return $disabled && css(["opacity:0.4;pointer-events:none;"]);
|
|
12
|
-
});
|
|
12
|
+
})(["flex:0 1 24px;min-height:0;width:24px;display:flex;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:inherit;cursor:pointer;user-select:none;&:disabled{opacity:0.4;cursor:default;}"]);
|
|
13
13
|
export var ControlWrapper = styled.div.withConfig({
|
|
14
14
|
displayName: "styles__ControlWrapper",
|
|
15
15
|
componentId: "sc-ctutr8-2"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var _excluded = ["paddingLeft", "paddingRight", "paddingTop", "paddingBottom", "borderRadius", "hasError", "inFocus", "hasBorder", "isOnlyNumbers", "className", "onChange", "element", "variant", "renderedValue", "useModernStyles", "id"];
|
|
2
2
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
3
|
-
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var
|
|
4
|
-
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.
|
|
3
|
+
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
4
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
5
5
|
import React, { useCallback, useId } from 'react';
|
|
6
6
|
import * as S from './styles';
|
|
7
7
|
import { useComponentPalette } from '../../../palette';
|
|
@@ -37,8 +37,8 @@ export var getPureInputStyled = function getPureInputStyled(component) {
|
|
|
37
37
|
$hasBorder = _ref9.$hasBorder,
|
|
38
38
|
$palette = _ref9.$palette;
|
|
39
39
|
return !$hasBorder ? 'transparent' : $hasError ? $palette.error : $inFocus ? $palette.borderFocus : $palette.border;
|
|
40
|
-
}, function (
|
|
41
|
-
var $palette =
|
|
40
|
+
}, function (_ref0) {
|
|
41
|
+
var $palette = _ref0.$palette;
|
|
42
42
|
return $palette.placeholder;
|
|
43
43
|
}, reducedMotion);
|
|
44
44
|
};
|
|
@@ -148,13 +148,20 @@ var SelectInner = function SelectInner(props) {
|
|
|
148
148
|
}, [activeIndex, baseId]);
|
|
149
149
|
var dropdownRef = useRef(null);
|
|
150
150
|
var wrapperRef = useOutsideClick(handleHide, [dropdownRef]);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
|
|
152
|
+
// Список не открывается на фокус (WCAG 2.4.11): иначе при переходе по Tab он перекрывает
|
|
153
|
+
// следующие элементы. Открытие — кликом, Enter, пробелом или стрелками
|
|
154
|
+
var handleFocus = focusHandler;
|
|
155
155
|
var handleBlur = useCallback(function (event) {
|
|
156
|
+
var _wrapperRef$current, _dropdownRef$current;
|
|
156
157
|
var relatedTarget = event.relatedTarget;
|
|
157
158
|
var isOptionClick = (relatedTarget === null || relatedTarget === void 0 ? void 0 : relatedTarget.dataset.optionIdentifier) === OPTION_IDENTIFIER;
|
|
159
|
+
|
|
160
|
+
// Фокус ушёл на другой элемент вне поля и списка — закрываем список. Без relatedTarget
|
|
161
|
+
// (клик по неактивной области списка, уход из окна) не закрываем: клик снаружи
|
|
162
|
+
// обрабатывает useOutsideClick
|
|
163
|
+
var isFocusOutside = !!relatedTarget && !((_wrapperRef$current = wrapperRef.current) !== null && _wrapperRef$current !== void 0 && _wrapperRef$current.contains(relatedTarget)) && !((_dropdownRef$current = dropdownRef.current) !== null && _dropdownRef$current !== void 0 && _dropdownRef$current.contains(relatedTarget));
|
|
164
|
+
if (isFocusOutside) handleHide();
|
|
158
165
|
if (!isOptionClick) {
|
|
159
166
|
blurHandler(event);
|
|
160
167
|
return;
|
|
@@ -162,7 +169,7 @@ var SelectInner = function SelectInner(props) {
|
|
|
162
169
|
delayBlur.current = function () {
|
|
163
170
|
return blurHandler(event);
|
|
164
171
|
};
|
|
165
|
-
}, [blurHandler]);
|
|
172
|
+
}, [blurHandler, handleHide, wrapperRef]);
|
|
166
173
|
var selectValue = useCallback(function (newValue) {
|
|
167
174
|
if (isMultiple) {
|
|
168
175
|
var next = multipleValue.includes(newValue) ? multipleValue.filter(function (item) {
|
|
@@ -214,6 +221,10 @@ var SelectInner = function SelectInner(props) {
|
|
|
214
221
|
handleHide();
|
|
215
222
|
}
|
|
216
223
|
break;
|
|
224
|
+
case 'Tab':
|
|
225
|
+
// фокус уходит дальше по странице — список не должен оставаться поверх
|
|
226
|
+
if (isOpen) handleHide();
|
|
227
|
+
break;
|
|
217
228
|
default:
|
|
218
229
|
break;
|
|
219
230
|
}
|
|
@@ -231,6 +242,12 @@ var SelectInner = function SelectInner(props) {
|
|
|
231
242
|
(_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.blur();
|
|
232
243
|
handleShow();
|
|
233
244
|
};
|
|
245
|
+
|
|
246
|
+
// Раньше список открывался фокусом, который ставит и клик; теперь клик открывает его сам.
|
|
247
|
+
// Повторный клик по полю закрывает список, как у нативного select
|
|
248
|
+
var handleToggleClick = function handleToggleClick() {
|
|
249
|
+
if (isOpen) handleHide();else handleShow();
|
|
250
|
+
};
|
|
234
251
|
var handleOptionRender = function handleOptionRender(option) {
|
|
235
252
|
if (renderOption) {
|
|
236
253
|
if (!option.entity) {
|
|
@@ -357,7 +374,7 @@ var SelectInner = function SelectInner(props) {
|
|
|
357
374
|
isOpen: isOpen,
|
|
358
375
|
onReset: isResetButtonEnabled ? handleReset : undefined,
|
|
359
376
|
size: fieldSize,
|
|
360
|
-
onClick: isDrawerOptions || !!renderedValue ? handleInputClick :
|
|
377
|
+
onClick: isDrawerOptions || !!renderedValue ? handleInputClick : handleToggleClick,
|
|
361
378
|
required: required,
|
|
362
379
|
useModernStyles: useModernStyles,
|
|
363
380
|
comboboxProps: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var _excluded = ["children"];
|
|
2
2
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
3
|
-
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var
|
|
4
|
-
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.
|
|
3
|
+
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
4
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { getPureInputStyled } from '../PureInput/styles';
|
|
7
7
|
import styled from 'styled-components';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var _excluded = ["checked", "variant", "label", "onChange", "disabled", "alwaysHighlighted"];
|
|
2
2
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
3
|
-
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var
|
|
4
|
-
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.
|
|
3
|
+
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
4
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import * as S from './styles';
|
|
7
7
|
import { INPUT_VARIANT_MAP } from './styles';
|
|
@@ -57,22 +57,22 @@ export var StyledInput = styled.input.withConfig({
|
|
|
57
57
|
}, function (_ref9) {
|
|
58
58
|
var $variant = _ref9.$variant;
|
|
59
59
|
return INPUT_VARIANT_MAP[$variant].circleSize;
|
|
60
|
+
}, function (_ref0) {
|
|
61
|
+
var $palette = _ref0.$palette;
|
|
62
|
+
return $palette.circle;
|
|
63
|
+
}, function (_ref1) {
|
|
64
|
+
var $variant = _ref1.$variant;
|
|
65
|
+
return INPUT_VARIANT_MAP[$variant].leftChecked;
|
|
60
66
|
}, function (_ref10) {
|
|
61
67
|
var $palette = _ref10.$palette;
|
|
62
|
-
return $palette.
|
|
68
|
+
return $palette.activeBg;
|
|
63
69
|
}, function (_ref11) {
|
|
64
70
|
var $variant = _ref11.$variant;
|
|
65
|
-
return INPUT_VARIANT_MAP[$variant].
|
|
71
|
+
return INPUT_VARIANT_MAP[$variant].border;
|
|
66
72
|
}, function (_ref12) {
|
|
67
73
|
var $palette = _ref12.$palette;
|
|
68
|
-
return $palette.activeBg;
|
|
69
|
-
}, function (_ref13) {
|
|
70
|
-
var $variant = _ref13.$variant;
|
|
71
|
-
return INPUT_VARIANT_MAP[$variant].border;
|
|
72
|
-
}, function (_ref14) {
|
|
73
|
-
var $palette = _ref14.$palette;
|
|
74
74
|
return $palette.disabledBorder;
|
|
75
|
-
}, function (
|
|
76
|
-
var $palette =
|
|
75
|
+
}, function (_ref13) {
|
|
76
|
+
var $palette = _ref13.$palette;
|
|
77
77
|
return $palette.activeBg;
|
|
78
78
|
}, reducedMotion);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from 'react';
|
|
2
|
+
export var DEFAULT_PAGE_PARAM_NAME = 'page';
|
|
3
|
+
export var DEFAULT_FIRST_PAGE = 1;
|
|
4
|
+
var QueryFilterContext = /*#__PURE__*/createContext(null);
|
|
5
|
+
/**
|
|
6
|
+
* Подключает `useQueryFilter` к роутеру приложения. Библиотека от роутера не зависит:
|
|
7
|
+
* приложение само передаёт параметры строки запроса и способ их записать.
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const [searchParams, setSearchParams] = useSearchParams();
|
|
11
|
+
* <QueryFilterProvider searchParams={searchParams} setSearchParams={setSearchParams}>
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export var QueryFilterProvider = function QueryFilterProvider(_ref) {
|
|
15
|
+
var searchParams = _ref.searchParams,
|
|
16
|
+
setSearchParams = _ref.setSearchParams,
|
|
17
|
+
_ref$pageParamName = _ref.pageParamName,
|
|
18
|
+
pageParamName = _ref$pageParamName === void 0 ? DEFAULT_PAGE_PARAM_NAME : _ref$pageParamName,
|
|
19
|
+
_ref$firstPage = _ref.firstPage,
|
|
20
|
+
firstPage = _ref$firstPage === void 0 ? DEFAULT_FIRST_PAGE : _ref$firstPage,
|
|
21
|
+
children = _ref.children;
|
|
22
|
+
var value = useMemo(function () {
|
|
23
|
+
return {
|
|
24
|
+
searchParams: searchParams,
|
|
25
|
+
setSearchParams: setSearchParams,
|
|
26
|
+
pageParamName: pageParamName,
|
|
27
|
+
firstPage: firstPage
|
|
28
|
+
};
|
|
29
|
+
}, [searchParams, setSearchParams, pageParamName, firstPage]);
|
|
30
|
+
return /*#__PURE__*/React.createElement(QueryFilterContext.Provider, {
|
|
31
|
+
value: value
|
|
32
|
+
}, children);
|
|
33
|
+
};
|
|
34
|
+
export var useQueryFilterContext = function useQueryFilterContext() {
|
|
35
|
+
var context = useContext(QueryFilterContext);
|
|
36
|
+
if (!context) {
|
|
37
|
+
throw new Error('useQueryFilter must be used inside QueryFilterProvider');
|
|
38
|
+
}
|
|
39
|
+
return context;
|
|
40
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export var DATA_SPLITTER = '!!!';
|
|
2
|
+
var pad = function pad(value) {
|
|
3
|
+
return String(value).padStart(2, '0');
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Дата в строку запроса: локальное время со смещением, `yyyy-MM-dd'T'HH:mm:ssxxx`
|
|
8
|
+
* (например, `2026-10-06T19:16:00+07:00`). Миллисекунды отбрасываются.
|
|
9
|
+
*/
|
|
10
|
+
export var formatDateForQuery = function formatDateForQuery(date) {
|
|
11
|
+
var offsetMinutes = -date.getTimezoneOffset();
|
|
12
|
+
var sign = offsetMinutes >= 0 ? '+' : '-';
|
|
13
|
+
var absOffset = Math.abs(offsetMinutes);
|
|
14
|
+
return "".concat(date.getFullYear(), "-").concat(pad(date.getMonth() + 1), "-").concat(pad(date.getDate())) + "T".concat(pad(date.getHours()), ":").concat(pad(date.getMinutes()), ":").concat(pad(date.getSeconds())) + "".concat(sign).concat(pad(Math.floor(absOffset / 60)), ":").concat(pad(absOffset % 60));
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Дата без времени: `yyyy-MM-dd` по местному времени */
|
|
18
|
+
export var formatDateOnlyForQuery = function formatDateOnlyForQuery(date) {
|
|
19
|
+
return "".concat(date.getFullYear(), "-").concat(pad(date.getMonth() + 1), "-").concat(pad(date.getDate()));
|
|
20
|
+
};
|
|
21
|
+
export var getDateOrNullFromQueryString = function getDateOrNullFromQueryString(value) {
|
|
22
|
+
return value ? new Date(value) : null;
|
|
23
|
+
};
|
|
24
|
+
export var getQueryStringFromDateOrNull = function getQueryStringFromDateOrNull(value) {
|
|
25
|
+
return value ? formatDateForQuery(value) : '';
|
|
26
|
+
};
|
|
27
|
+
export var getDateArrOrNullFromQueryString = function getDateArrOrNullFromQueryString(value) {
|
|
28
|
+
if (value) {
|
|
29
|
+
var datesArray = value.split(DATA_SPLITTER).map(function (date) {
|
|
30
|
+
return new Date(date);
|
|
31
|
+
});
|
|
32
|
+
if (datesArray.length === 2) {
|
|
33
|
+
return [datesArray[0], datesArray[1]];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
};
|
|
38
|
+
// Диапазон дат хранится в адресе без времени — так он и раньше писался в кабинете
|
|
39
|
+
export var getQueryStringFromDateArrOrNull = function getQueryStringFromDateArrOrNull(value) {
|
|
40
|
+
return value ? value.map(formatDateOnlyForQuery).join(DATA_SPLITTER) : '';
|
|
41
|
+
};
|
|
42
|
+
export var getNumberOrNullFromQueryString = function getNumberOrNullFromQueryString(value) {
|
|
43
|
+
return value ? Number(value) : null;
|
|
44
|
+
};
|
|
45
|
+
export var getQueryStringFromNumberOrNull = function getQueryStringFromNumberOrNull(value) {
|
|
46
|
+
return value ? String(value) : '';
|
|
47
|
+
};
|
|
48
|
+
export var getQueryStringFromArrayString = function getQueryStringFromArrayString(value) {
|
|
49
|
+
return (value === null || value === void 0 ? void 0 : value.join(DATA_SPLITTER)) || '';
|
|
50
|
+
};
|
|
51
|
+
export var getArrayStringsFromQueryString = function getArrayStringsFromQueryString(value) {
|
|
52
|
+
return value ? value.split(DATA_SPLITTER) : undefined;
|
|
53
|
+
};
|
|
54
|
+
export var booleanFromString = function booleanFromString(value) {
|
|
55
|
+
if (value === 'true') return true;
|
|
56
|
+
if (value === 'false') return false;
|
|
57
|
+
return null;
|
|
58
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
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
|
+
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
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
8
|
+
import { useQueryFilterContext } from './context';
|
|
9
|
+
export { QueryFilterProvider, DEFAULT_PAGE_PARAM_NAME, DEFAULT_FIRST_PAGE } from './context';
|
|
10
|
+
export * from './helpers';
|
|
11
|
+
var getIsEmptyValue = function getIsEmptyValue(value) {
|
|
12
|
+
return value === '' || value === undefined || value === null || Array.isArray(value) && value.length === 0;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Значение фильтра, которое живёт в строке запроса.
|
|
17
|
+
*
|
|
18
|
+
* - `initialValue` записывается в адрес при монтировании, если параметра там ещё нет;
|
|
19
|
+
* пустое значение (`''`, `null`, `undefined`, `[]`) удаляет параметр.
|
|
20
|
+
* - Смена значения сбрасывает номер страницы на первую (`isResetPageWhenChanged`).
|
|
21
|
+
* - Начальное значение можно передавать выражением (`new Date()`, `[]`): хук сравнивает его
|
|
22
|
+
* по строке, которую оно даёт в адресе, а не по ссылке.
|
|
23
|
+
*
|
|
24
|
+
* Нужен `QueryFilterProvider` выше по дереву.
|
|
25
|
+
*/
|
|
26
|
+
export var useQueryFilter = function useQueryFilter(name, initialValue, fromString, toString) {
|
|
27
|
+
var _searchParams$get;
|
|
28
|
+
var isResetPageWhenChanged = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
|
|
29
|
+
var _useQueryFilterContex = useQueryFilterContext(),
|
|
30
|
+
searchParams = _useQueryFilterContex.searchParams,
|
|
31
|
+
setSearchParams = _useQueryFilterContex.setSearchParams,
|
|
32
|
+
pageParamName = _useQueryFilterContex.pageParamName,
|
|
33
|
+
firstPage = _useQueryFilterContex.firstPage;
|
|
34
|
+
|
|
35
|
+
// для тех случаев когда если initialValue и в случае когда он не успел вставить в квери параметры - возвращать его
|
|
36
|
+
var _useState = useState(false),
|
|
37
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
38
|
+
acceptInitialValueDone = _useState2[0],
|
|
39
|
+
setAcceptInitialValueDone = _useState2[1];
|
|
40
|
+
var serialize = useCallback(function (value) {
|
|
41
|
+
if (getIsEmptyValue(value)) return '';
|
|
42
|
+
return toString ? toString(value) : String(value);
|
|
43
|
+
}, [toString]);
|
|
44
|
+
var removeAttribute = useCallback(function (isResetPage) {
|
|
45
|
+
if (searchParams.has(name)) {
|
|
46
|
+
searchParams["delete"](name);
|
|
47
|
+
if (isResetPage) {
|
|
48
|
+
searchParams.set(pageParamName, String(firstPage));
|
|
49
|
+
}
|
|
50
|
+
setSearchParams(searchParams);
|
|
51
|
+
}
|
|
52
|
+
}, [firstPage, name, pageParamName, searchParams, setSearchParams]);
|
|
53
|
+
var handleQueryChange = useCallback(function (newValue) {
|
|
54
|
+
if (getIsEmptyValue(newValue)) {
|
|
55
|
+
removeAttribute(isResetPageWhenChanged);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
searchParams.set(name, serialize(newValue));
|
|
59
|
+
if (isResetPageWhenChanged) {
|
|
60
|
+
searchParams.set(pageParamName, String(firstPage));
|
|
61
|
+
}
|
|
62
|
+
setSearchParams(searchParams);
|
|
63
|
+
}, [firstPage, isResetPageWhenChanged, name, pageParamName, removeAttribute, searchParams, serialize, setSearchParams]);
|
|
64
|
+
var acceptInitialValue = useCallback(function (initial) {
|
|
65
|
+
if (!searchParams.has(name)) {
|
|
66
|
+
if (getIsEmptyValue(initial)) {
|
|
67
|
+
removeAttribute(true);
|
|
68
|
+
setAcceptInitialValueDone(true);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
searchParams.set(name, serialize(initial));
|
|
72
|
+
setSearchParams(searchParams);
|
|
73
|
+
}
|
|
74
|
+
setAcceptInitialValueDone(true);
|
|
75
|
+
}, [name, removeAttribute, searchParams, serialize, setSearchParams]);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Начальное значение, стабилизированное по его строковому представлению.
|
|
79
|
+
*
|
|
80
|
+
* Вызывающие часто передают начальное значение выражением (`monthStart()`, `[]`,
|
|
81
|
+
* `new Date()`), то есть новым объектом на каждый рендер. Если зависеть от самой
|
|
82
|
+
* ссылки, эффект ниже срабатывает на каждом рендере (и возвращает в адрес значение
|
|
83
|
+
* по умолчанию, которое пользователь только что сбросил), а значение фильтра каждый
|
|
84
|
+
* раз получает новую ссылку — эффект с запросом перезапускается без конца. Поэтому
|
|
85
|
+
* ссылка меняется только когда меняется строка, которую значение даёт в адресе.
|
|
86
|
+
*/
|
|
87
|
+
var initialKey = serialize(initialValue);
|
|
88
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
89
|
+
var stableInitialValue = useMemo(function () {
|
|
90
|
+
return initialValue;
|
|
91
|
+
}, [initialKey]);
|
|
92
|
+
useEffect(function () {
|
|
93
|
+
acceptInitialValue(stableInitialValue);
|
|
94
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
95
|
+
}, [stableInitialValue]);
|
|
96
|
+
var handleGetValueFromString = useCallback(function (queryValue) {
|
|
97
|
+
if (fromString) {
|
|
98
|
+
return fromString(queryValue);
|
|
99
|
+
}
|
|
100
|
+
return queryValue || '';
|
|
101
|
+
}, [fromString]);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Строку читаем на каждом рендере, а не кешируем по объекту `searchParams`: эффект
|
|
105
|
+
* начального значения дописывает параметр прямо в текущий объект, а навигация
|
|
106
|
+
* (react-router 7 — переходом с низким приоритетом) приходит позже. Между ними есть
|
|
107
|
+
* рендер, где объект прежний, но строка в нём уже новая, — по ней и надо считать,
|
|
108
|
+
* иначе фильтр на один рендер «пустеет» и уходит лишний запрос.
|
|
109
|
+
*
|
|
110
|
+
* Пока строка в адресе совпадает с начальной, отдаём сам `stableInitialValue`, а не
|
|
111
|
+
* результат разбора: иначе `new Date(...)` из разборщика дал бы новую ссылку при тех
|
|
112
|
+
* же данных и эффект с запросом сработал бы ещё раз.
|
|
113
|
+
*/
|
|
114
|
+
var rawQueryValue = (_searchParams$get = searchParams.get(name)) !== null && _searchParams$get !== void 0 ? _searchParams$get : '';
|
|
115
|
+
var isInitialNotAppliedYet = !acceptInitialValueDone && !searchParams.has(name);
|
|
116
|
+
var formattedValue = useMemo(function () {
|
|
117
|
+
if (isInitialNotAppliedYet || rawQueryValue === initialKey) {
|
|
118
|
+
return stableInitialValue;
|
|
119
|
+
}
|
|
120
|
+
return handleGetValueFromString(rawQueryValue || null);
|
|
121
|
+
}, [handleGetValueFromString, initialKey, isInitialNotAppliedYet, rawQueryValue, stableInitialValue]);
|
|
122
|
+
return [formattedValue, handleQueryChange];
|
|
123
|
+
};
|