@bitrise/bitkit 13.263.0 → 13.265.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 +1 -1
- package/src/Components/Dialog/Dialog.context.tsx +22 -0
- package/src/Components/Dialog/Dialog.tsx +38 -50
- package/src/Components/Dialog/DialogBody.tsx +95 -2
- package/src/Components/Dialog/DialogProps.ts +16 -0
- package/src/Components/TreeView/TreeView.theme.ts +5 -1
- package/src/Components/TreeView/TreeViewNode.tsx +23 -21
- package/src/index.ts +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createContext, ReactNode, useContext } from 'react';
|
|
2
|
+
import { DialogProps } from './DialogProps';
|
|
3
|
+
|
|
4
|
+
export type DialogContextValueType = {
|
|
5
|
+
scrollBehavior: DialogProps['scrollBehavior'];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const DialogContext = createContext<DialogContextValueType>({ scrollBehavior: 'outside' });
|
|
9
|
+
|
|
10
|
+
export const DialogContextProvider = ({ children, value }: { children: ReactNode; value: DialogContextValueType }) => {
|
|
11
|
+
return <DialogContext.Provider value={value}>{children}</DialogContext.Provider>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const useDialogContext = () => {
|
|
15
|
+
const context = useContext(DialogContext);
|
|
16
|
+
|
|
17
|
+
if (!context) {
|
|
18
|
+
throw new Error('DialogContext is not provided');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return context;
|
|
22
|
+
};
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { useId } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ComponentWithAs,
|
|
4
|
-
HTMLChakraProps,
|
|
5
4
|
Modal,
|
|
6
5
|
ModalCloseButton,
|
|
7
6
|
ModalContent,
|
|
8
7
|
ModalHeader,
|
|
9
8
|
ModalOverlay,
|
|
10
|
-
ModalProps,
|
|
11
9
|
useBreakpointValue,
|
|
12
10
|
usePrefersReducedMotion,
|
|
13
11
|
} from '@chakra-ui/react';
|
|
@@ -15,21 +13,8 @@ import { BREAKPOINTS } from '../../types/bitkit';
|
|
|
15
13
|
import Icon from '../Icon/Icon';
|
|
16
14
|
import Text from '../Text/Text';
|
|
17
15
|
import Tooltip from '../Tooltip/Tooltip';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
extends Omit<HTMLChakraProps<'section'>, 'scrollBehavior'>,
|
|
21
|
-
Pick<ModalProps, 'returnFocusOnClose'> {
|
|
22
|
-
isClosable?: boolean;
|
|
23
|
-
isOpen: boolean;
|
|
24
|
-
onClose(): void;
|
|
25
|
-
onCloseComplete?: () => void;
|
|
26
|
-
size?: 'small' | 'medium' | 'large' | 'full';
|
|
27
|
-
scrollBehavior?: 'inside' | 'outside';
|
|
28
|
-
title: string;
|
|
29
|
-
trapFocus?: boolean;
|
|
30
|
-
variant?: 'default' | 'empty';
|
|
31
|
-
closeNotice?: string;
|
|
32
|
-
}
|
|
16
|
+
import { DialogContextProvider } from './Dialog.context';
|
|
17
|
+
import { DialogProps } from './DialogProps';
|
|
33
18
|
|
|
34
19
|
const Dialog: ComponentWithAs<'section', DialogProps> = ({
|
|
35
20
|
children,
|
|
@@ -57,39 +42,42 @@ const Dialog: ComponentWithAs<'section', DialogProps> = ({
|
|
|
57
42
|
);
|
|
58
43
|
|
|
59
44
|
return (
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
45
|
+
<DialogContextProvider value={{ scrollBehavior }}>
|
|
46
|
+
<Modal
|
|
47
|
+
closeOnEsc={isClosable}
|
|
48
|
+
closeOnOverlayClick={false}
|
|
49
|
+
isOpen={isOpen}
|
|
50
|
+
motionPreset={prefersReducedMotion ? 'none' : 'scale'}
|
|
51
|
+
onClose={isClosable ? onClose : () => {}}
|
|
52
|
+
onCloseComplete={onCloseComplete}
|
|
53
|
+
returnFocusOnClose={returnFocusOnClose}
|
|
54
|
+
scrollBehavior={scrollBehavior}
|
|
55
|
+
size={dialogSize}
|
|
56
|
+
trapFocus={trapFocus}
|
|
57
|
+
>
|
|
58
|
+
<ModalOverlay />
|
|
59
|
+
|
|
60
|
+
<ModalContent aria-labelledby={variant !== 'empty' ? headerId : undefined} {...rest}>
|
|
61
|
+
{variant !== 'empty' && (
|
|
62
|
+
<>
|
|
63
|
+
<ModalHeader marginRight="48">
|
|
64
|
+
<Text as="h1" id={headerId} textStyle="comp/dialog/title">
|
|
65
|
+
{title}
|
|
66
|
+
</Text>
|
|
67
|
+
</ModalHeader>
|
|
68
|
+
{closeNotice ? (
|
|
69
|
+
<Tooltip label={closeNotice} placement="top">
|
|
70
|
+
{closeBtn}
|
|
71
|
+
</Tooltip>
|
|
72
|
+
) : (
|
|
73
|
+
closeBtn
|
|
74
|
+
)}
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
77
|
+
{children}
|
|
78
|
+
</ModalContent>
|
|
79
|
+
</Modal>
|
|
80
|
+
</DialogContextProvider>
|
|
93
81
|
);
|
|
94
82
|
};
|
|
95
83
|
|
|
@@ -1,9 +1,102 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRef, useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { ModalBody, ModalBodyProps, useModalContext } from '@chakra-ui/react';
|
|
3
|
+
import Box, { BoxProps } from '../Box/Box';
|
|
4
|
+
import Icon from '../Icon/Icon';
|
|
5
|
+
import { useDialogContext } from './Dialog.context';
|
|
6
|
+
|
|
7
|
+
const ScrollButton = (props: BoxProps) => {
|
|
8
|
+
return (
|
|
9
|
+
<Box
|
|
10
|
+
{...props}
|
|
11
|
+
as="button"
|
|
12
|
+
bg="background/primary"
|
|
13
|
+
borderColor="border/minimal"
|
|
14
|
+
borderRadius={16}
|
|
15
|
+
borderWidth={1}
|
|
16
|
+
boxShadow="large"
|
|
17
|
+
h={32}
|
|
18
|
+
w={32}
|
|
19
|
+
>
|
|
20
|
+
<Icon color="icon/tertiary" name="ArrowDown" size="16" />
|
|
21
|
+
</Box>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
2
24
|
|
|
3
25
|
export type DialogBodyProps = ModalBodyProps;
|
|
4
26
|
|
|
27
|
+
const ScrollableDialogBody = (props: DialogBodyProps) => {
|
|
28
|
+
const { isOpen } = useModalContext();
|
|
29
|
+
|
|
30
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
|
|
32
|
+
const [isScrollButtonVisible, setIsScrollButtonVisible] = useState(false);
|
|
33
|
+
|
|
34
|
+
const updateScrollButtonVisibility = useCallback(() => {
|
|
35
|
+
const content = contentRef.current;
|
|
36
|
+
|
|
37
|
+
if (!isOpen || !content) {
|
|
38
|
+
setIsScrollButtonVisible(false);
|
|
39
|
+
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const didScrollToBottom = content.scrollTop >= content.scrollHeight - content.offsetHeight - 1;
|
|
44
|
+
setIsScrollButtonVisible(!didScrollToBottom);
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const content = contentRef.current;
|
|
49
|
+
|
|
50
|
+
if (!isOpen || !content) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
updateScrollButtonVisibility();
|
|
55
|
+
|
|
56
|
+
content.addEventListener('scroll', updateScrollButtonVisibility);
|
|
57
|
+
const resizeObserver = new ResizeObserver(updateScrollButtonVisibility);
|
|
58
|
+
resizeObserver.observe(content);
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
content.removeEventListener('scroll', updateScrollButtonVisibility);
|
|
62
|
+
resizeObserver.unobserve(content);
|
|
63
|
+
resizeObserver.disconnect();
|
|
64
|
+
};
|
|
65
|
+
}, [isOpen]);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Box display="flex" flexDir="column" minH={0} position="relative">
|
|
69
|
+
<ModalBody ref={contentRef} {...props} />
|
|
70
|
+
{isScrollButtonVisible && (
|
|
71
|
+
<ScrollButton
|
|
72
|
+
alignSelf="center"
|
|
73
|
+
bottom={8}
|
|
74
|
+
flexShrink={0}
|
|
75
|
+
onClick={() => {
|
|
76
|
+
if (!contentRef.current) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
contentRef.current.scrollTo({
|
|
81
|
+
top: contentRef.current.scrollHeight,
|
|
82
|
+
behavior: 'smooth',
|
|
83
|
+
});
|
|
84
|
+
}}
|
|
85
|
+
position="absolute"
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
</Box>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
5
92
|
const DialogBody = (props: DialogBodyProps) => {
|
|
6
|
-
|
|
93
|
+
const { scrollBehavior } = useDialogContext();
|
|
94
|
+
|
|
95
|
+
if (scrollBehavior === 'outside') {
|
|
96
|
+
return <ModalBody {...props} />;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return <ScrollableDialogBody {...props} />;
|
|
7
100
|
};
|
|
8
101
|
|
|
9
102
|
export default DialogBody;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { HTMLChakraProps, ModalProps } from '@chakra-ui/react';
|
|
2
|
+
|
|
3
|
+
export interface DialogProps
|
|
4
|
+
extends Omit<HTMLChakraProps<'section'>, 'scrollBehavior'>,
|
|
5
|
+
Pick<ModalProps, 'returnFocusOnClose'> {
|
|
6
|
+
isClosable?: boolean;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
onClose(): void;
|
|
9
|
+
onCloseComplete?: () => void;
|
|
10
|
+
size?: 'small' | 'medium' | 'large' | 'full';
|
|
11
|
+
scrollBehavior?: 'inside' | 'outside';
|
|
12
|
+
title: string;
|
|
13
|
+
trapFocus?: boolean;
|
|
14
|
+
variant?: 'default' | 'empty';
|
|
15
|
+
closeNotice?: string;
|
|
16
|
+
}
|
|
@@ -39,12 +39,16 @@ const baseStyle = definePartsStyle({
|
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
41
|
buttonContent: {
|
|
42
|
+
gap: '8',
|
|
43
|
+
display: 'flex',
|
|
44
|
+
},
|
|
45
|
+
borderBox: {
|
|
42
46
|
w: '100%',
|
|
43
47
|
gap: '8',
|
|
44
48
|
display: 'flex',
|
|
45
49
|
alignItems: 'flex-start',
|
|
46
50
|
textAlign: 'start',
|
|
47
|
-
|
|
51
|
+
borderBottom: '1px solid',
|
|
48
52
|
borderColor: 'border/minimal',
|
|
49
53
|
},
|
|
50
54
|
textBlock: {
|
|
@@ -60,31 +60,33 @@ const TreeViewNodeContent = memo(
|
|
|
60
60
|
>
|
|
61
61
|
<Box sx={{ ...styles.buttonContent }}>
|
|
62
62
|
{isBranch && <Icon size="16" name={isExpanded ? 'ChevronDown' : 'ChevronRight'} sx={styles.icon} />}
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<Box sx={styles.textBlock}>
|
|
71
|
-
<Text fontWeight={isExpanded ? 'semibold' : 'normal'}>{title}</Text>
|
|
72
|
-
{description && (
|
|
73
|
-
<Text textStyle="body/sm/regular" color="text/secondary">
|
|
74
|
-
{description}
|
|
75
|
-
</Text>
|
|
63
|
+
<Box sx={styles.borderBox}>
|
|
64
|
+
{iconName && (
|
|
65
|
+
<Icon
|
|
66
|
+
size="16"
|
|
67
|
+
name={iconName}
|
|
68
|
+
sx={{ ...styles.icon, ...{ color: iconColor || (isSelected ? 'icon/primary' : 'icon/secondary') } }}
|
|
69
|
+
/>
|
|
76
70
|
)}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<Text textStyle="body/md/regular" textAlign="right">
|
|
83
|
-
{label}
|
|
71
|
+
<Box sx={styles.textBlock}>
|
|
72
|
+
<Text fontWeight={isExpanded ? 'semibold' : 'normal'}>{title}</Text>
|
|
73
|
+
{description && (
|
|
74
|
+
<Text textStyle="body/sm/regular" color="text/secondary">
|
|
75
|
+
{description}
|
|
84
76
|
</Text>
|
|
85
77
|
)}
|
|
86
78
|
</Box>
|
|
87
|
-
|
|
79
|
+
{(label || labelIconName) && (
|
|
80
|
+
<Box sx={styles.suffixBlock} color={isSelected || isExpanded ? 'text/primary' : 'text/secondary'}>
|
|
81
|
+
{labelIconName && <Icon name={labelIconName} size="16" color={labelIconColor} />}
|
|
82
|
+
{label && (
|
|
83
|
+
<Text textStyle="body/md/regular" textAlign="right">
|
|
84
|
+
{label}
|
|
85
|
+
</Text>
|
|
86
|
+
)}
|
|
87
|
+
</Box>
|
|
88
|
+
)}
|
|
89
|
+
</Box>
|
|
88
90
|
{isSelected && <Box sx={styles.selectionIndicator} />}
|
|
89
91
|
</Box>
|
|
90
92
|
</Box>
|
package/src/index.ts
CHANGED
|
@@ -95,7 +95,7 @@ export { default as useToast } from './Components/Toast/Toast';
|
|
|
95
95
|
export type { EmptyStateProps } from './Components/EmptyState/EmptyState';
|
|
96
96
|
export { default as EmptyState } from './Components/EmptyState/EmptyState';
|
|
97
97
|
|
|
98
|
-
export type { DialogProps } from './Components/Dialog/
|
|
98
|
+
export type { DialogProps } from './Components/Dialog/DialogProps';
|
|
99
99
|
export { default as Dialog } from './Components/Dialog/Dialog';
|
|
100
100
|
|
|
101
101
|
export type { DialogBodyProps } from './Components/Dialog/DialogBody';
|