@bitrise/bitkit 12.89.0 → 12.90.1-alpha.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,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit",
3
3
  "description": "Bitrise React component library",
4
- "version": "12.89.0",
4
+ "version": "12.90.1-alpha.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+ssh://git@github.com/bitrise-io/bitkit.git"
@@ -0,0 +1,19 @@
1
+ import { defineStyleConfig } from '@chakra-ui/styled-system';
2
+
3
+ const ChipInput = defineStyleConfig({
4
+ baseStyle: {
5
+ ':has(:focus-visible)': { boxShadow: 'outline' },
6
+ appearance: 'none',
7
+ background: 'neutral.100',
8
+ border: '1px solid',
9
+ borderColor: 'neutral.90',
10
+ borderRadius: '4',
11
+ boxShadow: 'inner',
12
+ outline: 0,
13
+ padding: '0.6875rem',
14
+ transition: '200ms',
15
+ width: '100%',
16
+ },
17
+ });
18
+
19
+ export default ChipInput;
@@ -0,0 +1,142 @@
1
+ import { ClipboardEventHandler, KeyboardEventHandler, ReactNode, useId } from 'react';
2
+ import {
3
+ chakra,
4
+ FormControl,
5
+ FormControlProps,
6
+ FormErrorMessage,
7
+ FormHelperText,
8
+ FormLabel,
9
+ useStyleConfig,
10
+ } from '@chakra-ui/react';
11
+ import Icon from '../../Icon/Icon';
12
+ import Box from '../../Box/Box';
13
+ import Tag from '../../Tag/Tag';
14
+ import Text from '../../Text/Text';
15
+
16
+ type UsedFormControlProps = Omit<FormControlProps, 'label' | 'onBlur' | 'onChange'>;
17
+ export interface ChipInputProps extends UsedFormControlProps {
18
+ label?: string;
19
+ placeholder?: string;
20
+ value: string[];
21
+ onChange: (nv: string[]) => void;
22
+ separator?: RegExp;
23
+ maxCount?: number;
24
+ invalidValues?: string[];
25
+ errorText?: ReactNode;
26
+ helperText?: ReactNode;
27
+ }
28
+
29
+ const ChipInput = ({
30
+ errorText,
31
+ helperText,
32
+ invalidValues = [],
33
+ label,
34
+ maxCount,
35
+ onChange,
36
+ placeholder,
37
+ separator = /[, ]/,
38
+ value,
39
+ ...rest
40
+ }: ChipInputProps) => {
41
+ const theme = useStyleConfig('ChipInput');
42
+ const keyupHandler: KeyboardEventHandler<HTMLInputElement> = (ev) => {
43
+ const target = ev.currentTarget;
44
+ if (ev.key.match(separator)) {
45
+ ev.preventDefault();
46
+ if (target.value) {
47
+ const newValue = target.value.split(separator)[0];
48
+ if (!value.includes(newValue)) {
49
+ onChange([...value, newValue]);
50
+ }
51
+ target.value = '';
52
+ }
53
+ } else if (ev.key === 'Backspace' && target.value.length === 0) {
54
+ onChange(value.slice(0, -1));
55
+ }
56
+ };
57
+ const pasteEventHandler: ClipboardEventHandler<HTMLInputElement> = (ev) => {
58
+ ev.preventDefault();
59
+ const newItems = ev.clipboardData
60
+ .getData('text/plain')
61
+ .split(separator)
62
+ .filter((item) => item && !item.match(/\s/) && !value.includes(item));
63
+ onChange([...value, ...newItems]);
64
+ };
65
+ const removeItem = (deleted: string) => {
66
+ onChange(value.filter((val) => val !== deleted));
67
+ };
68
+ const isInvalid = rest.isInvalid || (maxCount && value.length > maxCount) || Boolean(errorText);
69
+ const id = useId();
70
+ return (
71
+ <FormControl {...rest} isInvalid={isInvalid}>
72
+ <Box alignItems="center" display={label || maxCount ? 'flex' : 'none'} gap="4" marginBlockEnd="4">
73
+ {label && (
74
+ <FormLabel
75
+ htmlFor={id}
76
+ optionalIndicator={
77
+ <Text as="span" color="neutral.40" fontSize="2" fontWeight="normal" marginLeft="4px">
78
+ (Optional)
79
+ </Text>
80
+ }
81
+ requiredIndicator={false}
82
+ >
83
+ {label}
84
+ </FormLabel>
85
+ )}
86
+
87
+ {maxCount && (
88
+ <Text
89
+ as="span"
90
+ color="neutral.40"
91
+ marginInlineStart="auto"
92
+ size="2"
93
+ sx={{ fontVariantNumeric: 'tabular-nums' }}
94
+ >
95
+ {value.length}/{maxCount}
96
+ </Text>
97
+ )}
98
+ </Box>
99
+ <Box
100
+ __css={theme}
101
+ display="flex"
102
+ flexDirection="row"
103
+ flexWrap="wrap"
104
+ gap="4"
105
+ paddingRight={isInvalid ? '42px' : undefined}
106
+ >
107
+ {value.map((item, idx) => (
108
+ <Tag
109
+ key={item}
110
+ colorScheme={invalidValues.includes(item) || (maxCount && idx > maxCount - 1) ? 'red' : undefined}
111
+ onClose={() => removeItem(item)}
112
+ size="sm"
113
+ >
114
+ {item}
115
+ </Tag>
116
+ ))}
117
+ {value.length === 0 && placeholder && (
118
+ <Text
119
+ color="sys.fg.subtle"
120
+ pointerEvents="none"
121
+ position="absolute"
122
+ sx={{ ':has(+ :focus-visible)': { display: 'none' } }}
123
+ >
124
+ {placeholder}
125
+ </Text>
126
+ )}
127
+ <chakra.input
128
+ _focusVisible={{ boxShadow: 'none' }}
129
+ flexGrow="1"
130
+ id={id}
131
+ onKeyDown={keyupHandler}
132
+ onPaste={pasteEventHandler}
133
+ />
134
+ {isInvalid && <Icon color="icon.negative" name="ErrorCircleFilled" position="absolute" right="2rem" />}
135
+ </Box>
136
+ {errorText && <FormErrorMessage as="p">{errorText}</FormErrorMessage>}
137
+ {helperText && <FormHelperText as="p">{helperText}</FormHelperText>}
138
+ </FormControl>
139
+ );
140
+ };
141
+
142
+ export default ChipInput;
@@ -4,10 +4,14 @@ const Duplicate = forwardRef<IconProps, 'svg'>((props, ref) => (
4
4
  <Icon ref={ref} viewBox="0 0 16 16" {...props}>
5
5
  <path
6
6
  clipRule="evenodd"
7
- d="M12 12v.667a2 2 0 0 1-2 2H3.333a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2H4v-.667a2 2 0 0 1 2-2h6.667a2 2 0 0 1 2 2V10a2 2 0 0 1-2 2H12Zm-1.333 0H6a2 2 0 0 1-2-2V5.333h-.667A.667.667 0 0 0 2.667 6v6.667c0 .368.298.666.666.666H10a.667.667 0 0 0 .667-.666V12ZM6 2.667a.667.667 0 0 0-.667.666V10c0 .368.299.667.667.667h6.667a.667.667 0 0 0 .666-.667V3.333a.667.667 0 0 0-.666-.666H6Z"
7
+ d="M11 13.5C11 14.3284 10.3284 15 9.5 15H2.5C1.67157 15 1 14.3284 1 13.5V6.5C1 5.67157 1.67157 5 2.5 5H9.5C10.3284 5 11 5.67157 11 6.5V13.5ZM9.5 13.5H2.5V6.5H9.5V13.5Z"
8
8
  fill="currentColor"
9
9
  fillRule="evenodd"
10
10
  />
11
+ <path
12
+ d="M13.5 11H12V9.5H13.5V2.5H6.5V4H5V2.5C5 1.67157 5.67157 1 6.5 1H13.5C14.3284 1 15 1.67157 15 2.5V9.5C15 10.3284 14.3284 11 13.5 11Z"
13
+ fill="currentColor"
14
+ />
11
15
  </Icon>
12
16
  ));
