@sima-land/ui-nucleons 40.2.0 → 41.0.0
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/_internal/custom-scrollbar/index.js +3 -3
- package/alert/index.d.ts +2 -0
- package/alert/index.js +3 -3
- package/arrow-button/index.d.ts +1 -0
- package/autocomplete/index.js +12 -17
- package/avatar/index.d.ts +1 -1
- package/avatar/index.js +2 -4
- package/avatar/user.d.ts +2 -1
- package/avatar/user.js +2 -2
- package/avatar/utils.js +7 -1
- package/base-input/index.d.ts +1 -1
- package/base-input/index.js +1 -1
- package/bordered-layout/index.d.ts +1 -1
- package/bordered-layout/index.js +1 -1
- package/box/index.js +17 -36
- package/button/index.d.ts +3 -3
- package/button/index.js +4 -4
- package/card/index.d.ts +1 -0
- package/card/slots.js +1 -1
- package/carousel/draggable.js +14 -16
- package/carousel/index.d.ts +1 -1
- package/carousel/index.js +17 -37
- package/chips/index.d.ts +5 -14
- package/chips/index.js +24 -9
- package/chips/item.d.ts +15 -0
- package/chips/item.js +20 -0
- package/clean-buttons/index.js +1 -3
- package/dot-nav/dot-nav.module.scss +2 -2
- package/dot-nav/index.d.ts +1 -0
- package/dropdown/dropdown.module.scss +1 -3
- package/field-grid/index.d.ts +2 -2
- package/field-grid/index.js +3 -3
- package/file-icon/index.js +5 -6
- package/helpers/bounds-of.js +1 -3
- package/helpers/find-child-element.js +1 -3
- package/helpers/find-offset-parent.js +4 -3
- package/helpers/fit-element-height.js +1 -1
- package/helpers/format-date.js +3 -1
- package/helpers/get-fraction-depth.js +1 -3
- package/hooks/identity.d.ts +1 -0
- package/hooks/identity.js +1 -0
- package/hooks/index.d.ts +2 -1
- package/hooks/index.js +9 -7
- package/layout/index.js +1 -1
- package/link/index.js +1 -3
- package/loading-overlay/index.d.ts +1 -1
- package/loading-overlay/index.js +1 -1
- package/nav-bar/index.d.ts +1 -0
- package/nav-bar/index.js +1 -1
- package/no-index-mark/index.d.ts +1 -0
- package/package.json +54 -51
- package/pagination/base-pagination.js +18 -20
- package/pagination/index.js +1 -3
- package/pagination/utils.d.ts +3 -3
- package/pagination/utils.js +7 -6
- package/panel/index.js +1 -3
- package/phone-input/index.js +19 -20
- package/price/index.d.ts +1 -0
- package/price/utils.js +1 -3
- package/range/index.js +11 -14
- package/rating/index.js +2 -2
- package/readme.md +7 -7
- package/screen/index.d.ts +12 -47
- package/screen/index.js +30 -67
- package/screen/screen.module.scss +16 -15
- package/screen/slots.d.ts +32 -0
- package/screen/slots.js +64 -0
- package/screen/utils.d.ts +1 -2
- package/select/index.js +5 -7
- package/side-page/index.d.ts +15 -2
- package/side-page/index.js +2 -2
- package/spinner/index.d.ts +1 -1
- package/spinner/index.js +1 -1
- package/spinner/spinner.module.scss +1 -4
- package/styling/fonts.js +1 -3
- package/styling/utils.js +1 -3
- package/super-ellipse-clip-path/index.d.ts +1 -0
- package/tabs/index.d.ts +32 -25
- package/tabs/index.js +30 -24
- package/tabs/tabs.module.scss +21 -19
- package/text-field/index.js +3 -10
- package/timer/index.d.ts +3 -3
- package/timer/index.js +4 -4
- package/timer/utils.js +1 -4
- package/top-bar/index.js +1 -1
- package/unknown-content/index.d.ts +4 -2
- package/unknown-content/index.js +28 -2
- package/unknown-content/unknown-content.module.scss +214 -120
- package/unknown-content/utils.d.ts +6 -0
- package/unknown-content/utils.js +18 -0
- package/upload/area.js +3 -1
- package/upload/utils.d.ts +2 -2
- package/upload/utils.js +2 -2
- package/with-hint/positioning-hint.d.ts +1 -1
- package/with-hint/positioning-hint.js +1 -1
- package/with-hint/utils.js +5 -7
- package/with-in-viewport-observer/index.js +1 -1
- package/screen/screen-layout.d.ts +0 -57
- package/screen/screen-layout.js +0 -110
package/pagination/index.js
CHANGED
|
@@ -12,8 +12,6 @@ const base_pagination_1 = require("./base-pagination");
|
|
|
12
12
|
* @return Элемент.
|
|
13
13
|
*/
|
|
14
14
|
const Pagination = ({ total, current, onChange, renderButton, }) => (react_1.default.createElement(base_pagination_1.BasePagination, { total: total, current: current, renderButton: renderButton, onButtonClick: content => {
|
|
15
|
-
Number.isFinite(content)
|
|
16
|
-
&& onChange
|
|
17
|
-
&& onChange({ value: content });
|
|
15
|
+
Number.isFinite(content) && onChange && onChange({ value: content });
|
|
18
16
|
} }));
|
|
19
17
|
exports.Pagination = Pagination;
|
package/pagination/utils.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ declare type State = {
|
|
|
7
7
|
range: number;
|
|
8
8
|
total: number;
|
|
9
9
|
};
|
|
10
|
-
export declare const cx: import("classnames
|
|
10
|
+
export declare const cx: (...args: import("classnames").Argument[]) => string;
|
|
11
11
|
/**
|
|
12
12
|
* Фабрика объектов с данными о кнопке страницы.
|
|
13
13
|
* @param value Номер целевой страницы.
|
|
@@ -41,7 +41,7 @@ export declare const getPageButtons: ({ current, total, buttonKeys, }: {
|
|
|
41
41
|
* @param props.total Максимальное значение.
|
|
42
42
|
* @return Список номеров.
|
|
43
43
|
*/
|
|
44
|
-
export declare const getCorrectRangeNumbers: ({ current, range: rangeSize, total
|
|
44
|
+
export declare const getCorrectRangeNumbers: ({ current, range: rangeSize, total }: State) => number[];
|
|
45
45
|
/**
|
|
46
46
|
* Возвращает список чисел вокруг заданного.
|
|
47
47
|
* @param props Свойства.
|
|
@@ -50,5 +50,5 @@ export declare const getCorrectRangeNumbers: ({ current, range: rangeSize, total
|
|
|
50
50
|
* @param props.total Максимальное значение.
|
|
51
51
|
* @return Список номеров.
|
|
52
52
|
*/
|
|
53
|
-
export declare const getRangeNumbers: ({ current, range: rangeSize, total
|
|
53
|
+
export declare const getRangeNumbers: ({ current, range: rangeSize, total }: State) => number[];
|
|
54
54
|
export {};
|
package/pagination/utils.js
CHANGED
|
@@ -14,7 +14,10 @@ exports.cx = bind_1.default.bind(pagination_module_scss_1.default);
|
|
|
14
14
|
* @param content Содержимое кнопки.
|
|
15
15
|
* @return Список кнопок.
|
|
16
16
|
*/
|
|
17
|
-
const PageButton = (value, content = value) => ({
|
|
17
|
+
const PageButton = (value, content = value) => ({
|
|
18
|
+
value,
|
|
19
|
+
content,
|
|
20
|
+
});
|
|
18
21
|
exports.PageButton = PageButton;
|
|
19
22
|
/**
|
|
20
23
|
* Возвращает список кнопок навигации.
|
|
@@ -40,9 +43,7 @@ const getPageButtons = ({ current, total, buttonKeys = { more: '...' }, }) => {
|
|
|
40
43
|
else {
|
|
41
44
|
list.unshift((0, exports.PageButton)(1),
|
|
42
45
|
// не выводим многоточие когда вместо него можно поставить одно число
|
|
43
|
-
list[0].value === 3
|
|
44
|
-
? (0, exports.PageButton)(2)
|
|
45
|
-
: (0, exports.PageButton)(list[0].value - 1, buttonKeys.more));
|
|
46
|
+
list[0].value === 3 ? (0, exports.PageButton)(2) : (0, exports.PageButton)(list[0].value - 1, buttonKeys.more));
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
if (list[list.length - 1].value < total) {
|
|
@@ -72,7 +73,7 @@ exports.getPageButtons = getPageButtons;
|
|
|
72
73
|
* @param props.total Максимальное значение.
|
|
73
74
|
* @return Список номеров.
|
|
74
75
|
*/
|
|
75
|
-
const getCorrectRangeNumbers = ({ current, range: rangeSize, total
|
|
76
|
+
const getCorrectRangeNumbers = ({ current, range: rangeSize, total }) => {
|
|
76
77
|
let result = [
|
|
77
78
|
...(0, exports.getRangeNumbers)({
|
|
78
79
|
current,
|
|
@@ -99,7 +100,7 @@ exports.getCorrectRangeNumbers = getCorrectRangeNumbers;
|
|
|
99
100
|
* @param props.total Максимальное значение.
|
|
100
101
|
* @return Список номеров.
|
|
101
102
|
*/
|
|
102
|
-
const getRangeNumbers = ({ current, range: rangeSize, total
|
|
103
|
+
const getRangeNumbers = ({ current, range: rangeSize, total }) => {
|
|
103
104
|
const pageNumbers = [];
|
|
104
105
|
const readyRange = Math.min(rangeSize, total) || 0;
|
|
105
106
|
const halfRange = Math.floor(readyRange / 2);
|
package/panel/index.js
CHANGED
|
@@ -16,9 +16,7 @@ const cx = bind_1.default.bind(panel_module_scss_1.default);
|
|
|
16
16
|
* @return Элемент.
|
|
17
17
|
*/
|
|
18
18
|
const Panel = ({ color: panelColor = 'gray4', contentColor = 'gray87', inline = false, icon: Icon, className, children, html, 'data-testid': testId = 'panel', }) => {
|
|
19
|
-
const contentProps = html
|
|
20
|
-
? { dangerouslySetInnerHTML: { __html: html } }
|
|
21
|
-
: { children };
|
|
19
|
+
const contentProps = html ? { dangerouslySetInnerHTML: { __html: html } } : { children };
|
|
22
20
|
return (react_1.default.createElement("div", { className: cx('panel', inline && 'inline', (0, colors_1.bgColor)(panelColor), className), "data-testid": testId },
|
|
23
21
|
Icon && (react_1.default.createElement(Icon, { fill: colors_2.COLORS.get(contentColor), width: 16, height: 20, className: cx('icon'), "aria-hidden": 'true' })),
|
|
24
22
|
react_1.default.createElement("div", Object.assign({}, contentProps, { className: cx('content', (0, colors_1.color)(contentColor)) }))));
|
package/phone-input/index.js
CHANGED
|
@@ -83,32 +83,31 @@ const PhoneInput = (_a) => {
|
|
|
83
83
|
react_1.default.createElement("img", { alt: '', width: 24, height: 24, src: country.imageSrc }),
|
|
84
84
|
react_1.default.createElement(AdornmentSVG, { fill: colors_1.COLORS.get('gray38'), className: (0, sizes_1.marginLeft)(1) }))) });
|
|
85
85
|
return (react_1.default.createElement("div", { "data-testid": testId, style: style, className: cx('root', className) },
|
|
86
|
-
country.id === presets_1.IDS.other
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
onBlur
|
|
86
|
+
country.id === presets_1.IDS.other ? (react_1.default.createElement(text_field_1.TextField, Object.assign({}, commonFieldProps, { onChange: (e) => {
|
|
87
|
+
e.target.value = e.target.value.replace(/\D/g, '').slice(0, 20);
|
|
88
|
+
onChange && onChange(e);
|
|
89
|
+
}, onBlur: e => {
|
|
90
|
+
onBlur &&
|
|
91
|
+
onBlur(e, {
|
|
92
92
|
value: e.target.value,
|
|
93
93
|
cleanValue: e.target.value,
|
|
94
94
|
ready: e.target.value.length > 0,
|
|
95
95
|
});
|
|
96
|
-
|
|
96
|
+
} }))) : (react_1.default.createElement(masked_field_1.MaskedField, Object.assign({}, commonFieldProps, {
|
|
97
97
|
// убираем код страны, так как он уже зашит в маску
|
|
98
98
|
value: cleanValue, mask: country.mask, onBlur: (event, state) => {
|
|
99
|
-
onBlur &&
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
onBlur &&
|
|
100
|
+
onBlur(event, Object.assign(Object.assign({}, state), {
|
|
101
|
+
// вставляем код страны обратно в чистое значение
|
|
102
|
+
cleanValue: `${country.codeChars}${state.cleanValue}` }));
|
|
102
103
|
} }))),
|
|
103
|
-
isPopupOpen && (react_1.default.createElement(dropdown_1.Dropdown, { ref: dropdownRef, role: 'menu', className: cx('popup'), "data-testid": 'phone-input:dropdown' }, presets_1.countriesList.map((item, index) => item.id === country.id
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
react_1.default.createElement("span", { className: cx('item-name') }, item.name),
|
|
112
|
-
react_1.default.createElement("span", { className: cx('item-code') }, item.code))))))));
|
|
104
|
+
isPopupOpen && (react_1.default.createElement(dropdown_1.Dropdown, { ref: dropdownRef, role: 'menu', className: cx('popup'), "data-testid": 'phone-input:dropdown' }, presets_1.countriesList.map((item, index) => item.id === country.id ? null : (react_1.default.createElement(dropdown_item_1.DropdownItem, { key: index, size: 'l', role: 'menuitem', className: cx('popup-item'), onClick: () => {
|
|
105
|
+
togglePopup(false);
|
|
106
|
+
setCountry(item);
|
|
107
|
+
onCountrySelect && onCountrySelect(item);
|
|
108
|
+
} },
|
|
109
|
+
react_1.default.createElement("img", { alt: '', width: 24, height: 24, src: item.imageSrc }),
|
|
110
|
+
react_1.default.createElement("span", { className: cx('item-name') }, item.name),
|
|
111
|
+
react_1.default.createElement("span", { className: cx('item-code') }, item.code))))))));
|
|
113
112
|
};
|
|
114
113
|
exports.PhoneInput = PhoneInput;
|
package/price/index.d.ts
CHANGED
package/price/utils.js
CHANGED
|
@@ -16,9 +16,7 @@ const formatPrice = (value, grapheme, { graphemeBefore } = {}) => {
|
|
|
16
16
|
// целая часть
|
|
17
17
|
(0, exports.formatInteger)(number),
|
|
18
18
|
// дробная часть
|
|
19
|
-
Number.isFinite(number) &&
|
|
20
|
-
!Number.isInteger(number) &&
|
|
21
|
-
`,${(0, exports.formatFractional)(number)}`,
|
|
19
|
+
Number.isFinite(number) && !Number.isInteger(number) && `,${(0, exports.formatFractional)(number)}`,
|
|
22
20
|
// графема после
|
|
23
21
|
grapheme && !graphemeBefore && ` ${grapheme}`,
|
|
24
22
|
]
|
package/range/index.js
CHANGED
|
@@ -42,7 +42,7 @@ const cx = bind_1.default.bind(range_module_scss_1.default);
|
|
|
42
42
|
* @param value Значение для расчета доли.
|
|
43
43
|
* @return Доля, которую занимает значение.
|
|
44
44
|
*/
|
|
45
|
-
const getFraction = (start, end, value) => (
|
|
45
|
+
const getFraction = (start, end, value) => (value - start) / (end - start) || 0;
|
|
46
46
|
/**
|
|
47
47
|
* Возвращает процент от диапазона, заданную числом.
|
|
48
48
|
* @param args Данные для расчета доли.
|
|
@@ -58,7 +58,7 @@ class Range extends react_1.Component {
|
|
|
58
58
|
* @param props Свойства.
|
|
59
59
|
*/
|
|
60
60
|
constructor(props) {
|
|
61
|
-
const { min = 0, max = 1, startValue = min, finishValue = max
|
|
61
|
+
const { min = 0, max = 1, startValue = min, finishValue = max } = props;
|
|
62
62
|
super(props);
|
|
63
63
|
// конечное значение имеет приоритет - делаем стартовое зависимым от конечного
|
|
64
64
|
const readyStart = this.constrainValue(Math.min(startValue, finishValue));
|
|
@@ -93,7 +93,7 @@ class Range extends react_1.Component {
|
|
|
93
93
|
componentDidUpdate(prevProps) {
|
|
94
94
|
const propKeys = ['min', 'max', 'step', 'startValue', 'finishValue'];
|
|
95
95
|
if (propKeys.some(key => !Object.is(this.props[key], prevProps[key]))) {
|
|
96
|
-
const { startValue = this.start.get(), finishValue = this.finish.get()
|
|
96
|
+
const { startValue = this.start.get(), finishValue = this.finish.get() } = this.props;
|
|
97
97
|
// конечное значение имеет приоритет - делаем стартовое зависимым от конечного
|
|
98
98
|
const readyStart = this.constrainValue(Math.min(startValue, finishValue));
|
|
99
99
|
const readyFinish = this.constrainValue(finishValue);
|
|
@@ -171,11 +171,9 @@ class Range extends react_1.Component {
|
|
|
171
171
|
const { left, right } = (0, bounds_of_1.default)(this.containerRef.current);
|
|
172
172
|
const wrappedX = Math.max(start, Math.min((0, events_1.getEventClientPos)(event).x, end));
|
|
173
173
|
const fraction = getFraction(left, right, wrappedX);
|
|
174
|
-
const value = this.constrainValue(min +
|
|
174
|
+
const value = this.constrainValue(min + fraction * (max - min));
|
|
175
175
|
// проверяем, является ползунок стартовым или конечным
|
|
176
|
-
thumbEl === this.startThumbRef.current
|
|
177
|
-
? this.start.set(value)
|
|
178
|
-
: this.finish.set(value);
|
|
176
|
+
thumbEl === this.startThumbRef.current ? this.start.set(value) : this.finish.set(value);
|
|
179
177
|
}
|
|
180
178
|
}
|
|
181
179
|
/**
|
|
@@ -233,9 +231,7 @@ class Range extends react_1.Component {
|
|
|
233
231
|
else {
|
|
234
232
|
isStartActive = abs(startX - clientX) < abs(finishX - clientX);
|
|
235
233
|
}
|
|
236
|
-
this.activeThumbEl = (isStartActive
|
|
237
|
-
? this.startThumbRef.current
|
|
238
|
-
: this.finishThumbRef.current);
|
|
234
|
+
this.activeThumbEl = (isStartActive ? this.startThumbRef.current : this.finishThumbRef.current);
|
|
239
235
|
this.activeBounds = isStartActive
|
|
240
236
|
? [(0, bounds_of_1.default)(this.containerRef.current).left, finishX]
|
|
241
237
|
: [startX, (0, bounds_of_1.default)(this.containerRef.current).right];
|
|
@@ -262,10 +258,11 @@ class Range extends react_1.Component {
|
|
|
262
258
|
handleEvent = onSlide;
|
|
263
259
|
break;
|
|
264
260
|
}
|
|
265
|
-
handleEvent &&
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
261
|
+
handleEvent &&
|
|
262
|
+
handleEvent({
|
|
263
|
+
startValue: this.start.get(),
|
|
264
|
+
finishValue: this.finish.get(),
|
|
265
|
+
});
|
|
269
266
|
}
|
|
270
267
|
/** @inheritdoc */
|
|
271
268
|
render() {
|
package/rating/index.js
CHANGED
|
@@ -37,8 +37,8 @@ exports.Rating = Rating;
|
|
|
37
37
|
* @return Элемент.
|
|
38
38
|
*/
|
|
39
39
|
const Star = ({ type, size = 's' }) => (react_1.default.createElement("div", { className: cx('star', `size-${size}`) },
|
|
40
|
-
type === 'empty' &&
|
|
40
|
+
type === 'empty' && react_1.default.createElement(star_svg_1.default, { className: cx('svg') }),
|
|
41
41
|
type === 'half' && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
42
42
|
react_1.default.createElement(star_svg_1.default, { className: cx('svg') }),
|
|
43
43
|
react_1.default.createElement(star_svg_1.default, { className: cx('svg', 'half'), preserveAspectRatio: 'xMinYMin slice' }))),
|
|
44
|
-
type === 'full' &&
|
|
44
|
+
type === 'full' && react_1.default.createElement(star_svg_1.default, { className: cx('svg', 'full') })));
|
package/readme.md
CHANGED
|
@@ -145,7 +145,7 @@ declare module '*.module.scss' {
|
|
|
145
145
|
|
|
146
146
|
// svg как React-компоненты
|
|
147
147
|
declare module '*.svg' {
|
|
148
|
-
const content: React.
|
|
148
|
+
const content: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
|
149
149
|
export default content;
|
|
150
150
|
}
|
|
151
151
|
```
|
|
@@ -158,12 +158,12 @@ declare module '*.svg' {
|
|
|
158
158
|
- компонент интерфейса не должен иметь внешних отступов по умолчанию
|
|
159
159
|
|
|
160
160
|
### Консольные команды, необходимые в процессе разработки
|
|
161
|
-
* ```
|
|
162
|
-
* ```
|
|
163
|
-
* ```
|
|
164
|
-
* ```
|
|
165
|
-
* ```
|
|
166
|
-
* ```
|
|
161
|
+
* ```npm run build``` - сборка компонентов в `./build` для публикации
|
|
162
|
+
* ```npm run test``` - запуск тестов *Jest*
|
|
163
|
+
* ```npm run lint:scripts``` - запуск линтера *ESLint*
|
|
164
|
+
* ```npm run lint:styles``` - запуск линтера *styleLint*
|
|
165
|
+
* ```npm run type-check``` - Проверка типов TS
|
|
166
|
+
* ```npm run storybook``` - запуск *storybook*
|
|
167
167
|
|
|
168
168
|
### Структура библиотеки
|
|
169
169
|
|
package/screen/index.d.ts
CHANGED
|
@@ -1,64 +1,29 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ScreenLayoutProps, CallbackData } from './screen-layout';
|
|
3
|
-
import { OrNil } from './utils';
|
|
4
2
|
import { LoadingOverlayProps } from '../loading-overlay';
|
|
5
|
-
interface AdvancedCallbackData extends CallbackData {
|
|
6
|
-
rootElement: OrNil<HTMLDivElement>;
|
|
7
|
-
}
|
|
8
3
|
export interface ScreenProps {
|
|
9
4
|
/** Содержимое. */
|
|
10
5
|
children?: React.ReactNode;
|
|
11
|
-
/** Реф контента. */
|
|
12
|
-
contentRef?: ScreenLayoutProps['childrenRef'];
|
|
13
|
-
/** Содержимое подвала. */
|
|
14
|
-
footer?: any;
|
|
15
|
-
/** Отступ от нижней границы для срабатывания onFullScroll. */
|
|
16
|
-
fullScrollThreshold?: number;
|
|
17
6
|
/** Нужно ли выводить вместо содержимого состояние загрузки. */
|
|
18
7
|
loading?: boolean;
|
|
19
8
|
/** Определяет область отображаемую как загружающуюся. */
|
|
20
9
|
loadingArea?: 'content' | 'full';
|
|
21
10
|
/** Свойства компонента LoadingOverlay. */
|
|
22
11
|
loadingOverlayProps?: LoadingOverlayProps;
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
/** Сработает при полной прокрутке контента. */
|
|
30
|
-
onFullScroll?: ScreenLayoutProps['onFullScroll'];
|
|
31
|
-
/** Подзаголовок. */
|
|
32
|
-
subtitle?: string;
|
|
33
|
-
/** Заголовок. */
|
|
34
|
-
title?: string;
|
|
35
|
-
/** Нужно ли выводить кнопку "назад". */
|
|
36
|
-
withBackButton?: boolean;
|
|
37
|
-
/** Нужно ли выводить закрывающий крест. */
|
|
38
|
-
withCloseButton?: boolean;
|
|
39
|
-
/** Нужно ли рисовать черту между шапкой и основным содержимым. */
|
|
40
|
-
withDivideHeader?: boolean;
|
|
41
|
-
/** Нужно ли выводить кнопку "назад". */
|
|
42
|
-
withHeader?: boolean;
|
|
43
|
-
/** Нужно ли выводить Layer (при SSR необходимо указать false). */
|
|
44
|
-
inPortal?: boolean;
|
|
12
|
+
/** Будет вызвана при полной прокрутке содержимого экрана. */
|
|
13
|
+
onFullScroll?: () => void;
|
|
14
|
+
/** Отступ от нижней границы содержимого экрана, после которого начинает срабатывать onFullScroll. */
|
|
15
|
+
fullScrollThreshold?: number;
|
|
16
|
+
/** Идентификатор для систем автоматизированного тестирования. */
|
|
17
|
+
'data-testid'?: string;
|
|
45
18
|
}
|
|
46
19
|
/**
|
|
47
20
|
* Экран.
|
|
48
21
|
* @param props Свойства.
|
|
49
22
|
* @return Элемент.
|
|
50
23
|
*/
|
|
51
|
-
export declare const Screen:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
export declare const setRefValue: (ref: React.RefCallback<any> | React.MutableRefObject<any>, value: any) => void;
|
|
58
|
-
/**
|
|
59
|
-
* Записывает полученный элемент в ref и отключает прокрутку.
|
|
60
|
-
* @param ref Ref-контейнер.
|
|
61
|
-
* @param element Элемент для записи.
|
|
62
|
-
*/
|
|
63
|
-
export declare const takeScrollableElement: (ref: React.MutableRefObject<OrNil<HTMLDivElement>>, element?: HTMLDivElement | null | undefined) => void;
|
|
64
|
-
export {};
|
|
24
|
+
export declare const Screen: {
|
|
25
|
+
({ children, loading, loadingArea, loadingOverlayProps, onFullScroll, fullScrollThreshold, "data-testid": testId, }: ScreenProps): JSX.Element;
|
|
26
|
+
Header: ({ divided, title, subtitle, buttons, onBack, onClose, ...restProps }: import("./slots").HeaderSlotProps) => JSX.Element;
|
|
27
|
+
Body: React.FC<{}>;
|
|
28
|
+
Footer: React.FC<{}>;
|
|
29
|
+
};
|
package/screen/index.js
CHANGED
|
@@ -18,82 +18,45 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
18
18
|
__setModuleDefault(result, mod);
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
21
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.
|
|
25
|
+
exports.Screen = void 0;
|
|
23
26
|
const react_1 = __importStar(require("react"));
|
|
24
|
-
const
|
|
25
|
-
const lodash_1 = require("lodash");
|
|
26
|
-
const screen_layout_1 = require("./screen-layout");
|
|
27
|
-
const utils_1 = require("./utils");
|
|
27
|
+
const slots_1 = require("./slots");
|
|
28
28
|
const loading_overlay_1 = require("../loading-overlay");
|
|
29
|
-
const
|
|
29
|
+
const body_scroll_1 = require("../_internal/body-scroll");
|
|
30
30
|
const layer_1 = require("../helpers/layer");
|
|
31
|
+
const define_slots_1 = require("../helpers/define-slots");
|
|
32
|
+
const hooks_1 = require("../hooks");
|
|
33
|
+
const screen_module_scss_1 = __importDefault(require("./screen.module.scss"));
|
|
31
34
|
/**
|
|
32
35
|
* Экран.
|
|
33
36
|
* @param props Свойства.
|
|
34
37
|
* @return Элемент.
|
|
35
38
|
*/
|
|
36
|
-
const Screen = ({ children,
|
|
37
|
-
const
|
|
39
|
+
const Screen = ({ children, loading, loadingArea = 'full', loadingOverlayProps, onFullScroll, fullScrollThreshold = 320, 'data-testid': testId = 'screen', }) => {
|
|
40
|
+
const fullLoading = loading && loadingArea === 'full';
|
|
38
41
|
const layer = (0, layer_1.useLayer)() + 300; // 300 из-за монолита
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
(0, exports.setRefValue)(contentRef, element);
|
|
56
|
-
(0, exports.takeScrollableElement)(innerContentRef, element);
|
|
57
|
-
}, footer: footer, onBack: ({ contentElement }) => {
|
|
58
|
-
onBack && onBack({
|
|
59
|
-
rootElement: rootRef.current,
|
|
60
|
-
contentElement,
|
|
61
|
-
});
|
|
62
|
-
}, onClose: ({ contentElement }) => {
|
|
63
|
-
onClose && onClose({
|
|
64
|
-
rootElement: rootRef.current,
|
|
65
|
-
contentElement,
|
|
66
|
-
});
|
|
67
|
-
}, onFullScroll: onFullScroll, fullScrollThreshold: fullScrollThreshold, navBarProps: navBarProps }))))));
|
|
42
|
+
const { header, body, footer } = (0, define_slots_1.defineSlots)(children, {
|
|
43
|
+
header: slots_1.HeaderSlot,
|
|
44
|
+
body: slots_1.BodySlot,
|
|
45
|
+
footer: slots_1.FooterSlot,
|
|
46
|
+
});
|
|
47
|
+
// при блокировке прокрутки нужно передавать элемент которому нужно сохранить возможность прокрутки
|
|
48
|
+
// https://github.com/willmcpo/body-scroll-lock/blame/79c4cf2c956eb7d5cf8d54a03d12751bc6ac8aa3/README.md#L52
|
|
49
|
+
// поэтому используем внутренний ref и useImperativeHandle
|
|
50
|
+
const bodyRef = (0, react_1.useRef)(null);
|
|
51
|
+
(0, body_scroll_1.useBodyScrollLock)(bodyRef, true);
|
|
52
|
+
(0, hooks_1.useInfiniteScroll)(bodyRef, { onFullScroll, threshold: fullScrollThreshold });
|
|
53
|
+
return (react_1.default.createElement("div", { className: screen_module_scss_1.default.root, style: { zIndex: layer }, "data-testid": testId },
|
|
54
|
+
react_1.default.createElement(layer_1.LayerProvider, { value: layer },
|
|
55
|
+
!fullLoading && header,
|
|
56
|
+
react_1.default.createElement("div", { ref: bodyRef, className: screen_module_scss_1.default.body, "data-testid": 'screen:body' }, loading ? (react_1.default.createElement(loading_overlay_1.LoadingOverlay, Object.assign({}, loadingOverlayProps, { fill: false, style: { height: '100%' } }))) : (body)),
|
|
57
|
+
!fullLoading && footer && (react_1.default.createElement("div", { className: screen_module_scss_1.default.footer, "data-testid": 'screen:footer' }, footer)))));
|
|
68
58
|
};
|
|
69
59
|
exports.Screen = Screen;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
* @param value Значение для записи.
|
|
74
|
-
*/
|
|
75
|
-
const setRefValue = (ref, value) => {
|
|
76
|
-
if ((0, lodash_1.isFunction)(ref)) {
|
|
77
|
-
ref(value);
|
|
78
|
-
}
|
|
79
|
-
else if (ref) {
|
|
80
|
-
ref.current = value;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
exports.setRefValue = setRefValue;
|
|
84
|
-
/**
|
|
85
|
-
* Записывает полученный элемент в ref и отключает прокрутку.
|
|
86
|
-
* @param ref Ref-контейнер.
|
|
87
|
-
* @param element Элемент для записи.
|
|
88
|
-
*/
|
|
89
|
-
const takeScrollableElement = (ref, element) => {
|
|
90
|
-
if (element && element !== ref.current) {
|
|
91
|
-
// если элемент изменился - включаем прокрутку для старого
|
|
92
|
-
ref.current && (0, body_scroll_lock_1.enableBodyScroll)(ref.current);
|
|
93
|
-
// сохраняем новый элемент
|
|
94
|
-
ref.current = element;
|
|
95
|
-
// отключаем прокрутку для нового элемента
|
|
96
|
-
(0, body_scroll_lock_1.disableBodyScroll)(element);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
exports.takeScrollableElement = takeScrollableElement;
|
|
60
|
+
exports.Screen.Header = slots_1.HeaderSlot;
|
|
61
|
+
exports.Screen.Body = slots_1.BodySlot;
|
|
62
|
+
exports.Screen.Footer = slots_1.FooterSlot;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.
|
|
1
|
+
.root {
|
|
2
2
|
position: fixed;
|
|
3
3
|
|
|
4
4
|
// для корректного отображения с динамическим viewport переменная --offsetTop должна быть доступна
|
|
@@ -9,24 +9,25 @@
|
|
|
9
9
|
|
|
10
10
|
// для корректного отображения с динамическим viewport переменная --vh должна быть доступна
|
|
11
11
|
height: calc(var(--vh, 1vh) * 100);
|
|
12
|
+
width: 100%;
|
|
12
13
|
box-sizing: border-box;
|
|
13
14
|
background-color: #fff;
|
|
14
15
|
outline: 100px solid #fff;
|
|
15
|
-
.content {
|
|
16
|
-
height: 0;
|
|
17
|
-
overflow-y: auto;
|
|
18
|
-
flex-grow: 1;
|
|
19
|
-
flex-shrink: 0;
|
|
20
|
-
-webkit-overflow-scrolling: touch;
|
|
21
|
-
}
|
|
22
|
-
.footer {
|
|
23
|
-
min-height: 56px;
|
|
24
|
-
flex-grow: 0;
|
|
25
|
-
flex-shrink: 0;
|
|
26
|
-
box-shadow: inset 0 1px 0 #e0e0e0;
|
|
27
|
-
}
|
|
28
16
|
}
|
|
29
17
|
|
|
30
|
-
.
|
|
18
|
+
.body {
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 0;
|
|
21
|
+
overflow-y: auto;
|
|
22
|
+
flex-grow: 1;
|
|
23
|
+
flex-shrink: 0;
|
|
24
|
+
-webkit-overflow-scrolling: touch;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.footer {
|
|
28
|
+
min-height: 56px;
|
|
31
29
|
width: 100%;
|
|
30
|
+
flex-grow: 0;
|
|
31
|
+
flex-shrink: 0;
|
|
32
|
+
box-shadow: inset 0 1px 0 #e0e0e0;
|
|
32
33
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { NavBarProps } from '../nav-bar';
|
|
3
|
+
export interface HeaderSlotProps extends NavBarProps {
|
|
4
|
+
/** Нужна ли разделительная полоса внизу шапки экрана. */
|
|
5
|
+
divided?: boolean;
|
|
6
|
+
/** Заголовок. */
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Подзаголовок. */
|
|
9
|
+
subtitle?: string;
|
|
10
|
+
/** Будет вызвана при нажатии на кнопку-стрелку. */
|
|
11
|
+
onBack?: () => void;
|
|
12
|
+
/** Будет вызвана при нажатии на кнопку-крести. */
|
|
13
|
+
onClose?: () => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Слот шапки экрана.
|
|
17
|
+
* @param props Свойства.
|
|
18
|
+
* @return Элемент.
|
|
19
|
+
*/
|
|
20
|
+
export declare const HeaderSlot: ({ divided, title, subtitle, buttons, onBack, onClose, ...restProps }: HeaderSlotProps) => JSX.Element;
|
|
21
|
+
/**
|
|
22
|
+
* Слот основного контента экрана.
|
|
23
|
+
* @param props Свойства.
|
|
24
|
+
* @return Элемент.
|
|
25
|
+
*/
|
|
26
|
+
export declare const BodySlot: React.FC;
|
|
27
|
+
/**
|
|
28
|
+
* Слот футера экрана.
|
|
29
|
+
* @param props Свойства.
|
|
30
|
+
* @return Элемент.
|
|
31
|
+
*/
|
|
32
|
+
export declare const FooterSlot: React.FC;
|
package/screen/slots.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.FooterSlot = exports.BodySlot = exports.HeaderSlot = void 0;
|
|
18
|
+
const react_1 = __importDefault(require("react"));
|
|
19
|
+
const nav_bar_1 = require("../nav-bar");
|
|
20
|
+
const lodash_1 = require("lodash");
|
|
21
|
+
const arrow_left_1 = __importDefault(require("@sima-land/ui-quarks/icons/24x24/Stroked/arrow-left"));
|
|
22
|
+
const cross_1 = __importDefault(require("@sima-land/ui-quarks/icons/24x24/Stroked/cross"));
|
|
23
|
+
/**
|
|
24
|
+
* Слот шапки экрана.
|
|
25
|
+
* @param props Свойства.
|
|
26
|
+
* @return Элемент.
|
|
27
|
+
*/
|
|
28
|
+
const HeaderSlot = (_a) => {
|
|
29
|
+
var { divided, title, subtitle, buttons, onBack, onClose } = _a, restProps = __rest(_a, ["divided", "title", "subtitle", "buttons", "onBack", "onClose"]);
|
|
30
|
+
return (react_1.default.createElement(nav_bar_1.NavBar, Object.assign({}, restProps, { title: title, subtitle: subtitle, bottomBordered: divided, buttons: Object.assign(Object.assign({}, buttons), { start: onBack
|
|
31
|
+
? {
|
|
32
|
+
// ВАЖНО: не подмешиваем свойства из buttons.start
|
|
33
|
+
// чтобы не распылять конфигурацию одной и той же кнопки по нескольким местам
|
|
34
|
+
icon: arrow_left_1.default,
|
|
35
|
+
onClick: onBack,
|
|
36
|
+
'aria-label': 'Вернуться назад',
|
|
37
|
+
'data-testid': 'screen:back',
|
|
38
|
+
}
|
|
39
|
+
: (0, lodash_1.get)(buttons, 'start'), end: onClose
|
|
40
|
+
? {
|
|
41
|
+
// ВАЖНО: не подмешиваем свойства из buttons.end
|
|
42
|
+
// чтобы не распылять конфигурацию одной и той же кнопки по нескольким местам
|
|
43
|
+
icon: cross_1.default,
|
|
44
|
+
onClick: onClose,
|
|
45
|
+
'aria-label': `Закрыть ${title || ''}`.trim(),
|
|
46
|
+
'data-testid': 'screen:close',
|
|
47
|
+
}
|
|
48
|
+
: (0, lodash_1.get)(buttons, 'end') }) })));
|
|
49
|
+
};
|
|
50
|
+
exports.HeaderSlot = HeaderSlot;
|
|
51
|
+
/**
|
|
52
|
+
* Слот основного контента экрана.
|
|
53
|
+
* @param props Свойства.
|
|
54
|
+
* @return Элемент.
|
|
55
|
+
*/
|
|
56
|
+
const BodySlot = ({ children }) => react_1.default.createElement(react_1.default.Fragment, null, children);
|
|
57
|
+
exports.BodySlot = BodySlot;
|
|
58
|
+
/**
|
|
59
|
+
* Слот футера экрана.
|
|
60
|
+
* @param props Свойства.
|
|
61
|
+
* @return Элемент.
|
|
62
|
+
*/
|
|
63
|
+
const FooterSlot = ({ children }) => react_1.default.createElement(react_1.default.Fragment, null, children);
|
|
64
|
+
exports.FooterSlot = FooterSlot;
|
package/screen/utils.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export declare
|
|
2
|
-
export declare const cx: import("classnames/types").ClassNamesFn;
|
|
1
|
+
export declare const cx: (...args: import("classnames").Argument[]) => string;
|