@bitrise/bitkit 12.42.1 → 12.43.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
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
|
|
2
|
+
|
|
3
|
+
const { defineMultiStyleConfig } = createMultiStyleConfigHelpers(['container', 'label', 'input']);
|
|
4
|
+
|
|
5
|
+
const getContainerType = (hasSelectedFiles?: boolean) => {
|
|
6
|
+
if (hasSelectedFiles) {
|
|
7
|
+
return {
|
|
8
|
+
display: 'flex',
|
|
9
|
+
alignItems: 'center',
|
|
10
|
+
justifyContent: 'space-between',
|
|
11
|
+
borderRadius: '8',
|
|
12
|
+
backgroundColor: 'neutral.100',
|
|
13
|
+
paddingX: '24',
|
|
14
|
+
paddingY: '16',
|
|
15
|
+
border: '1px solid',
|
|
16
|
+
borderColor: 'neutral.90',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
borderRadius: '8',
|
|
21
|
+
backgroundColor: 'neutral.95',
|
|
22
|
+
padding: '8',
|
|
23
|
+
cursor: 'pointer',
|
|
24
|
+
_hover: {
|
|
25
|
+
backgroundColor: 'purple.93',
|
|
26
|
+
},
|
|
27
|
+
_active: {
|
|
28
|
+
backgroundColor: 'purple.93',
|
|
29
|
+
},
|
|
30
|
+
_disabled: {
|
|
31
|
+
_active: {
|
|
32
|
+
backgroundColor: 'neutral.95',
|
|
33
|
+
},
|
|
34
|
+
_hover: {
|
|
35
|
+
backgroundColor: 'neutral.95',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const FileInputTheme = defineMultiStyleConfig({
|
|
42
|
+
baseStyle: ({ hasSelectedFiles }) => ({
|
|
43
|
+
container: getContainerType(hasSelectedFiles),
|
|
44
|
+
label: {
|
|
45
|
+
display: 'flex',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
justifyContent: 'center',
|
|
48
|
+
gap: '8',
|
|
49
|
+
padding: '24',
|
|
50
|
+
border: '1px dashed',
|
|
51
|
+
borderColor: 'purple.50',
|
|
52
|
+
borderRadius: '4',
|
|
53
|
+
color: 'purple.50',
|
|
54
|
+
cursor: 'pointer',
|
|
55
|
+
_active: {
|
|
56
|
+
color: 'purple.10',
|
|
57
|
+
borderColor: 'purple.10',
|
|
58
|
+
},
|
|
59
|
+
_disabled: {
|
|
60
|
+
borderColor: 'neutral.60',
|
|
61
|
+
color: 'neutral.60',
|
|
62
|
+
cursor: 'not-allowed',
|
|
63
|
+
},
|
|
64
|
+
_invalid: {
|
|
65
|
+
borderColor: 'red.50',
|
|
66
|
+
color: 'red.50',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
input: {
|
|
70
|
+
display: 'none',
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export default FileInputTheme;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ChangeEvent, DragEvent, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
FormControl,
|
|
4
|
+
FormControlProps,
|
|
5
|
+
FormErrorMessage,
|
|
6
|
+
FormHelperText,
|
|
7
|
+
forwardRef,
|
|
8
|
+
useId,
|
|
9
|
+
useMultiStyleConfig,
|
|
10
|
+
} from '@chakra-ui/react';
|
|
11
|
+
import Box from '../../Box/Box';
|
|
12
|
+
import Button from '../../Button/Button';
|
|
13
|
+
import Icon from '../../Icon/Icon';
|
|
14
|
+
import Text from '../../Text/Text';
|
|
15
|
+
|
|
16
|
+
export interface FileInputProps extends Omit<FormControlProps, 'onChange'> {
|
|
17
|
+
accept?: HTMLInputElement['accept'];
|
|
18
|
+
disabledLabel?: string;
|
|
19
|
+
errorText?: string;
|
|
20
|
+
helperText?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
onChange: (files: FileList | null) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const FileInput = forwardRef<FileInputProps, 'div'>((props, ref) => {
|
|
26
|
+
const {
|
|
27
|
+
accept,
|
|
28
|
+
disabledLabel,
|
|
29
|
+
errorText,
|
|
30
|
+
helperText,
|
|
31
|
+
isDisabled,
|
|
32
|
+
isInvalid,
|
|
33
|
+
isRequired,
|
|
34
|
+
label,
|
|
35
|
+
name,
|
|
36
|
+
onChange,
|
|
37
|
+
...rest
|
|
38
|
+
} = props;
|
|
39
|
+
|
|
40
|
+
const id = useId();
|
|
41
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
42
|
+
|
|
43
|
+
const [selectedFileNames, setSelectedFileNames] = useState<string[]>([]);
|
|
44
|
+
const [internalErrorMessage, setInternalErrorMessage] = useState('');
|
|
45
|
+
|
|
46
|
+
const style = useMultiStyleConfig('FileInput', { hasSelectedFiles: selectedFileNames.length > 0 });
|
|
47
|
+
|
|
48
|
+
const handleFiles = async (files: FileList | null) => {
|
|
49
|
+
setSelectedFileNames(files ? Array.from(files).map((file) => file.name) : []);
|
|
50
|
+
onChange(files);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const onFileInputChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
54
|
+
if (event.target.files) {
|
|
55
|
+
handleFiles(event.target.files);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const onDragOver = (event: DragEvent<HTMLElement>) => {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const onDrop = (event: DragEvent<HTMLElement>) => {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
if (isDisabled) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (accept) {
|
|
69
|
+
const acceptedFormats = accept.split(', ');
|
|
70
|
+
if (!acceptedFormats.includes(event.dataTransfer.files[0].type)) {
|
|
71
|
+
setInternalErrorMessage(`Invalid format. You can upload only ${accept} file.`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (inputRef.current) {
|
|
76
|
+
inputRef.current.files = event.dataTransfer.files;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
handleFiles(event.dataTransfer.files);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const onRemoveClick = () => {
|
|
83
|
+
if (inputRef.current) {
|
|
84
|
+
inputRef.current.value = '';
|
|
85
|
+
}
|
|
86
|
+
onChange(null);
|
|
87
|
+
setSelectedFileNames([]);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const isInputInvalid = isInvalid || !!internalErrorMessage;
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<FormControl {...rest} isDisabled={isDisabled} isInvalid={isInputInvalid} ref={ref}>
|
|
94
|
+
<Box __css={style.container} data-disabled={isDisabled || undefined}>
|
|
95
|
+
{selectedFileNames.length > 0 ? (
|
|
96
|
+
<>
|
|
97
|
+
<div>
|
|
98
|
+
<Text as="h6" size="2" fontWeight="bold">
|
|
99
|
+
Selected file
|
|
100
|
+
</Text>
|
|
101
|
+
<Text as="span">{selectedFileNames[0]}</Text>
|
|
102
|
+
</div>
|
|
103
|
+
<Button leftIconName="MinusRemove" size="small" variant="secondary" onClick={onRemoveClick}>
|
|
104
|
+
Remove
|
|
105
|
+
</Button>
|
|
106
|
+
</>
|
|
107
|
+
) : (
|
|
108
|
+
<Box
|
|
109
|
+
as="label"
|
|
110
|
+
aria-disabled={isDisabled || undefined}
|
|
111
|
+
data-invalid={isInputInvalid || undefined}
|
|
112
|
+
htmlFor={id}
|
|
113
|
+
onDragOver={onDragOver}
|
|
114
|
+
onDrop={onDrop}
|
|
115
|
+
__css={style.label}
|
|
116
|
+
>
|
|
117
|
+
<Icon name="Upload" size="24" />
|
|
118
|
+
{isDisabled && disabledLabel ? disabledLabel : label}
|
|
119
|
+
</Box>
|
|
120
|
+
)}
|
|
121
|
+
</Box>
|
|
122
|
+
<FormErrorMessage as="p" marginBlockStart="8">
|
|
123
|
+
{errorText || internalErrorMessage}
|
|
124
|
+
</FormErrorMessage>
|
|
125
|
+
{helperText && (
|
|
126
|
+
<FormHelperText as="p" marginBlockStart={isInvalid ? '4' : '8'}>
|
|
127
|
+
{helperText}
|
|
128
|
+
</FormHelperText>
|
|
129
|
+
)}
|
|
130
|
+
<Box
|
|
131
|
+
as="input"
|
|
132
|
+
aria-hidden="true"
|
|
133
|
+
accept={accept}
|
|
134
|
+
disabled={isDisabled}
|
|
135
|
+
id={id}
|
|
136
|
+
name={name}
|
|
137
|
+
type="file"
|
|
138
|
+
onChange={onFileInputChange}
|
|
139
|
+
ref={inputRef}
|
|
140
|
+
__css={style.input}
|
|
141
|
+
/>
|
|
142
|
+
</FormControl>
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
FileInput.defaultProps = {
|
|
147
|
+
label: 'Drag and drop file here or click to select',
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default FileInput;
|
package/src/index.ts
CHANGED
|
@@ -301,3 +301,6 @@ export { default as TableIconButton } from './Components/Table/TableIconButton';
|
|
|
301
301
|
|
|
302
302
|
export type { ExpandableCardProps } from './Components/ExpandableCard/ExpandableCard';
|
|
303
303
|
export { default as ExpandableCard } from './Components/ExpandableCard/ExpandableCard';
|
|
304
|
+
|
|
305
|
+
export type { FileInputProps } from './Components/Form/FileInput/FileInput';
|
|
306
|
+
export { default as FileInput } from './Components/Form/FileInput/FileInput';
|
package/src/theme.ts
CHANGED
|
@@ -40,6 +40,7 @@ import Note from './Components/Note/Note.theme';
|
|
|
40
40
|
import CodeBlock from './Components/CodeBlock/CodeBlock.theme';
|
|
41
41
|
import DefinitionTooltip from './Components/DefinitionTooltip/DefinitionTooltip.theme';
|
|
42
42
|
import ExpandableCard from './Components/ExpandableCard/ExpandableCard.theme';
|
|
43
|
+
import FileInput from './Components/Form/FileInput/FileInput.theme';
|
|
43
44
|
|
|
44
45
|
import breakpoints from './Foundations/Breakpoints/Breakpoints';
|
|
45
46
|
import colors from './Foundations/Colors/Colors';
|
|
@@ -127,6 +128,7 @@ const theme = {
|
|
|
127
128
|
CodeBlock,
|
|
128
129
|
DefinitionTooltip,
|
|
129
130
|
ExpandableCard,
|
|
131
|
+
FileInput,
|
|
130
132
|
},
|
|
131
133
|
};
|
|
132
134
|
|