13
17
 
@@ -0,0 +1,14 @@
1
+ import { forwardRef, Icon, IconProps } from '@chakra-ui/react';
2
+
3
+ const ErrorCircleFilled = forwardRef<IconProps, 'svg'>((props, ref) => (
4
+ <Icon ref={ref} viewBox="0 0 16 16" {...props}>
5
+ <path
6
+ clipRule="evenodd"
7
+ d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM7.25 9V4H8.75V9H7.25ZM7.25 12V10.5H8.75V12H7.25Z"
8
+ fill="currentColor"
9
+ fillRule="evenodd"
10
+ />
11
+ </Icon>
12
+ ));
13
+
14
+ export default ErrorCircleFilled;
@@ -224,3 +224,4 @@ export { default as Workflow } from './Workflow';
224
224
  export { default as Wow } from './Wow';
225
225
  export { default as WrappedLines } from './WrappedLines';
226
226
  export { default as Xamarin } from './Xamarin';
227
+ export { default as ErrorCircleFilled } from './ErrorCircleFilled';
@@ -4,10 +4,14 @@ const Duplicate = forwardRef<IconProps, 'svg'>((props, ref) => (
4
4
  <Icon ref={ref} viewBox="0 0 24 24" {...props}>
5
5
  <path
6
6
  clipRule="evenodd"
7
- d="M18 18v1a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V9a3 3 0 0 1 3-3h1V5a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-1Zm-2 0H9a3 3 0 0 1-3-3V8H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-1ZM9 4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H9Z"
7
+ d="M17 20C17 21.1046 16.1046 22 15 22H4C2.89543 22 2 21.1046 2 20V9C2 7.89543 2.89543 7 4 7H15C16.1046 7 17 7.89543 17 9V20ZM15 20H4V9H15V20Z"
8
8
  fill="currentColor"
9
9
  fillRule="evenodd"
10
10
  />
11
+ <path
12
+ d="M19.8571 17H18V14.8571H19.8571V4.14286H9.14286V6H7V4.14286C7 2.95939 7.95939 2 9.14286 2H19.8571C21.0406 2 22 2.95939 22 4.14286V14.8571C22 16.0406 21.0406 17 19.8571 17Z"
13
+ fill="currentColor"
14
+ />
11
15
  </Icon>
12
16
  ));
