@vkontakte/vkui 4.22.0 → 4.22.1
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/.cache/.eslintcache +1 -1
- package/.cache/.stylelintcache +1 -1
- package/.cache/.tsbuildinfo +14 -8
- package/.cache/ts/src/components/TabbarItem/TabbarItem.d.ts +3 -1
- package/dist/cjs/components/CardScroll/CardScroll.js +1 -1
- package/dist/cjs/components/CardScroll/CardScroll.js.map +1 -1
- package/dist/cjs/components/Cell/useDraggable.js +3 -1
- package/dist/cjs/components/Cell/useDraggable.js.map +1 -1
- package/dist/cjs/components/FocusTrap/FocusTrap.js +3 -5
- package/dist/cjs/components/FocusTrap/FocusTrap.js.map +1 -1
- package/dist/cjs/components/Tabbar/Tabbar.js +3 -1
- package/dist/cjs/components/Tabbar/Tabbar.js.map +1 -1
- package/dist/cjs/components/TabbarItem/TabbarItem.d.ts +3 -1
- package/dist/cjs/components/TabbarItem/TabbarItem.js +29 -11
- package/dist/cjs/components/TabbarItem/TabbarItem.js.map +1 -1
- package/dist/components/CardScroll/CardScroll.js +1 -1
- package/dist/components/CardScroll/CardScroll.js.map +1 -1
- package/dist/components/Cell/useDraggable.js +2 -1
- package/dist/components/Cell/useDraggable.js.map +1 -1
- package/dist/components/FocusTrap/FocusTrap.js +3 -5
- package/dist/components/FocusTrap/FocusTrap.js.map +1 -1
- package/dist/components/Tabbar/Tabbar.js +3 -1
- package/dist/components/Tabbar/Tabbar.js.map +1 -1
- package/dist/components/TabbarItem/TabbarItem.d.ts +3 -1
- package/dist/components/TabbarItem/TabbarItem.js +26 -10
- package/dist/components/TabbarItem/TabbarItem.js.map +1 -1
- package/dist/components.css +1 -1
- package/dist/components.css.map +1 -1
- package/dist/cssm/components/CardScroll/CardScroll.js +1 -1
- package/dist/cssm/components/CardScroll/CardScroll.js.map +1 -1
- package/dist/cssm/components/Cell/useDraggable.js +2 -1
- package/dist/cssm/components/Cell/useDraggable.js.map +1 -1
- package/dist/cssm/components/FocusTrap/FocusTrap.js +3 -5
- package/dist/cssm/components/FocusTrap/FocusTrap.js.map +1 -1
- package/dist/cssm/components/Tabbar/Tabbar.css +1 -1
- package/dist/cssm/components/Tabbar/Tabbar.js +3 -1
- package/dist/cssm/components/Tabbar/Tabbar.js.map +1 -1
- package/dist/cssm/components/TabbarItem/TabbarItem.css +1 -1
- package/dist/cssm/components/TabbarItem/TabbarItem.js +26 -10
- package/dist/cssm/components/TabbarItem/TabbarItem.js.map +1 -1
- package/dist/cssm/styles/components.css +1 -1
- package/dist/vkui.css +1 -1
- package/dist/vkui.css.map +1 -1
- package/package.json +1 -1
- package/src/components/CardScroll/CardScroll.tsx +4 -1
- package/src/components/Cell/useDraggable.tsx +1 -1
- package/src/components/Epic/Readme.md +1 -0
- package/src/components/FocusTrap/FocusTrap.tsx +11 -8
- package/src/components/Tabbar/Tabbar.css +15 -4
- package/src/components/Tabbar/Tabbar.tsx +3 -1
- package/src/components/TabbarItem/Readme.md +72 -0
- package/src/components/TabbarItem/TabbarItem.css +65 -14
- package/src/components/TabbarItem/TabbarItem.tsx +46 -22
package/package.json
CHANGED
|
@@ -46,7 +46,10 @@ const CardScroll: React.FC<CardScrollProps> = ({ children, size, sizeX, ...restP
|
|
|
46
46
|
|
|
47
47
|
function getScrollToRight(offset: number): number {
|
|
48
48
|
const containerWidth = refContainer.current.offsetWidth;
|
|
49
|
-
const slide = Array.
|
|
49
|
+
const slide = Array.prototype.find.call(
|
|
50
|
+
refContainer.current.children,
|
|
51
|
+
(el: HTMLElement) => el.offsetLeft + el.offsetWidth - offset > containerWidth,
|
|
52
|
+
) as HTMLElement;
|
|
50
53
|
|
|
51
54
|
if (!slide) {
|
|
52
55
|
return offset;
|
|
@@ -28,7 +28,7 @@ export const useDraggable = ({ onDragFinish }: Pick<CellProps, 'onDragFinish'>)
|
|
|
28
28
|
|
|
29
29
|
setDragging(true);
|
|
30
30
|
|
|
31
|
-
const _siblings: HTMLElement[] =
|
|
31
|
+
const _siblings: HTMLElement[] = [...rootEl.parentElement.childNodes];
|
|
32
32
|
const idx = _siblings.indexOf(rootEl);
|
|
33
33
|
|
|
34
34
|
setDragStartIndex(idx);
|
|
@@ -135,6 +135,7 @@ const Example = withAdaptivity(({ viewWidth }) => {
|
|
|
135
135
|
onClick={onStoryChange}
|
|
136
136
|
selected={activeStory === 'profile'}
|
|
137
137
|
data-story="profile"
|
|
138
|
+
indicator={<Badge mode="prominent" />}
|
|
138
139
|
text="Профиль"
|
|
139
140
|
><Icon28UserCircleOutline /></TabbarItem>
|
|
140
141
|
</Tabbar>
|
|
@@ -54,14 +54,17 @@ export const FocusTrap: React.FC<FocusTrapProps> = ({
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
const nodes: HTMLElement[] = [];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
Array.prototype.forEach.call(
|
|
58
|
+
// eslint-disable-next-line no-restricted-properties
|
|
59
|
+
ref.current.querySelectorAll(FOCUSABLE_ELEMENTS),
|
|
60
|
+
(focusableEl: Element) => {
|
|
61
|
+
const { display, visibility } = window.getComputedStyle(focusableEl);
|
|
62
|
+
|
|
63
|
+
if (display !== 'none' && visibility !== 'hidden') {
|
|
64
|
+
nodes.push(focusableEl as HTMLElement);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
);
|
|
65
68
|
|
|
66
69
|
if (nodes?.length) {
|
|
67
70
|
setFocusableNodes(nodes);
|
|
@@ -5,14 +5,21 @@
|
|
|
5
5
|
left: 0;
|
|
6
6
|
width: 100%;
|
|
7
7
|
height: var(--tabbar_height);
|
|
8
|
-
|
|
9
|
-
align-items: stretch;
|
|
10
|
-
justify-content: center;
|
|
8
|
+
padding-bottom: var(--safe-area-inset-bottom);
|
|
11
9
|
box-sizing: content-box;
|
|
12
10
|
background: var(--header_alternate_background);
|
|
13
|
-
padding-bottom: var(--safe-area-inset-bottom);
|
|
14
11
|
}
|
|
15
12
|
|
|
13
|
+
.Tabbar__in {
|
|
14
|
+
display: flex;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* iOS
|
|
21
|
+
*/
|
|
22
|
+
|
|
16
23
|
.Tabbar--ios.Tabbar--shadow::before {
|
|
17
24
|
position: absolute;
|
|
18
25
|
bottom: 100%;
|
|
@@ -36,6 +43,10 @@
|
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Android & VKCOM
|
|
48
|
+
*/
|
|
49
|
+
|
|
39
50
|
.Tabbar--android.Tabbar--shadow,
|
|
40
51
|
.Tabbar--vkcom.Tabbar--shadow {
|
|
41
52
|
box-shadow: 0 -2px 4px 0 rgba(0, 0, 0, .06), 0 0 2px 0 rgba(0, 0, 0, .08);
|
|
@@ -1 +1,73 @@
|
|
|
1
1
|
Элемент [Tabbar](#!/Tabbar). В качестве `children` рекомендуется использовать [иконку](https://vkcom.github.io/icons) 28 размера.
|
|
2
|
+
|
|
3
|
+
```jsx { "props": { "layout": false, "iframe": false } }
|
|
4
|
+
const [simple, setSimple] = useState('one');
|
|
5
|
+
const [text, setText] = useState('one');
|
|
6
|
+
const [horizontalText, setHorizontalText] = useState('one');
|
|
7
|
+
const [indicator, setIndicator] = useState('one');
|
|
8
|
+
|
|
9
|
+
<div style={{background: 'var(--background_page)', padding: '10px 0'}}>
|
|
10
|
+
<div style={{maxWidth: 768, margin: 'auto'}}>
|
|
11
|
+
<Tabbar style={{position: 'static', margin: '0 0 10px'}}>
|
|
12
|
+
<TabbarItem selected={simple === 'one'} onClick={() => setSimple('one')}><Icon28NewsfeedOutline/></TabbarItem>
|
|
13
|
+
<TabbarItem selected={simple === 'two'} onClick={() => setSimple('two')}><Icon28UserCircleOutline/></TabbarItem>
|
|
14
|
+
</Tabbar>
|
|
15
|
+
|
|
16
|
+
<Tabbar style={{position: 'static', margin: '10px 0'}}>
|
|
17
|
+
<TabbarItem selected={text === 'one'} onClick={() => setText('one')} text="Новости"><Icon28NewsfeedOutline/></TabbarItem>
|
|
18
|
+
<TabbarItem selected={text === 'two'} onClick={() => setText('two')} text="Профиль"><Icon28UserCircleOutline/></TabbarItem>
|
|
19
|
+
<TabbarItem selected={text === 'three'} onClick={() => setText('three')} text="Мессенджер"><Icon28MessageOutline/></TabbarItem>
|
|
20
|
+
</Tabbar>
|
|
21
|
+
|
|
22
|
+
<Tabbar itemsLayout="horizontal" style={{position: 'static', margin: '10px 0'}}>
|
|
23
|
+
<TabbarItem
|
|
24
|
+
selected={horizontalText === 'one'}
|
|
25
|
+
onClick={() => setHorizontalText('one')}
|
|
26
|
+
text="Новости"
|
|
27
|
+
>
|
|
28
|
+
<Icon28NewsfeedOutline/>
|
|
29
|
+
</TabbarItem>
|
|
30
|
+
<TabbarItem
|
|
31
|
+
selected={horizontalText === 'two'}
|
|
32
|
+
onClick={() => setHorizontalText('two')}
|
|
33
|
+
text="Профиль"
|
|
34
|
+
>
|
|
35
|
+
<Icon28UserCircleOutline/>
|
|
36
|
+
</TabbarItem>
|
|
37
|
+
<TabbarItem
|
|
38
|
+
selected={horizontalText === 'three'}
|
|
39
|
+
onClick={() => setHorizontalText('three')}
|
|
40
|
+
text="Мессенджер"
|
|
41
|
+
>
|
|
42
|
+
<Icon28MessageOutline/>
|
|
43
|
+
</TabbarItem>
|
|
44
|
+
</Tabbar>
|
|
45
|
+
|
|
46
|
+
<Tabbar style={{position: 'static', margin: '10px 0 0'}}>
|
|
47
|
+
<TabbarItem
|
|
48
|
+
selected={indicator === 'one'}
|
|
49
|
+
onClick={() => setIndicator('one')}
|
|
50
|
+
indicator={<Badge mode="prominent" />}
|
|
51
|
+
text="Новости"
|
|
52
|
+
>
|
|
53
|
+
<Icon28NewsfeedOutline />
|
|
54
|
+
</TabbarItem>
|
|
55
|
+
<TabbarItem
|
|
56
|
+
selected={indicator === 'two'}
|
|
57
|
+
onClick={() => setIndicator('two')}
|
|
58
|
+
text="Профиль"
|
|
59
|
+
>
|
|
60
|
+
<Icon28UserCircleOutline />
|
|
61
|
+
</TabbarItem>
|
|
62
|
+
<TabbarItem
|
|
63
|
+
selected={indicator === 'three'}
|
|
64
|
+
onClick={() => setIndicator('three')}
|
|
65
|
+
text="Мессенджер"
|
|
66
|
+
indicator={<Counter size="s" mode="prominent">3</Counter>}
|
|
67
|
+
>
|
|
68
|
+
<Icon28MessageOutline />
|
|
69
|
+
</TabbarItem>
|
|
70
|
+
</Tabbar>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
```
|
|
@@ -1,42 +1,54 @@
|
|
|
1
1
|
.TabbarItem {
|
|
2
|
-
flex-shrink: 0;
|
|
3
|
-
max-width: 100%;
|
|
4
|
-
flex-grow: 1;
|
|
5
|
-
flex-basis: 0;
|
|
6
|
-
overflow: hidden;
|
|
7
2
|
display: flex;
|
|
8
3
|
align-items: center;
|
|
9
4
|
justify-content: center;
|
|
10
5
|
color: var(--tabbar_inactive_icon);
|
|
11
6
|
text-decoration: none;
|
|
7
|
+
border: none;
|
|
8
|
+
outline: none;
|
|
9
|
+
padding: 0;
|
|
10
|
+
background: transparent;
|
|
11
|
+
height: var(--tabbar_height);
|
|
12
|
+
position: relative;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
.Tabbar
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
.Tabbar .TabbarItem {
|
|
16
|
+
flex-shrink: 0;
|
|
17
|
+
max-width: 100%;
|
|
18
|
+
min-width: 0;
|
|
19
|
+
flex-grow: 1;
|
|
20
|
+
flex-basis: 0;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
.TabbarItem--selected {
|
|
20
24
|
color: var(--tabbar_active_icon);
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
.TabbarItem__tappable {
|
|
28
|
+
position: absolute;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
.TabbarItem__in {
|
|
24
|
-
align-self: center;
|
|
25
32
|
display: flex;
|
|
26
|
-
max-width: 100%;
|
|
27
33
|
padding: 0 2px;
|
|
28
|
-
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
pointer-events: none;
|
|
37
|
+
height: 100%;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.Tabbar--l-vertical .TabbarItem--text .TabbarItem__in {
|
|
42
|
+
position: relative;
|
|
43
|
+
top: 2px;
|
|
29
44
|
}
|
|
30
45
|
|
|
31
46
|
.Tabbar--l-vertical .TabbarItem__in {
|
|
32
47
|
flex-direction: column;
|
|
33
|
-
align-items: center;
|
|
34
|
-
justify-content: center;
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
.Tabbar--l-horizontal .TabbarItem__in {
|
|
38
51
|
flex-direction: row;
|
|
39
|
-
align-items: center;
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
.TabbarItem__icon {
|
|
@@ -74,3 +86,42 @@
|
|
|
74
86
|
font-weight: 500;
|
|
75
87
|
margin-left: 8px;
|
|
76
88
|
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Android
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
.TabbarItem--android .TabbarItem__tappable {
|
|
95
|
+
width: calc(100% + 16px);
|
|
96
|
+
padding-bottom: calc(100% + 16px);
|
|
97
|
+
border-radius: 50%;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.TabbarItem--android:first-child {
|
|
101
|
+
border-top-left-radius: 0;
|
|
102
|
+
border-bottom-left-radius: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.TabbarItem--android:last-child {
|
|
106
|
+
border-top-right-radius: 0;
|
|
107
|
+
border-bottom-right-radius: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* iOS
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
.TabbarItem--ios .TabbarItem__tappable {
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 100%;
|
|
117
|
+
top: 0;
|
|
118
|
+
left: 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.TabbarItem--ios .TabbarItem__in {
|
|
122
|
+
transition: transform .07s var(--ios-easing);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.TabbarItem--ios .TabbarItem__tappable--active + .TabbarItem__in {
|
|
126
|
+
transform: scale(.96);
|
|
127
|
+
}
|
|
@@ -4,9 +4,15 @@ import Counter from '../Counter/Counter';
|
|
|
4
4
|
import { classNames } from '../../lib/classNames';
|
|
5
5
|
import { usePlatform } from '../../hooks/usePlatform';
|
|
6
6
|
import { hasReactNode } from '../../lib/utils';
|
|
7
|
+
import Tappable from '../Tappable/Tappable';
|
|
8
|
+
import { Platform } from '../../lib/platform';
|
|
9
|
+
import { HasComponent, HasRootRef } from '../../types';
|
|
7
10
|
import './TabbarItem.css';
|
|
8
11
|
|
|
9
|
-
export interface TabbarItemProps extends
|
|
12
|
+
export interface TabbarItemProps extends
|
|
13
|
+
Omit<React.AllHTMLAttributes<HTMLElement>, 'label'>, // TODO убрать Omit после удаления свойства label
|
|
14
|
+
HasRootRef<HTMLElement>,
|
|
15
|
+
HasComponent {
|
|
10
16
|
selected?: boolean;
|
|
11
17
|
/**
|
|
12
18
|
* Тест рядом с иконкой
|
|
@@ -22,31 +28,49 @@ export interface TabbarItemProps extends React.HTMLAttributes<HTMLElement>, Reac
|
|
|
22
28
|
label?: React.ReactNode;
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
const TabbarItem: React.FunctionComponent<TabbarItemProps> = (
|
|
26
|
-
|
|
31
|
+
const TabbarItem: React.FunctionComponent<TabbarItemProps> = ({
|
|
32
|
+
children,
|
|
33
|
+
selected,
|
|
34
|
+
label,
|
|
35
|
+
indicator,
|
|
36
|
+
text,
|
|
37
|
+
href,
|
|
38
|
+
Component = href ? 'a' : 'button',
|
|
39
|
+
disabled,
|
|
40
|
+
...restProps
|
|
41
|
+
}: TabbarItemProps) => {
|
|
27
42
|
const platform = usePlatform();
|
|
28
|
-
const Component: React.ElementType = restProps.href ? 'a' : 'div';
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// @ts-ignore ругается на то, что у AllHTMLAttributes type это строка, а button не любую строку считает валидным значением
|
|
45
|
+
return (<Component
|
|
46
|
+
{...restProps}
|
|
47
|
+
disabled={disabled}
|
|
48
|
+
href={href}
|
|
49
|
+
vkuiClass={classNames(getClassName('TabbarItem', platform), {
|
|
50
|
+
'TabbarItem--selected': selected,
|
|
51
|
+
'TabbarItem--text': !!text,
|
|
52
|
+
})}
|
|
53
|
+
>
|
|
54
|
+
<Tappable
|
|
55
|
+
role="presentation"
|
|
56
|
+
Component="div"
|
|
57
|
+
disabled={disabled}
|
|
58
|
+
activeMode={platform === Platform.IOS ? 'TabbarItem__tappable--active' : 'background'}
|
|
59
|
+
activeEffectDelay={platform === Platform.IOS ? 0 : 300}
|
|
60
|
+
hasHover={false}
|
|
61
|
+
vkuiClass="TabbarItem__tappable"
|
|
62
|
+
/>
|
|
63
|
+
<div vkuiClass="TabbarItem__in">
|
|
64
|
+
<div vkuiClass="TabbarItem__icon">
|
|
65
|
+
{children}
|
|
66
|
+
<div vkuiClass="TabbarItem__label">
|
|
67
|
+
{hasReactNode(indicator) && indicator}
|
|
68
|
+
{!indicator && label && <Counter size="s" mode="prominent">{label}</Counter>}
|
|
45
69
|
</div>
|
|
46
|
-
{text && <div vkuiClass="TabbarItem__text">{text}</div>}
|
|
47
70
|
</div>
|
|
48
|
-
|
|
49
|
-
|
|
71
|
+
{text && <div vkuiClass="TabbarItem__text">{text}</div>}
|
|
72
|
+
</div>
|
|
73
|
+
</Component>);
|
|
50
74
|
};
|
|
51
75
|
|
|
52
76
|
export default TabbarItem;
|