@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.
Files changed (53) hide show
  1. package/.cache/.eslintcache +1 -1
  2. package/.cache/.stylelintcache +1 -1
  3. package/.cache/.tsbuildinfo +14 -8
  4. package/.cache/ts/src/components/TabbarItem/TabbarItem.d.ts +3 -1
  5. package/dist/cjs/components/CardScroll/CardScroll.js +1 -1
  6. package/dist/cjs/components/CardScroll/CardScroll.js.map +1 -1
  7. package/dist/cjs/components/Cell/useDraggable.js +3 -1
  8. package/dist/cjs/components/Cell/useDraggable.js.map +1 -1
  9. package/dist/cjs/components/FocusTrap/FocusTrap.js +3 -5
  10. package/dist/cjs/components/FocusTrap/FocusTrap.js.map +1 -1
  11. package/dist/cjs/components/Tabbar/Tabbar.js +3 -1
  12. package/dist/cjs/components/Tabbar/Tabbar.js.map +1 -1
  13. package/dist/cjs/components/TabbarItem/TabbarItem.d.ts +3 -1
  14. package/dist/cjs/components/TabbarItem/TabbarItem.js +29 -11
  15. package/dist/cjs/components/TabbarItem/TabbarItem.js.map +1 -1
  16. package/dist/components/CardScroll/CardScroll.js +1 -1
  17. package/dist/components/CardScroll/CardScroll.js.map +1 -1
  18. package/dist/components/Cell/useDraggable.js +2 -1
  19. package/dist/components/Cell/useDraggable.js.map +1 -1
  20. package/dist/components/FocusTrap/FocusTrap.js +3 -5
  21. package/dist/components/FocusTrap/FocusTrap.js.map +1 -1
  22. package/dist/components/Tabbar/Tabbar.js +3 -1
  23. package/dist/components/Tabbar/Tabbar.js.map +1 -1
  24. package/dist/components/TabbarItem/TabbarItem.d.ts +3 -1
  25. package/dist/components/TabbarItem/TabbarItem.js +26 -10
  26. package/dist/components/TabbarItem/TabbarItem.js.map +1 -1
  27. package/dist/components.css +1 -1
  28. package/dist/components.css.map +1 -1
  29. package/dist/cssm/components/CardScroll/CardScroll.js +1 -1
  30. package/dist/cssm/components/CardScroll/CardScroll.js.map +1 -1
  31. package/dist/cssm/components/Cell/useDraggable.js +2 -1
  32. package/dist/cssm/components/Cell/useDraggable.js.map +1 -1
  33. package/dist/cssm/components/FocusTrap/FocusTrap.js +3 -5
  34. package/dist/cssm/components/FocusTrap/FocusTrap.js.map +1 -1
  35. package/dist/cssm/components/Tabbar/Tabbar.css +1 -1
  36. package/dist/cssm/components/Tabbar/Tabbar.js +3 -1
  37. package/dist/cssm/components/Tabbar/Tabbar.js.map +1 -1
  38. package/dist/cssm/components/TabbarItem/TabbarItem.css +1 -1
  39. package/dist/cssm/components/TabbarItem/TabbarItem.js +26 -10
  40. package/dist/cssm/components/TabbarItem/TabbarItem.js.map +1 -1
  41. package/dist/cssm/styles/components.css +1 -1
  42. package/dist/vkui.css +1 -1
  43. package/dist/vkui.css.map +1 -1
  44. package/package.json +1 -1
  45. package/src/components/CardScroll/CardScroll.tsx +4 -1
  46. package/src/components/Cell/useDraggable.tsx +1 -1
  47. package/src/components/Epic/Readme.md +1 -0
  48. package/src/components/FocusTrap/FocusTrap.tsx +11 -8
  49. package/src/components/Tabbar/Tabbar.css +15 -4
  50. package/src/components/Tabbar/Tabbar.tsx +3 -1
  51. package/src/components/TabbarItem/Readme.md +72 -0
  52. package/src/components/TabbarItem/TabbarItem.css +65 -14
  53. package/src/components/TabbarItem/TabbarItem.tsx +46 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vkontakte/vkui",
3
- "version": "4.22.0",
3
+ "version": "4.22.1",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -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.from(refContainer.current.children).find((el: HTMLElement) => el.offsetLeft + el.offsetWidth - offset > containerWidth) as HTMLElement;
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[] = Array.from(rootEl.parentElement.childNodes);
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
- // eslint-disable-next-line no-restricted-properties
58
- ref.current?.querySelectorAll(FOCUSABLE_ELEMENTS).forEach((focusableEl) => {
59
- const { display, visibility } = window.getComputedStyle(focusableEl);
60
-
61
- if (display !== 'none' && visibility !== 'hidden') {
62
- nodes.push(focusableEl as HTMLElement);
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
- display: flex;
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);
@@ -33,7 +33,9 @@ const Tabbar: React.FunctionComponent<TabbarProps> = (props: TabbarProps) => {
33
33
  })}
34
34
  {...restProps}
35
35
  >
36
- {children}
36
+ <div vkuiClass="Tabbar__in">
37
+ {children}
38
+ </div>
37
39
  </div>
38
40
  );
39
41
  };
@@ -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--l-vertical .TabbarItem.TabbarItem--text {
15
- position: relative;
16
- top: 2px;
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
- box-sizing: border-box;
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 React.HTMLAttributes<HTMLElement>, React.AnchorHTMLAttributes<HTMLElement> {
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> = (props: TabbarItemProps) => {
26
- const { children, selected, label, indicator, text, ...restProps } = props;
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
- return (
31
- <Component
32
- {...restProps}
33
- vkuiClass={classNames(getClassName('TabbarItem', platform), {
34
- 'TabbarItem--selected': selected,
35
- 'TabbarItem--text': !!text,
36
- })}
37
- >
38
- <div vkuiClass="TabbarItem__in">
39
- <div vkuiClass="TabbarItem__icon">
40
- {children}
41
- <div vkuiClass="TabbarItem__label">
42
- {hasReactNode(indicator) && indicator}
43
- {!indicator && label && <Counter size="s" mode="prominent">{label}</Counter>}
44
- </div>
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
- </Component>
49
- );
71
+ {text && <div vkuiClass="TabbarItem__text">{text}</div>}
72
+ </div>
73
+ </Component>);
50
74
  };
51
75
 
52
76
  export default TabbarItem;