13
17
 
@@ -0,0 +1,14 @@
1
+ import { forwardRef, Icon, IconProps } from '@chakra-ui/react';
2
+
3
+ const ErrorCircleFilled = forwardRef<IconProps, 'svg'>((props, ref) => (
4
+ <Icon ref={ref} viewBox="0 0 24 24" {...props}>
5
+ <path
6
+ clipRule="evenodd"
7
+ d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22ZM11 13.5V6.5H13V13.5H11ZM11 17.5V15.5H13V17.5H11Z"
8
+ fill="currentColor"
9
+ fillRule="evenodd"
10
+ />
11
+ </Icon>
12
+ ));
13
+
14
+ export default ErrorCircleFilled;
@@ -224,3 +224,4 @@ export { default as Workflow } from './Workflow';
224
224
  export { default as Wow } from './Wow';
225
225
  export { default as WrappedLines } from './WrappedLines';
226
226
  export { default as Xamarin } from './Xamarin';
227
+ export { default as ErrorCircleFilled } from './ErrorCircleFilled';
@@ -1,15 +1,6 @@
1
- import { useRef } from 'react';
2
-
3
- let uniqueId = 0;
4
- const getUniqueId = () => {
5
- uniqueId += 1;
6
- return uniqueId;
7
- };
1
+ import { useId } from 'react';
8
2
 
9
3
  export function useIconId() {
10
- const idRef = useRef<null | number>(null);
11
- if (idRef.current === null) {
12
- idRef.current = getUniqueId();
13
- }
14
- return `iconRender${idRef.current}`;
4
+ const id = useId();
5
+ return `iconRender${id}`;
15
6
  }
@@ -217,7 +217,7 @@ export const figmaIcons: {
217
217
  { figmaToken: 'check-circle', iconName: 'BuildstatusSuccessful', tags: 'success' },
218
218
  { figmaToken: 'check-circle-filled', iconName: 'BuildstatusSuccessfulSolid', tags: 'success' },
219
219
  { figmaToken: 'error-circle', iconName: 'StepstatusWarning' },
220
- { figmaToken: 'error-circle-filled', iconName: '' },
220
+ { figmaToken: 'error-circle-filled', iconName: 'ErrorCircleFilled' },
221
221
  { figmaToken: 'info-circle', iconName: '', tags: 'tooltip' },
222
222
  { figmaToken: 'info-circle-filled', iconName: '' },
223
223
  { figmaToken: 'warning', iconName: 'Warning' },
package/src/theme.ts CHANGED
@@ -54,6 +54,7 @@ import zIndices from './Foundations/Zindex/Zindex';
54
54
  import Toggletip from './Components/Toggletip/Toggletip.theme';
55
55
  import FilterSwitch from './Components/Filter/FilterSwitch/FilterSwitch.theme';
56
56
  import { colors, semanticTokens } from './tokens/tokens';
57
+ import ChipInput from './Components/Form/ChipInput/ChipInput.theme';
57
58
 
58
59
  const theme = {
59
60
  breakpoints,
@@ -78,6 +79,7 @@ const theme = {
78
79
  FilterSwitch,
79
80
  ...Form,
80
81
  Alert,
82
+ ChipInput,
81
83
  CloseButton,
82
84
  CodeBlock,
83
85
  CodeSnippet,