@bitrise/bitkit 12.93.0 → 12.93.1-alpha.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.
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.93.0",
4
+ "version": "12.93.1-alpha.1",
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,144 @@
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
+ onNewValues: (nv: string[]) => void;
22
+ onRemove: (v: string) => void;
23
+ separator?: RegExp;
24
+ maxCount?: number;
25
+ invalidValues?: string[];
26
+ errorText?: ReactNode;
27
+ helperText?: ReactNode;
28
+ }
29
+
30
+ const ChipInput = ({
31
+ errorText,
32
+ helperText,
33
+ invalidValues = [],
34
+ label,
35
+ maxCount,
36
+ onNewValues,
37
+ onRemove,
38
+ placeholder,
39
+ separator = /[, ]/,
40
+ value,
41
+ ...rest
42
+ }: ChipInputProps) => {
43
+ const theme = useStyleConfig('ChipInput');
44
+ const keyupHandler: KeyboardEventHandler<HTMLInputElement> = (ev) => {
45
+ const target = ev.currentTarget;
46
+ if (ev.key.match(separator)) {
47
+ ev.preventDefault();
48
+ if (target.value) {
49
+ const newValue = target.value.split(separator)[0];
50
+ if (!value.includes(newValue)) {
51
+ onNewValues([newValue]);
52
+ }
53
+ target.value = '';
54
+ }
55
+ } else if (ev.key === 'Backspace' && target.value.length === 0) {
56
+ onRemove(value[value.length - 1]);
57
+ }
58
+ };
59
+ const pasteEventHandler: ClipboardEventHandler<HTMLInputElement> = (ev) => {
60
+ ev.preventDefault();
61
+ const newItems = ev.clipboardData
62
+ .getData('text/plain')
63
+ .split(separator)
64
+ .filter((item) => item && !item.match(/\s/) && !value.includes(item));
65
+ onNewValues(newItems);
66
+ };
67
+ const removeItem = (deleted: string) => {
68
+ onRemove(deleted);
69
+ };
70
+ const isInvalid = rest.isInvalid || (maxCount && value.length > maxCount) || Boolean(errorText);
71
+ const id = useId();
72
+ return (
73
+ <FormControl {...rest} isInvalid={isInvalid}>
74
+ <Box alignItems="center" display={label || maxCount ? 'flex' : 'none'} gap="4" marginBlockEnd="4">
75
+ {label && (
76
+ <FormLabel
77
+ htmlFor={id}
78
+ optionalIndicator={
79
+ <Text as="span" color="neutral.40" fontSize="2" fontWeight="normal" marginLeft="4px">
80
+ (Optional)
81
+ </Text>
82
+ }
83
+ requiredIndicator={false}
84
+ >
85
+ {label}
86
+ </FormLabel>
87
+ )}
88
+
89
+ {maxCount && (
90
+ <Text
91
+ as="span"
92
+ color="neutral.40"
93
+ marginInlineStart="auto"
94
+ size="2"
95
+ sx={{ fontVariantNumeric: 'tabular-nums' }}
96
+ >
97
+ {value.length}/{maxCount}
98
+ </Text>
99
+ )}
100
+ </Box>
101
+ <Box
102
+ __css={theme}
103
+ display="flex"
104
+ flexDirection="row"
105
+ flexWrap="wrap"
106
+ gap="4"
107
+ paddingRight={isInvalid ? '42px' : undefined}
108
+ >
109
+ {value.map((item, idx) => (
110
+ <Tag
111
+ key={item}
112
+ colorScheme={invalidValues.includes(item) || (maxCount && idx > maxCount - 1) ? 'red' : undefined}
113
+ onClose={() => removeItem(item)}
114
+ size="sm"
115
+ >
116
+ {item}
117
+ </Tag>
118
+ ))}
119
+ {value.length === 0 && placeholder && (
120
+ <Text
121
+ color="sys.fg.subtle"
122
+ pointerEvents="none"
123
+ position="absolute"
124
+ sx={{ ':has(+ :focus-visible)': { display: 'none' } }}
125
+ >
126
+ {placeholder}
127
+ </Text>
128
+ )}
129
+ <chakra.input
130
+ _focusVisible={{ boxShadow: 'none' }}
131
+ flexGrow="1"
132
+ id={id}
133
+ onKeyDown={keyupHandler}
134
+ onPaste={pasteEventHandler}
135
+ />
136
+ {isInvalid && <Icon color="icon.negative" name="ErrorCircleFilled" position="absolute" right="2rem" />}
137
+ </Box>
138
+ {errorText && <FormErrorMessage as="p">{errorText}</FormErrorMessage>}
139
+ {helperText && <FormHelperText as="p">{helperText}</FormHelperText>}
140
+ </FormControl>
141
+ );
142
+ };
143
+
144
+ export default ChipInput;
@@ -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';
@@ -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/index.ts CHANGED
@@ -333,3 +333,6 @@ export * from './Components/Filter/Filter.types';
333
333
 
334
334
  export type { DateInputProps } from './Components/Form/DateInput/DateInput';
335
335
  export { default as DateInput } from './Components/Form/DateInput/DateInput';
336
+
337
+ export type { ChipInputProps } from './Components/Form/ChipInput/ChipInput';
338
+ export { default as ChipInput } from './Components/Form/ChipInput/ChipInput';
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,