@sima-land/ui-nucleons 44.1.0 → 44.2.2
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/carousel/index.js +1 -1
- package/clean-buttons/index.d.ts +6 -0
- package/clean-buttons/index.js +3 -2
- package/expandable/expandable-group.module.scss +10 -0
- package/expandable/index.d.ts +14 -3
- package/expandable/index.js +32 -25
- package/expandable/utils.d.ts +20 -9
- package/expandable/utils.js +69 -51
- package/modal/index.js +6 -4
- package/modal/slots.js +2 -1
- package/package.json +1 -1
- package/stepper/index.d.ts +2 -2
- package/stepper/index.js +4 -2
- package/stepper/stepper.module.scss +24 -9
package/carousel/index.js
CHANGED
|
@@ -101,7 +101,7 @@ class Carousel extends react_1.Component {
|
|
|
101
101
|
static defaultRenderControl({ canUse, onUse, type, vertical }, props = { size: 's' }) {
|
|
102
102
|
const forward = vertical ? 'down' : 'right';
|
|
103
103
|
const backward = vertical ? 'up' : 'left';
|
|
104
|
-
return (react_1.default.createElement(arrow_button_1.ArrowButton, Object.assign({}, props, { className: cx('control', vertical && 'vertical', type), direction: type === 'forward' ? forward : backward, onClick: onUse, disabled: !canUse })));
|
|
104
|
+
return (react_1.default.createElement(arrow_button_1.ArrowButton, Object.assign({}, props, { className: cx('control', vertical && 'vertical', type, props.className), direction: type === 'forward' ? forward : backward, onClick: onUse, disabled: !canUse })));
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
107
|
* Возвращает функцию для поиска наиболее отдаленного элемента карусели от стартовой границы viewport'а.
|
package/clean-buttons/index.d.ts
CHANGED
|
@@ -12,6 +12,12 @@ export interface CleanGroupProps {
|
|
|
12
12
|
/** Содержимое. */
|
|
13
13
|
children?: React.ReactNode;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Компонент группы прозрачных кнопок.
|
|
17
|
+
* @param props Свойства.
|
|
18
|
+
* @return Элемент.
|
|
19
|
+
*/
|
|
20
|
+
export declare const CleanGroup: React.FC<CleanGroupProps>;
|
|
15
21
|
export declare const Clean: {
|
|
16
22
|
Group: React.FC<CleanGroupProps>;
|
|
17
23
|
Button: ({ size, href, asLink, ...restProps }: CleanButtonProps) => JSX.Element;
|
package/clean-buttons/index.js
CHANGED
|
@@ -37,7 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.Clean = void 0;
|
|
40
|
+
exports.Clean = exports.CleanGroup = void 0;
|
|
41
41
|
const react_1 = __importStar(require("react"));
|
|
42
42
|
const bind_1 = __importDefault(require("classnames/bind"));
|
|
43
43
|
const link_1 = require("../link");
|
|
@@ -55,6 +55,7 @@ const CleanGroup = ({ size: sizeFromProps, children }) => {
|
|
|
55
55
|
const size = sizeFromProps || sizeFromContext;
|
|
56
56
|
return (react_1.default.createElement("div", { className: cx('group') }, react_1.Children.toArray(children).map(item => (0, react_1.isValidElement)(item) && item.type === CleanButton ? (0, react_1.cloneElement)(item, { size }) : null)));
|
|
57
57
|
};
|
|
58
|
+
exports.CleanGroup = CleanGroup;
|
|
58
59
|
/**
|
|
59
60
|
* Компонент прозрачной кнопки.
|
|
60
61
|
* @param props Свойства.
|
|
@@ -65,6 +66,6 @@ const CleanButton = (_a) => {
|
|
|
65
66
|
return (react_1.default.createElement(link_1.Link, Object.assign({ pseudo: !asLink, className: cx('button', `size-${size}`), href: href }, restProps)));
|
|
66
67
|
};
|
|
67
68
|
exports.Clean = {
|
|
68
|
-
Group: CleanGroup,
|
|
69
|
+
Group: exports.CleanGroup,
|
|
69
70
|
Button: CleanButton,
|
|
70
71
|
};
|
|
@@ -4,15 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
.inner {
|
|
6
6
|
display: flex;
|
|
7
|
+
padding: 0;
|
|
7
8
|
flex-wrap: wrap;
|
|
9
|
+
margin: 0 calc(var(--expandable-gap) * -1) calc(var(--expandable-gap) * -1) 0;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
.item {
|
|
13
|
+
display: block;
|
|
14
|
+
padding: 0;
|
|
11
15
|
max-width: 100%;
|
|
16
|
+
height: var(--expandable-item-height);
|
|
17
|
+
margin: 0 (var(--expandable-gap)) var(--expandable-gap) 0;
|
|
12
18
|
&.hidden {
|
|
13
19
|
display: none;
|
|
14
20
|
}
|
|
15
21
|
&.invisible {
|
|
16
22
|
opacity: 0;
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
}
|
|
25
|
+
&.opener {
|
|
26
|
+
width: var(--expandable-opener-width);
|
|
17
27
|
}
|
|
18
28
|
}
|
package/expandable/index.d.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import React, { ReactNode, MouseEventHandler, CSSProperties } from 'react';
|
|
2
2
|
export interface ExpandableGroupProps {
|
|
3
|
+
/** Расстояние между элементами группы в пикселях. */
|
|
3
4
|
gap?: number;
|
|
5
|
+
/** Высота каждого элемента группы в пикселях. */
|
|
4
6
|
itemHeight?: number;
|
|
7
|
+
/** Развернут ли список по умолчанию. */
|
|
5
8
|
defaultExpanded?: boolean;
|
|
9
|
+
/** Количество строк в свернутом состоянии группы. */
|
|
6
10
|
lineLimit?: number;
|
|
11
|
+
/** Содержимое. */
|
|
7
12
|
children?: ReactNode;
|
|
13
|
+
/** Функция, возвращающая содержимое кнопки разворачивания списка. */
|
|
8
14
|
opener?: (data: {
|
|
9
15
|
hiddenCount: number;
|
|
10
16
|
}) => ReactNode;
|
|
17
|
+
/** Сработает при разворачивании списка. */
|
|
11
18
|
onExpand?: () => void;
|
|
19
|
+
/** CSS-класс корневого элемента. */
|
|
12
20
|
className?: string;
|
|
21
|
+
/** Стили корневого элемента. */
|
|
13
22
|
style?: CSSProperties;
|
|
23
|
+
/** Нужно ли выводить список развернутым. */
|
|
24
|
+
expanded?: boolean;
|
|
25
|
+
openerWidth?: number;
|
|
14
26
|
}
|
|
15
27
|
export interface GroupItemProps {
|
|
16
28
|
children?: ReactNode;
|
|
@@ -22,9 +34,8 @@ export interface GroupItemProps {
|
|
|
22
34
|
* @param props Свойства.
|
|
23
35
|
* @return Элемент.
|
|
24
36
|
*/
|
|
25
|
-
declare function ExpandableGroup({ gap, itemHeight, lineLimit, defaultExpanded, children, onExpand, opener, className, style, }: ExpandableGroupProps): JSX.Element;
|
|
37
|
+
export declare function ExpandableGroup({ gap, itemHeight, lineLimit, defaultExpanded, children, onExpand, opener, className, style, expanded: expandedProp, openerWidth, }: ExpandableGroupProps): JSX.Element;
|
|
26
38
|
export declare const Expandable: {
|
|
27
39
|
readonly Group: typeof ExpandableGroup;
|
|
28
|
-
readonly Item: React.ForwardRefExoticComponent<GroupItemProps & React.RefAttributes<
|
|
40
|
+
readonly Item: React.ForwardRefExoticComponent<GroupItemProps & React.RefAttributes<HTMLLIElement>>;
|
|
29
41
|
};
|
|
30
|
-
export {};
|
package/expandable/index.js
CHANGED
|
@@ -26,13 +26,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.Expandable = void 0;
|
|
29
|
+
exports.Expandable = exports.ExpandableGroup = void 0;
|
|
30
30
|
const react_1 = __importStar(require("react"));
|
|
31
31
|
const utils_1 = require("./utils");
|
|
32
32
|
const bind_1 = __importDefault(require("classnames/bind"));
|
|
33
33
|
const expandable_group_module_scss_1 = __importDefault(require("./expandable-group.module.scss"));
|
|
34
34
|
const cx = bind_1.default.bind(expandable_group_module_scss_1.default);
|
|
35
|
-
const GroupContext = (0, react_1.createContext)({ gap: 8, itemHeight: 32 });
|
|
36
35
|
const ItemContext = (0, react_1.createContext)({});
|
|
37
36
|
/**
|
|
38
37
|
* Группа элементов с ограничением на число выводимых строк и возможностью показать все.
|
|
@@ -41,36 +40,44 @@ const ItemContext = (0, react_1.createContext)({});
|
|
|
41
40
|
*/
|
|
42
41
|
function ExpandableGroup({ gap = 8, itemHeight = 32, lineLimit = 2, defaultExpanded = false, children, onExpand, opener = data => react_1.default.createElement(react_1.default.Fragment, null,
|
|
43
42
|
"\u0415\u0449\u0451 ",
|
|
44
|
-
data.hiddenCount), className, style, }) {
|
|
43
|
+
data.hiddenCount), className, style, expanded: expandedProp, openerWidth = 72, }) {
|
|
44
|
+
const wrapperRef = (0, react_1.useRef)(null);
|
|
45
45
|
const containerRef = (0, react_1.useRef)(null);
|
|
46
46
|
const openerRef = (0, react_1.useRef)(null);
|
|
47
|
+
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
47
48
|
const [expanded, setExpanded] = (0, react_1.useState)(defaultExpanded);
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
(0, react_1.isValidElement)(child) &&
|
|
53
|
-
child.type === ExpandableItem &&
|
|
54
|
-
result.push(react_1.default.createElement(ItemContext.Provider, { key: index, value: { hidden: !isDisplayed } }, (0, react_1.cloneElement)(child, { hidden: !isDisplayed })));
|
|
49
|
+
const items = react_1.Children.toArray(children).reduce((result, child) => {
|
|
50
|
+
if ((0, react_1.isValidElement)(child) && child.type === ExpandableItem) {
|
|
51
|
+
result.push(child);
|
|
52
|
+
}
|
|
55
53
|
return result;
|
|
56
54
|
}, []);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
(0, react_1.useEffect)(() => {
|
|
56
|
+
setMounted(true);
|
|
57
|
+
}, []);
|
|
58
|
+
(0, react_1.useEffect)(() => {
|
|
59
|
+
expandedProp !== undefined && setExpanded(expandedProp);
|
|
60
|
+
}, [expandedProp]);
|
|
61
|
+
const { hiddenCount } = (0, utils_1.useExpandable)({
|
|
62
|
+
expanded,
|
|
63
|
+
wrapperRef,
|
|
64
|
+
containerRef,
|
|
65
|
+
openerRef,
|
|
66
|
+
gap,
|
|
67
|
+
});
|
|
68
|
+
return (react_1.default.createElement("div", { ref: wrapperRef, className: cx('root', className), style: Object.assign(Object.assign({}, style), { maxHeight: expanded ? undefined : `${itemHeight * lineLimit + gap * (lineLimit - 1)}px`, '--expandable-item-height': `${itemHeight}px`, '--expandable-opener-width': `${openerWidth}px`, '--expandable-gap': `${gap}px` }) },
|
|
69
|
+
react_1.default.createElement("ul", { ref: containerRef, className: expandable_group_module_scss_1.default.inner },
|
|
70
|
+
items,
|
|
71
|
+
mounted && !expanded && (react_1.default.createElement(ItemContext.Provider, { value: { invisible: hiddenCount === 0, opener: true } },
|
|
72
|
+
react_1.default.createElement(ExpandableItem, { ref: openerRef, onClick: () => {
|
|
73
|
+
setExpanded(true);
|
|
74
|
+
onExpand === null || onExpand === void 0 ? void 0 : onExpand();
|
|
75
|
+
}, "data-testid": 'expandable:opener' }, opener({ hiddenCount })))))));
|
|
66
76
|
}
|
|
77
|
+
exports.ExpandableGroup = ExpandableGroup;
|
|
67
78
|
const ExpandableItem = (0, react_1.forwardRef)(({ children, onClick, 'data-testid': testId = 'expandable:item' }, ref) => {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
return (react_1.default.createElement("div", { ref: ref, className: cx('item', { hidden, invisible }), "data-testid": testId, onClick: onClick, style: {
|
|
71
|
-
height: `${itemHeight}px`,
|
|
72
|
-
margin: `0 ${gap}px ${gap}px 0`,
|
|
73
|
-
} }, children));
|
|
79
|
+
const context = (0, react_1.useContext)(ItemContext);
|
|
80
|
+
return (react_1.default.createElement("li", { ref: ref, className: cx('item', context), "data-testid": testId, onClick: onClick }, children));
|
|
74
81
|
});
|
|
75
82
|
exports.Expandable = {
|
|
76
83
|
Group: ExpandableGroup,
|
package/expandable/utils.d.ts
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
2
|
export interface ViewState {
|
|
3
|
-
phase: 'default' | 'ready';
|
|
4
3
|
lastVisibleIndex: number;
|
|
5
4
|
}
|
|
6
|
-
export declare const initialViewState: ViewState;
|
|
7
5
|
/**
|
|
8
6
|
* Хук состояния группы элементов ограниченных несколькими строками с возможностью показать все.
|
|
9
|
-
* @param
|
|
10
|
-
* @param
|
|
11
|
-
* @
|
|
7
|
+
* @param options.expanded Развернут ли список.
|
|
8
|
+
* @param options.wrapperRef Реф элемента, формирующего ограничение по высоте.
|
|
9
|
+
* @param options.containerRef Реф элемента, содержащего список.
|
|
10
|
+
* @param options.openerRef Реф элемента "открывашки".
|
|
11
|
+
* @param options.gap Расстояние между элементами списка.
|
|
12
|
+
* @return Рассчитанные данные: кол-во скрытых элементов.
|
|
12
13
|
*/
|
|
13
|
-
export declare function
|
|
14
|
+
export declare function useExpandable({ expanded, wrapperRef, containerRef, openerRef, gap, }: {
|
|
15
|
+
expanded: boolean;
|
|
16
|
+
wrapperRef: RefObject<HTMLElement>;
|
|
17
|
+
containerRef: RefObject<HTMLElement>;
|
|
18
|
+
openerRef: RefObject<HTMLElement>;
|
|
19
|
+
gap: number;
|
|
20
|
+
}): {
|
|
21
|
+
hiddenCount: number;
|
|
22
|
+
};
|
|
14
23
|
/**
|
|
15
24
|
* Определяет индекс последнего видимого элемента для свернутого списка.
|
|
16
|
-
* @param
|
|
17
|
-
* @param
|
|
25
|
+
* @param wrapper Элемент, формирующий ограничение по высоте.
|
|
26
|
+
* @param items Элементы, все кроме "закрывашки".
|
|
27
|
+
* @param opener Элемент "закрывашки".
|
|
28
|
+
* @param gap Расстояние между элементами списка.
|
|
18
29
|
* @return Состояние.
|
|
19
30
|
*/
|
|
20
|
-
export declare function defineViewState(
|
|
31
|
+
export declare function defineViewState(wrapper: HTMLElement, items: Element[], opener: HTMLElement, gap: number): ViewState;
|
package/expandable/utils.js
CHANGED
|
@@ -3,69 +3,87 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.defineViewState = exports.
|
|
6
|
+
exports.defineViewState = exports.useExpandable = void 0;
|
|
7
7
|
const react_1 = require("react");
|
|
8
8
|
const hooks_1 = require("../hooks");
|
|
9
9
|
const on_1 = __importDefault(require("../helpers/on"));
|
|
10
|
-
|
|
11
|
-
phase: 'default',
|
|
12
|
-
lastVisibleIndex: -1,
|
|
13
|
-
};
|
|
10
|
+
const expandable_group_module_scss_1 = __importDefault(require("./expandable-group.module.scss"));
|
|
14
11
|
/**
|
|
15
12
|
* Хук состояния группы элементов ограниченных несколькими строками с возможностью показать все.
|
|
16
|
-
* @param
|
|
17
|
-
* @param
|
|
18
|
-
* @
|
|
13
|
+
* @param options.expanded Развернут ли список.
|
|
14
|
+
* @param options.wrapperRef Реф элемента, формирующего ограничение по высоте.
|
|
15
|
+
* @param options.containerRef Реф элемента, содержащего список.
|
|
16
|
+
* @param options.openerRef Реф элемента "открывашки".
|
|
17
|
+
* @param options.gap Расстояние между элементами списка.
|
|
18
|
+
* @return Рассчитанные данные: кол-во скрытых элементов.
|
|
19
19
|
*/
|
|
20
|
-
function
|
|
21
|
-
const [
|
|
22
|
-
(0, react_1.
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
function useExpandable({ expanded, wrapperRef, containerRef, openerRef, gap, }) {
|
|
21
|
+
const [hiddenCount, setHiddenCount] = (0, react_1.useState)(0);
|
|
22
|
+
const calc = (0, react_1.useCallback)(() => {
|
|
23
|
+
const wrapperEl = wrapperRef.current;
|
|
24
|
+
const containerEl = containerRef.current;
|
|
25
|
+
const openerEl = openerRef.current;
|
|
26
|
+
if (expanded) {
|
|
27
|
+
if (containerEl) {
|
|
28
|
+
for (const item of containerEl.children) {
|
|
29
|
+
item.classList.remove(expandable_group_module_scss_1.default.hidden);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
else {
|
|
34
|
+
if (wrapperEl && containerEl && openerEl) {
|
|
35
|
+
const items = [...containerEl.children].filter(child => child !== openerEl);
|
|
36
|
+
// делаем все элементы списка видимыми чтобы правильно считать размеры
|
|
37
|
+
for (const item of items) {
|
|
38
|
+
item.classList.remove(expandable_group_module_scss_1.default.hidden);
|
|
39
|
+
}
|
|
40
|
+
// определяем какие элементы не влезли в ограничение по высоте
|
|
41
|
+
const state = defineViewState(wrapperEl, items, openerEl, gap);
|
|
42
|
+
if (state.lastVisibleIndex !== -1) {
|
|
43
|
+
// делаем невидимыми элементы списка которые не влезли
|
|
44
|
+
for (const [index, item] of items.entries()) {
|
|
45
|
+
if (item !== openerEl && index >= state.lastVisibleIndex) {
|
|
46
|
+
item.classList.add(expandable_group_module_scss_1.default.hidden);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
setHiddenCount(state.lastVisibleIndex !== -1 ? items.length - state.lastVisibleIndex : 0);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}, [expanded, gap]);
|
|
54
|
+
// пересчитываем после каждого rerender'а
|
|
55
|
+
(0, hooks_1.useIsomorphicLayoutEffect)(calc);
|
|
56
|
+
// пересчитываем при resize
|
|
57
|
+
(0, react_1.useEffect)(() => (0, on_1.default)(window, 'resize', calc), [calc]);
|
|
58
|
+
return { hiddenCount };
|
|
34
59
|
}
|
|
35
|
-
exports.
|
|
60
|
+
exports.useExpandable = useExpandable;
|
|
36
61
|
/**
|
|
37
62
|
* Определяет индекс последнего видимого элемента для свернутого списка.
|
|
38
|
-
* @param
|
|
39
|
-
* @param
|
|
63
|
+
* @param wrapper Элемент, формирующий ограничение по высоте.
|
|
64
|
+
* @param items Элементы, все кроме "закрывашки".
|
|
65
|
+
* @param opener Элемент "закрывашки".
|
|
66
|
+
* @param gap Расстояние между элементами списка.
|
|
40
67
|
* @return Состояние.
|
|
41
68
|
*/
|
|
42
|
-
function defineViewState(
|
|
43
|
-
const state = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const lastVisible = childList[lastVisibleIndex];
|
|
61
|
-
if (lastVisible) {
|
|
62
|
-
const rightBound = lastVisible.getBoundingClientRect().right;
|
|
63
|
-
const rightContainerBound = container.getBoundingClientRect().right;
|
|
64
|
-
// проверяем, хватает ли кнопке места (с запасом в половину) после последнего видимого дочернего элемента
|
|
65
|
-
const isOpenerFit = rightContainerBound - rightBound >= opener.clientWidth * 1.5;
|
|
66
|
-
// если хватает места - ставим кнопку после последнего, иначе - вместо
|
|
67
|
-
state.lastVisibleIndex = lastVisibleIndex + Number(isOpenerFit);
|
|
68
|
-
}
|
|
69
|
+
function defineViewState(wrapper, items, opener, gap) {
|
|
70
|
+
const state = { lastVisibleIndex: -1 };
|
|
71
|
+
const parentRect = wrapper.getBoundingClientRect();
|
|
72
|
+
const firstHiddenNodeIndex = items.findIndex(child => {
|
|
73
|
+
const childRect = child.getBoundingClientRect();
|
|
74
|
+
return childRect.top - parentRect.top >= parentRect.height;
|
|
75
|
+
});
|
|
76
|
+
if (firstHiddenNodeIndex !== -1) {
|
|
77
|
+
const lastVisibleIndex = firstHiddenNodeIndex - 1;
|
|
78
|
+
const lastVisible = items[lastVisibleIndex];
|
|
79
|
+
if (lastVisible) {
|
|
80
|
+
const rightBound = lastVisible.getBoundingClientRect().right;
|
|
81
|
+
const rightContainerBound = wrapper.getBoundingClientRect().right;
|
|
82
|
+
// хватает ли кнопке места после последнего видимого дочернего элемента с учетом отступа между элементами
|
|
83
|
+
const isOpenerFit = rightContainerBound - rightBound - gap >= opener.clientWidth;
|
|
84
|
+
// если хватает места - ставим кнопку после последнего, иначе - вместо
|
|
85
|
+
// @todo возможна ситуация когда нужно будет удалить N элементов чтобы влезла кнопка
|
|
86
|
+
state.lastVisibleIndex = lastVisibleIndex + Number(isOpenerFit);
|
|
69
87
|
}
|
|
70
88
|
}
|
|
71
89
|
return state;
|
package/modal/index.js
CHANGED
|
@@ -35,17 +35,17 @@ const shadows_1 = require("../styling/shadows");
|
|
|
35
35
|
const lodash_1 = require("lodash");
|
|
36
36
|
const slots_1 = require("./slots");
|
|
37
37
|
const layer_1 = require("../helpers/layer");
|
|
38
|
-
const bind_1 = __importDefault(require("classnames/bind"));
|
|
39
|
-
const modal_module_scss_1 = __importDefault(require("./modal.module.scss"));
|
|
40
38
|
const define_slots_1 = require("../helpers/define-slots");
|
|
41
39
|
const body_scroll_1 = require("../_internal/body-scroll");
|
|
40
|
+
const bind_1 = __importDefault(require("classnames/bind"));
|
|
41
|
+
const modal_module_scss_1 = __importDefault(require("./modal.module.scss"));
|
|
42
42
|
const cx = bind_1.default.bind(modal_module_scss_1.default);
|
|
43
43
|
/**
|
|
44
44
|
* Компонент модального окна.
|
|
45
45
|
* @param props Свойства.
|
|
46
46
|
* @return Элемент.
|
|
47
47
|
*/
|
|
48
|
-
const Modal = ({ children, height, onClose, scrollDisableOptions
|
|
48
|
+
const Modal = ({ children, height, onClose, scrollDisableOptions, size = 'm', withScrollDisable = false, 'data-testid': testId = 'modal', }) => {
|
|
49
49
|
const layer = (0, layer_1.useLayer)() + 100;
|
|
50
50
|
const rootRef = (0, react_1.useRef)(null);
|
|
51
51
|
const bind = (0, utils_1.useCloseHandler)(onClose);
|
|
@@ -67,7 +67,9 @@ const Modal = ({ children, height, onClose, scrollDisableOptions = { reserveScro
|
|
|
67
67
|
'--header-height': `${headerHeight}px`,
|
|
68
68
|
'--footer-height': `${footerHeight}px`,
|
|
69
69
|
};
|
|
70
|
-
(0, body_scroll_1.useBodyScrollLock)(rootRef, withScrollDisable,
|
|
70
|
+
(0, body_scroll_1.useBodyScrollLock)(rootRef, withScrollDisable, Object.assign({
|
|
71
|
+
// defaults
|
|
72
|
+
reserveScrollBarGap: true, allowTouchMove: body_scroll_1.allowTouchMove }, scrollDisableOptions));
|
|
71
73
|
return (react_1.default.createElement("div", Object.assign({ ref: rootRef, className: cx('overlay'), "data-testid": 'modal:overlay', style: { zIndex: layer } }, bind),
|
|
72
74
|
react_1.default.createElement("div", { className: cx('modal', `size-${size}`, !fullscreen && shadows_1.BoxShadow.z4), style: style, "data-testid": testId },
|
|
73
75
|
header &&
|
package/modal/slots.js
CHANGED
|
@@ -46,6 +46,7 @@ const bottom_bar_1 = require("../bottom-bar");
|
|
|
46
46
|
const custom_scrollbar_1 = require("../_internal/custom-scrollbar");
|
|
47
47
|
const modal_module_scss_1 = __importDefault(require("./modal.module.scss"));
|
|
48
48
|
const viewport_1 = require("../context/viewport");
|
|
49
|
+
const constants_1 = require("../constants");
|
|
49
50
|
/**
|
|
50
51
|
* Шапка окна.
|
|
51
52
|
* @param props Свойства.
|
|
@@ -77,7 +78,7 @@ const ModalBody = ({ children, onFullScroll, fullScrollThreshold, }) => {
|
|
|
77
78
|
const ref = (0, react_1.useRef)(null);
|
|
78
79
|
return (react_1.default.createElement(custom_scrollbar_1.CustomScrollbar, { inFlexBox: true, className: modal_module_scss_1.default.body, overflow: { x: 'h', y: 's' }, onFullScroll: onFullScroll, fullScrollThreshold: fullScrollThreshold },
|
|
79
80
|
react_1.default.createElement(viewport_1.ViewportContext.Provider, { value: ref },
|
|
80
|
-
react_1.default.createElement("div", { className: modal_module_scss_1.default.main, ref: ref }, children))));
|
|
81
|
+
react_1.default.createElement("div", Object.assign({ className: modal_module_scss_1.default.main, ref: ref }, { [constants_1.BSL_IGNORE_ATTR]: true }), children))));
|
|
81
82
|
};
|
|
82
83
|
exports.ModalBody = ModalBody;
|
|
83
84
|
/**
|
package/package.json
CHANGED
package/stepper/index.d.ts
CHANGED
|
@@ -9,9 +9,9 @@ export interface StepperProps extends Omit<React.InputHTMLAttributes<HTMLInputEl
|
|
|
9
9
|
/** Нужно ли выводить кнопку вычитания. */
|
|
10
10
|
canSubtract?: boolean;
|
|
11
11
|
/** Сработает при добавлении. */
|
|
12
|
-
onAdd?: React.MouseEventHandler
|
|
12
|
+
onAdd?: React.MouseEventHandler;
|
|
13
13
|
/** Сработает при вычитании. */
|
|
14
|
-
onSubtract?: React.MouseEventHandler
|
|
14
|
+
onSubtract?: React.MouseEventHandler;
|
|
15
15
|
/** Размер. */
|
|
16
16
|
size?: Size;
|
|
17
17
|
/** Идентификатор для систем автоматизированного тестирования. */
|
package/stepper/index.js
CHANGED
|
@@ -53,7 +53,8 @@ exports.Stepper = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
53
53
|
var { canAdd = true, canSubtract = true, className, disabled, readOnly, onAdd, onBlur, onChange, onFocus, onSubtract, size = 'm', style, value, failed, 'data-testid': testId = 'stepper' } = _a, inputProps = __rest(_a, ["canAdd", "canSubtract", "className", "disabled", "readOnly", "onAdd", "onBlur", "onChange", "onFocus", "onSubtract", "size", "style", "value", "failed", 'data-testid']);
|
|
54
54
|
const [focused, setFocused] = (0, react_1.useState)(false);
|
|
55
55
|
return (react_1.default.createElement("div", { "data-testid": testId, style: style, className: cx('root', `size-${size}`, { disabled, focused, readonly: readOnly, failed }, className) },
|
|
56
|
-
react_1.default.createElement(
|
|
56
|
+
react_1.default.createElement("button", { className: cx('button', { hidden: !canSubtract }), "data-testid": 'stepper:minus', onClick: canSubtract && !disabled && !readOnly ? onSubtract : undefined },
|
|
57
|
+
react_1.default.createElement(minus_1.default, null)),
|
|
57
58
|
react_1.default.createElement("input", Object.assign({}, inputProps, { ref: ref, readOnly: readOnly, "data-testid": 'stepper:input', className: cx('input'), value: value, disabled: disabled, onChange: onChange, onFocus: e => {
|
|
58
59
|
setFocused(true);
|
|
59
60
|
onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
|
|
@@ -61,5 +62,6 @@ exports.Stepper = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
61
62
|
setFocused(false);
|
|
62
63
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
|
63
64
|
} })),
|
|
64
|
-
react_1.default.createElement(
|
|
65
|
+
react_1.default.createElement("button", { className: cx('button', { hidden: !canAdd }), "data-testid": 'stepper:plus', onClick: canAdd && !disabled && !readOnly ? onAdd : undefined },
|
|
66
|
+
react_1.default.createElement(plus_1.default, null))));
|
|
65
67
|
});
|
|
@@ -3,18 +3,19 @@
|
|
|
3
3
|
.root {
|
|
4
4
|
display: flex;
|
|
5
5
|
align-items: center;
|
|
6
|
-
border: 1px solid
|
|
6
|
+
border: 1px solid transparent;
|
|
7
7
|
border-radius: 4px;
|
|
8
8
|
color: colors.$basic-gray87;
|
|
9
|
-
background:
|
|
9
|
+
background: colors.$basic-gray8;
|
|
10
10
|
&.focused:not(.disabled) {
|
|
11
11
|
border-color: colors.$basic-blue;
|
|
12
12
|
}
|
|
13
13
|
&.disabled {
|
|
14
14
|
color: colors.$basic-gray24;
|
|
15
|
-
border-color: colors.$basic-
|
|
15
|
+
border-color: colors.$basic-gray4;
|
|
16
|
+
background: colors.$basic-gray4;
|
|
16
17
|
.button {
|
|
17
|
-
fill: colors.$basic-
|
|
18
|
+
fill: colors.$basic-gray24;
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
&.readonly {
|
|
@@ -23,17 +24,18 @@
|
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
&.failed:not(.disabled):not(.focused) {
|
|
26
|
-
border-color:
|
|
27
|
+
border-color: transparent;
|
|
28
|
+
color: colors.$additional-deep-red;
|
|
27
29
|
}
|
|
28
30
|
&.size-s {
|
|
31
|
+
--stepper-button-width: 24px;
|
|
29
32
|
width: var(--stepper-width, 122px);
|
|
30
33
|
height: 40px;
|
|
31
|
-
padding: 0 8px;
|
|
32
34
|
}
|
|
33
35
|
&.size-m {
|
|
36
|
+
--stepper-button-width: 32px;
|
|
34
37
|
width: var(--stepper-width, 160px);
|
|
35
38
|
height: 48px;
|
|
36
|
-
padding: 0 16px;
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -42,7 +44,7 @@
|
|
|
42
44
|
padding: 0;
|
|
43
45
|
|
|
44
46
|
// не надо делать transparent: https://stackoverflow.com/q/43483363/13166471
|
|
45
|
-
background:
|
|
47
|
+
background: inherit;
|
|
46
48
|
outline: 0;
|
|
47
49
|
width: 0;
|
|
48
50
|
flex-grow: 1;
|
|
@@ -56,8 +58,21 @@
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
.button {
|
|
59
|
-
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
border: 0;
|
|
64
|
+
padding: 0;
|
|
65
|
+
background: transparent;
|
|
66
|
+
width: var(--stepper-button-width);
|
|
67
|
+
height: 100%;
|
|
68
|
+
fill: colors.$basic-gray87;
|
|
60
69
|
flex-shrink: 0;
|
|
70
|
+
&:first-child {
|
|
71
|
+
justify-content: flex-end;
|
|
72
|
+
}
|
|
73
|
+
&:last-child {
|
|
74
|
+
justify-content: flex-start;
|
|
75
|
+
}
|
|
61
76
|
&:not(.hidden) {
|
|
62
77
|
cursor: pointer;
|
|
63
78
|
}
|