@bitrise/bitkit 13.44.0 → 13.45.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/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MouseEvent, useEffect } from 'react';
|
|
1
2
|
import { Button, forwardRef, TabProps as ChakraTabProps, useTab, useTabsStyles } from '@chakra-ui/react';
|
|
2
3
|
import { TypeColors } from '../../types/bitkit';
|
|
3
4
|
import Box from '../Box/Box';
|
|
@@ -5,6 +6,7 @@ import Icon, { TypeIconName } from '../Icon/Icon';
|
|
|
5
6
|
import Text from '../Text/Text';
|
|
6
7
|
|
|
7
8
|
export interface ContainedTabProps extends ChakraTabProps {
|
|
9
|
+
href?: string;
|
|
8
10
|
id: string;
|
|
9
11
|
iconColor?: TypeColors | 'currentColor';
|
|
10
12
|
iconName?: TypeIconName;
|
|
@@ -13,11 +15,36 @@ export interface ContainedTabProps extends ChakraTabProps {
|
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
const ContainedTab = forwardRef<ContainedTabProps, 'button'>((props, ref) => {
|
|
16
|
-
const { iconColor = 'currentColor', iconName, isWarning, secondaryText, ...rest } = props;
|
|
17
|
-
|
|
18
|
+
const { href, iconColor = 'currentColor', iconName, isDisabled, isWarning, secondaryText, ...rest } = props;
|
|
19
|
+
|
|
18
20
|
const styles = useTabsStyles();
|
|
21
|
+
|
|
22
|
+
const tabProps = useTab({
|
|
23
|
+
href,
|
|
24
|
+
isDisabled,
|
|
25
|
+
onClick: (e: MouseEvent<HTMLButtonElement>) => {
|
|
26
|
+
if (!e.metaKey) {
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
ref,
|
|
31
|
+
...rest,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (tabProps['aria-selected']) {
|
|
36
|
+
document.getElementById(tabProps.id)?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
37
|
+
}
|
|
38
|
+
}, [tabProps]);
|
|
39
|
+
|
|
19
40
|
return (
|
|
20
|
-
<Button
|
|
41
|
+
<Button
|
|
42
|
+
as={href ? 'a' : 'button'}
|
|
43
|
+
isDisabled={isDisabled}
|
|
44
|
+
__css={styles.containedTab}
|
|
45
|
+
{...tabProps}
|
|
46
|
+
type={href ? undefined : 'button'}
|
|
47
|
+
>
|
|
21
48
|
<Box display="flex" gap="16" justifyContent="space-between">
|
|
22
49
|
<Text as="span" hasEllipsis textStyle="comp/tabs/contained">
|
|
23
50
|
{tabProps.children}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { MouseEvent, useEffect } from 'react';
|
|
1
2
|
import { Button, forwardRef, TabProps as ChakraTabProps, useTab, useTabsStyles } from '@chakra-ui/react';
|
|
2
3
|
import Badge, { BadgeProps } from '../Badge/Badge';
|
|
3
4
|
import Icon, { TypeIconName } from '../Icon/Icon';
|
|
4
5
|
|
|
5
6
|
export interface TabProps extends ChakraTabProps {
|
|
6
7
|
badge?: BadgeProps;
|
|
8
|
+
href?: string;
|
|
7
9
|
id: string;
|
|
8
10
|
leftIconName?: TypeIconName;
|
|
9
11
|
isDisabled?: boolean;
|
|
@@ -11,11 +13,36 @@ export interface TabProps extends ChakraTabProps {
|
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
const Tab = forwardRef<TabProps, 'button'>((props, ref) => {
|
|
14
|
-
const { badge, leftIconName, rightIconName, ...rest } = props;
|
|
15
|
-
|
|
16
|
+
const { badge, href, isDisabled, leftIconName, rightIconName, ...rest } = props;
|
|
17
|
+
|
|
16
18
|
const styles = useTabsStyles();
|
|
19
|
+
|
|
20
|
+
const tabProps = useTab({
|
|
21
|
+
href,
|
|
22
|
+
isDisabled,
|
|
23
|
+
onClick: (e: MouseEvent<HTMLButtonElement>) => {
|
|
24
|
+
if (!e.metaKey) {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
ref,
|
|
29
|
+
...rest,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (tabProps['aria-selected']) {
|
|
34
|
+
document.getElementById(tabProps.id)?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
|
|
35
|
+
}
|
|
36
|
+
}, [tabProps]);
|
|
37
|
+
|
|
17
38
|
return (
|
|
18
|
-
<Button
|
|
39
|
+
<Button
|
|
40
|
+
as={href ? 'a' : 'button'}
|
|
41
|
+
isDisabled={isDisabled}
|
|
42
|
+
__css={styles.lineTab}
|
|
43
|
+
{...tabProps}
|
|
44
|
+
type={href ? undefined : 'button'}
|
|
45
|
+
>
|
|
19
46
|
{leftIconName && <Icon name={leftIconName} />}
|
|
20
47
|
<span>{tabProps.children}</span>
|
|
21
48
|
{badge?.children && <Badge {...badge} />}
|
|
@@ -1,7 +1,100 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { forwardRef, TabList as ChakraTabList, TabListProps, useTabsStyles } from '@chakra-ui/react';
|
|
3
|
+
import IconButton from '../IconButton/IconButton';
|
|
4
|
+
import Box from '../Box/Box';
|
|
2
5
|
|
|
3
6
|
const TabList = forwardRef<TabListProps, 'div'>((props, ref) => {
|
|
4
|
-
|
|
7
|
+
const styles: Record<string, any> = useTabsStyles();
|
|
8
|
+
|
|
9
|
+
const tabListRef = useRef<HTMLDivElement>(null);
|
|
10
|
+
const [isContentWider, setIsContentWider] = useState(false);
|
|
11
|
+
const [isLeftArrowDisabled, setIsLeftrrowDisabled] = useState(true);
|
|
12
|
+
const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(true);
|
|
13
|
+
|
|
14
|
+
const updateArrowButtonStates = () => {
|
|
15
|
+
const tabList = tabListRef.current;
|
|
16
|
+
if (!tabList) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setIsLeftrrowDisabled(Math.floor(tabList.scrollLeft) === 0);
|
|
21
|
+
setIsRightArrowDisabled(Math.ceil(tabList.scrollLeft) + tabList.clientWidth >= tabList.scrollWidth);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const handleResize = () => {
|
|
26
|
+
const tabList = tabListRef.current;
|
|
27
|
+
if (!tabList) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const viewPortWidth = document.documentElement.clientWidth;
|
|
32
|
+
const tabListContentWidth = tabList.scrollWidth;
|
|
33
|
+
|
|
34
|
+
setIsContentWider(tabListContentWidth > viewPortWidth);
|
|
35
|
+
updateArrowButtonStates();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
window.addEventListener('resize', handleResize);
|
|
39
|
+
handleResize();
|
|
40
|
+
|
|
41
|
+
const tabList = tabListRef.current;
|
|
42
|
+
if (tabList) {
|
|
43
|
+
tabList.addEventListener('scroll', updateArrowButtonStates);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
window.removeEventListener('resize', handleResize);
|
|
48
|
+
if (tabList) {
|
|
49
|
+
tabList.removeEventListener('scroll', updateArrowButtonStates);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}, [tabListRef]);
|
|
53
|
+
|
|
54
|
+
const handleScroll = (direction: 'left' | 'right') => {
|
|
55
|
+
const tabList = tabListRef.current;
|
|
56
|
+
if (!tabList) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const scrollAmount = direction === 'left' ? -(tabList.offsetWidth / 8) : tabList.offsetWidth / 8;
|
|
61
|
+
const newPosition = tabList.scrollLeft + scrollAmount;
|
|
62
|
+
|
|
63
|
+
tabList.scrollTo({
|
|
64
|
+
left: newPosition,
|
|
65
|
+
behavior: 'smooth',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
updateArrowButtonStates();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Box display="flex" alignItems="center" justifyContent="flex-start" ref={ref}>
|
|
73
|
+
{isContentWider && !isLeftArrowDisabled && (
|
|
74
|
+
<IconButton
|
|
75
|
+
__css={styles.arrowButton}
|
|
76
|
+
iconName="ChevronLeft"
|
|
77
|
+
onClick={() => handleScroll('left')}
|
|
78
|
+
aria-label="Scroll back"
|
|
79
|
+
isTooltipDisabled
|
|
80
|
+
left="0"
|
|
81
|
+
_after={styles.arrowButtonGradient('left')}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
<ChakraTabList {...props} ref={tabListRef} />
|
|
85
|
+
{isContentWider && !isRightArrowDisabled && (
|
|
86
|
+
<IconButton
|
|
87
|
+
__css={styles.arrowButton}
|
|
88
|
+
iconName="ChevronRight"
|
|
89
|
+
onClick={() => handleScroll('right')}
|
|
90
|
+
aria-label="Scroll forward"
|
|
91
|
+
isTooltipDisabled
|
|
92
|
+
right="0"
|
|
93
|
+
_after={styles.arrowButtonGradient('right')}
|
|
94
|
+
/>
|
|
95
|
+
)}
|
|
96
|
+
</Box>
|
|
97
|
+
);
|
|
5
98
|
});
|
|
6
99
|
|
|
7
100
|
export type { TabListProps };
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
import { defineStyle, defineStyleConfig, SystemStyleObject } from '@chakra-ui/styled-system';
|
|
2
|
+
import { rem } from '../../utils/utils';
|
|
3
|
+
|
|
4
|
+
const getGradient = (position: 'left' | 'right', variant: 'contained' | 'line') => {
|
|
5
|
+
const style: SystemStyleObject = {
|
|
6
|
+
content: `""`,
|
|
7
|
+
position: 'absolute',
|
|
8
|
+
width: '8',
|
|
9
|
+
top: '0',
|
|
10
|
+
bottom: '0',
|
|
11
|
+
bgGradient: `linear(${position === 'left' ? 'to-r' : 'to-l'}, ${
|
|
12
|
+
variant === 'line' ? 'background/primary' : 'background/tertiary'
|
|
13
|
+
}, transparent)`,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (position === 'left') {
|
|
17
|
+
style.right = '-8px';
|
|
18
|
+
} else {
|
|
19
|
+
style.left = '-8px';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return style;
|
|
23
|
+
};
|
|
2
24
|
|
|
3
25
|
const baseStyle = defineStyle(
|
|
4
26
|
({ variant }): SystemStyleObject => ({
|
|
5
27
|
containedTab: {
|
|
6
|
-
_disabled: {
|
|
7
|
-
_hover: {
|
|
8
|
-
backgroundColor: 'neutral.90',
|
|
9
|
-
},
|
|
10
|
-
backgroundColor: 'neutral.90',
|
|
11
|
-
color: 'neutral.60',
|
|
12
|
-
cursor: 'not-allowed',
|
|
13
|
-
},
|
|
14
28
|
_first: {
|
|
15
29
|
_focusVisible: {
|
|
16
30
|
boxShadow: 'inset 0 2px var(--colors-purple-50), inset 0 0 0 3px var(--colors-purple-70)',
|
|
@@ -23,6 +37,11 @@ const baseStyle = defineStyle(
|
|
|
23
37
|
_hover: {
|
|
24
38
|
backgroundColor: 'background/active',
|
|
25
39
|
},
|
|
40
|
+
_disabled: {
|
|
41
|
+
backgroundColor: 'neutral.90',
|
|
42
|
+
color: 'neutral.70',
|
|
43
|
+
cursor: 'not-allowed',
|
|
44
|
+
},
|
|
26
45
|
_selected: {
|
|
27
46
|
'+ [role="tab"]': {
|
|
28
47
|
boxShadow: 'none',
|
|
@@ -49,22 +68,20 @@ const baseStyle = defineStyle(
|
|
|
49
68
|
whiteSpace: 'nowrap',
|
|
50
69
|
},
|
|
51
70
|
lineTab: {
|
|
71
|
+
_hover: {
|
|
72
|
+
backgroundColor: 'background/secondary',
|
|
73
|
+
},
|
|
52
74
|
_active: {
|
|
53
75
|
backgroundColor: 'neutral.93',
|
|
54
76
|
},
|
|
55
77
|
_disabled: {
|
|
56
|
-
|
|
57
|
-
background: 'transparent',
|
|
58
|
-
},
|
|
78
|
+
background: 'transparent',
|
|
59
79
|
color: 'neutral.70',
|
|
60
80
|
cursor: 'not-allowed',
|
|
61
81
|
},
|
|
62
82
|
_focusVisible: {
|
|
63
83
|
boxShadow: 'inset 0 -2px var(--colors-purple-50), inset 0 0 0 3px var(--colors-purple-70)',
|
|
64
84
|
},
|
|
65
|
-
_hover: {
|
|
66
|
-
backgroundColor: 'neutral.95',
|
|
67
|
-
},
|
|
68
85
|
_selected: {
|
|
69
86
|
_hover: {
|
|
70
87
|
backgroundColor: 'transparent',
|
|
@@ -86,14 +103,31 @@ const baseStyle = defineStyle(
|
|
|
86
103
|
borderBottom: variant === 'line' ? '1px solid' : undefined,
|
|
87
104
|
borderBottomColor: variant === 'line' ? 'separator.primary' : undefined,
|
|
88
105
|
maxWidth: '100%',
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
overflowY: 'hidden',
|
|
107
|
+
overflowX: 'scroll',
|
|
108
|
+
scrollbarWidth: 'none',
|
|
109
|
+
'::-webkit-scrollbar': {
|
|
110
|
+
width: 0,
|
|
111
|
+
},
|
|
91
112
|
},
|
|
92
113
|
tabpanel: {
|
|
93
114
|
_focusVisible: {
|
|
94
115
|
boxShadow: 'none',
|
|
95
116
|
},
|
|
96
117
|
},
|
|
118
|
+
arrowButton: {
|
|
119
|
+
width: variant === 'line' ? '48' : rem(68),
|
|
120
|
+
height: variant === 'line' ? '48' : rem(68),
|
|
121
|
+
borderRadius: 0,
|
|
122
|
+
backgroundColor: variant === 'line' ? 'background/primary' : 'background/tertiary',
|
|
123
|
+
flexShrink: 0,
|
|
124
|
+
color: 'icon/primary',
|
|
125
|
+
paddingY: '12',
|
|
126
|
+
paddingX: '16',
|
|
127
|
+
position: 'absolute',
|
|
128
|
+
top: '0',
|
|
129
|
+
},
|
|
130
|
+
arrowButtonGradient: ((position: 'left' | 'right') => getGradient(position, variant)) as any,
|
|
97
131
|
}),
|
|
98
132
|
);
|
|
99
133
|
|