@bitrise/bitkit 13.272.0 → 13.274.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": "13.272.0",
4
+ "version": "13.274.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+ssh://git@github.com/bitrise-io/bitkit.git"
@@ -12,7 +12,8 @@ const BreadcrumbTheme: SystemStyleObject = {
12
12
  _hover: {
13
13
  textDecoration: 'none',
14
14
  },
15
- color: 'neutral.10',
15
+ color: 'text/body',
16
+ textStyle: 'body/md/semibold',
16
17
  },
17
18
  _hover: {
18
19
  textDecoration: 'underline',
@@ -29,6 +30,7 @@ const BreadcrumbTheme: SystemStyleObject = {
29
30
  separator: {
30
31
  color: 'neutral.80',
31
32
  display: 'flex',
33
+ marginInline: '4',
32
34
  },
33
35
  },
34
36
  };
@@ -49,6 +49,7 @@ export const FILTER_STORY_DATA: FilterData = {
49
49
  type: 'tag',
50
50
  },
51
51
  cache_type: {
52
+ allOptionLabel: 'All items',
52
53
  iconsMap: {
53
54
  bazel: 'Bazel',
54
55
  gradle: 'Gradle',
@@ -36,11 +36,19 @@ export type FilterCategoryProps = {
36
36
  dayTooltip?: DatePickerProps['dayTooltip'];
37
37
  isMultiple: true;
38
38
  }
39
+ | {
40
+ type: 'switch';
41
+ allOptionLabel?: string;
42
+ }
39
43
  | {
40
44
  type?: Exclude<FilterType, 'dateRange'>;
41
45
  selectable?: never;
42
46
  dayTooltip?: never;
43
47
  }
48
+ | {
49
+ type?: Exclude<FilterType, 'switch'>;
50
+ allOptionLabel?: never;
51
+ }
44
52
  );
45
53
 
46
54
  export type FilterData = Record<string, FilterCategoryProps>;
@@ -3,11 +3,12 @@ import { useEffect } from 'react';
3
3
  import { useDisclosure, useMultiStyleConfig } from '@chakra-ui/react';
4
4
  import { DateTime } from 'luxon';
5
5
  import Box from '../../Box/Box';
6
- import DatePicker, { DateRange, useDateRange } from '../../DatePicker/DatePicker';
6
+ import DatePicker, { DatePickerProps, DateRange, useDateRange } from '../../DatePicker/DatePicker';
7
7
  import Icon from '../../Icon/Icon';
8
8
  import Text from '../../Text/Text';
9
9
  import { useFilterContext } from '../Filter.context';
10
10
  import { FilterStyle } from '../Filter.theme';
11
+ import { FilterCategoryProps } from '../Filter.types';
11
12
 
12
13
  export type FilterDateProps = {
13
14
  category: string;
@@ -19,6 +20,11 @@ const FilterDate = (props: FilterDateProps) => {
19
20
 
20
21
  const { isLoading, onFilterChange, onFilterClear, setPopoverOpen, state, data } = useFilterContext();
21
22
 
23
+ const dateRangeData = data.date_range as FilterCategoryProps & {
24
+ dayTooltip?: DatePickerProps['dayTooltip'];
25
+ selectable?: DateRange;
26
+ };
27
+
22
28
  const { isOpen, onClose, onToggle } = useDisclosure();
23
29
 
24
30
  const value = state[category];
@@ -54,11 +60,11 @@ const FilterDate = (props: FilterDateProps) => {
54
60
 
55
61
  return (
56
62
  <DatePicker
57
- selectable={data.date_range.selectable || selectable}
63
+ selectable={dateRangeData.selectable || selectable}
58
64
  onApply={onDateRangeApply}
59
65
  onClear={value?.length ? onClearClick : undefined}
60
66
  onClose={onClose}
61
- dayTooltip={data.date_range.dayTooltip}
67
+ dayTooltip={dateRangeData.dayTooltip}
62
68
  selected={selectedRange}
63
69
  visible={isOpen}
64
70
  mode="range"
@@ -1,43 +1,44 @@
1
1
  import { rem } from '../../../utils/utils';
2
2
 
3
3
  const FilterSwitch = {
4
- baseStyle: () => {
4
+ baseStyle: ({ isChecked }: { isChecked: boolean }) => {
5
5
  return {
6
6
  container: {
7
7
  background: 'neutral.95',
8
- border: '1px solid',
9
- borderColor: 'neutral.80',
10
8
  borderRadius: '4',
11
9
  color: 'neutral.40',
12
10
  display: 'flex',
13
11
  },
12
+ icon: {
13
+ color: isChecked ? 'icon/secondary' : 'icon/tertiary',
14
+ },
14
15
  item: {
16
+ borderRadius: '4',
17
+ maxW: '20rem',
18
+ borderColor: isChecked ? 'border/strong' : 'transparent',
19
+ borderWidth: '1px',
20
+ borderStyle: 'solid',
21
+ _hover: {
22
+ background: 'background/hover',
23
+ color: 'text/primary',
24
+
25
+ '> svg': {
26
+ color: 'icon/secondary',
27
+ },
28
+ },
15
29
  _active: {
16
- background: 'neutral.100',
17
- color: 'purple.10',
30
+ background: 'background/active',
18
31
  },
19
32
  _checked: {
20
33
  background: 'neutral.100',
21
34
  color: 'purple.10',
22
35
  cursor: 'default',
23
36
  },
24
- _first: {
25
- borderLeft: 'none',
26
- borderLeftRadius: '4',
27
- },
28
37
  _focusVisible: {
29
38
  boxShadow: 'outline',
30
39
  zIndex: 1,
31
40
  },
32
- _hover: {
33
- background: 'neutral.100',
34
- },
35
- _last: {
36
- borderRightRadius: '4',
37
- },
38
41
  alignItems: 'center',
39
- borderLeft: '1px solid',
40
- borderLeftColor: 'neutral.80',
41
42
  cursor: 'pointer',
42
43
  display: 'flex',
43
44
  fontSize: rem(14),
@@ -1,3 +1,4 @@
1
+ import { useMemo } from 'react';
1
2
  import {
2
3
  chakra,
3
4
  forwardRef,
@@ -7,33 +8,47 @@ import {
7
8
  useRadioGroupContext,
8
9
  } from '@chakra-ui/react';
9
10
  import { omitThemingProps } from '@chakra-ui/styled-system';
11
+ import { createContext } from '@chakra-ui/react-utils';
12
+ import Divider from '../../Divider/Divider';
13
+ import Text from '../../Text/Text';
14
+ import Box from '../../Box/Box';
15
+ import Icon, { TypeIconName } from '../../Icon/Icon';
10
16
 
11
17
  type RadioInputProps = ChakraRadioProps['inputProps'] & {
12
18
  'data-testid'?: string;
13
19
  };
14
20
 
15
21
  export interface FilterSwitchProps extends Omit<ChakraRadioProps, 'inputProps'> {
22
+ iconName?: TypeIconName;
16
23
  inputProps?: RadioInputProps;
17
24
  }
18
25
 
26
+ type FilterSwicthContext = {
27
+ selectedOption: string;
28
+ options: string[];
29
+ };
30
+
31
+ export const [FilterSwitchContextProvider, useFilterSwitchContext] = createContext<FilterSwicthContext>();
32
+
19
33
  const FilterSwitch = forwardRef<FilterSwitchProps, 'input'>((props, ref) => {
20
34
  const group = useRadioGroupContext();
21
35
  const { value: valueProp } = props;
22
36
 
23
- const styles = useMultiStyleConfig('FilterSwitch');
37
+ const isChecked = group.value === valueProp;
38
+
39
+ const styles = useMultiStyleConfig('FilterSwitch', { isChecked });
24
40
 
25
41
  const ownProps = omitThemingProps(props);
26
42
 
27
43
  const {
28
44
  children,
45
+ iconName,
29
46
  inputProps: htmlInputProps,
30
47
  isDisabled = group?.isDisabled,
31
48
  isFocusable = group?.isFocusable,
32
49
  ...rest
33
50
  } = ownProps;
34
51
 
35
- const isChecked = group.value === valueProp;
36
-
37
52
  const { name } = group;
38
53
 
39
54
  const { getRadioProps, getInputProps, getLabelProps } = useRadio({
@@ -45,12 +60,43 @@ const FilterSwitch = forwardRef<FilterSwitchProps, 'input'>((props, ref) => {
45
60
  onChange: group.onChange,
46
61
  });
47
62
 
48
- return (
63
+ const { options, selectedOption } = useFilterSwitchContext();
64
+
65
+ const showDivider = useMemo(() => {
66
+ const optionCount = options.length;
67
+ const index = options.indexOf(valueProp || '');
68
+
69
+ const selectedIndex = options.indexOf(selectedOption);
70
+
71
+ return index < optionCount - 1 && selectedIndex !== index && selectedIndex !== index + 1;
72
+ }, [options, selectedOption, valueProp]);
73
+
74
+ const component = (
49
75
  <chakra.label {...getLabelProps()} {...getRadioProps()} __css={styles.item}>
50
76
  <chakra.input {...getInputProps(htmlInputProps, ref)} />
51
- {children}
77
+ {iconName && <Icon __css={styles.icon} name={iconName} size="16" />}
78
+ <Text as="span" display="block" overflow="hidden" textOverflow="ellipsis">
79
+ {children}
80
+ </Text>
52
81
  </chakra.label>
53
82
  );
83
+
84
+ return (
85
+ <Box height="fit-content" width="fit-content" position="relative">
86
+ {component}
87
+ {showDivider && (
88
+ <Divider
89
+ background="boder/regular"
90
+ height="16"
91
+ orientation="vertical"
92
+ marginTop="8"
93
+ position="absolute"
94
+ right="0"
95
+ top="0"
96
+ />
97
+ )}
98
+ </Box>
99
+ );
54
100
  });
55
101
 
56
102
  export default FilterSwitch;
@@ -1,11 +1,21 @@
1
1
  import { RadioGroup as ChakraRadioGroup, RadioGroupProps, useMultiStyleConfig } from '@chakra-ui/react';
2
+ import { FilterSwitchContextProvider } from './FilterSwitch';
2
3
 
3
- export type { RadioGroupProps };
4
+ export type FilterSwitchGroupProps = { options: string[] } & RadioGroupProps;
4
5
 
5
- const FilterSwitchGroup = (props: RadioGroupProps) => {
6
+ const FilterSwitchGroup = ({ options, value, ...rest }: FilterSwitchGroupProps) => {
6
7
  const { container } = useMultiStyleConfig('FilterSwitch');
7
8
 
8
- return <ChakraRadioGroup sx={container} {...props} />;
9
+ return (
10
+ <FilterSwitchContextProvider
11
+ value={{
12
+ options,
13
+ selectedOption: value || '',
14
+ }}
15
+ >
16
+ <ChakraRadioGroup sx={container} {...rest} value={value} />
17
+ </FilterSwitchContextProvider>
18
+ );
9
19
  };
10
20
 
11
21
  export default FilterSwitchGroup;
@@ -1,8 +1,9 @@
1
+ import { useMemo } from 'react';
1
2
  import { useFilterContext } from '../Filter.context';
2
3
  import { getOptionLabel } from '../Filter.utils';
3
- import Icon from '../../Icon/Icon';
4
4
  import FilterSwitch from '../FilterSwitch/FilterSwitch';
5
5
  import FilterSwitchGroup from '../FilterSwitch/FilterSwitchGroup';
6
+ import { FilterCategoryProps } from '../Filter.types';
6
7
 
7
8
  type FilterSwitchAdapterProps = {
8
9
  category: string;
@@ -11,7 +12,11 @@ type FilterSwitchAdapterProps = {
11
12
  const FilterSwitchAdapter = (props: FilterSwitchAdapterProps) => {
12
13
  const { category } = props;
13
14
  const { data, onFilterChange, state } = useFilterContext();
14
- const { iconsMap, options, optionsMap } = data[category];
15
+ const { allOptionLabel, iconsMap, options, optionsMap } = data[category] as FilterCategoryProps & {
16
+ allOptionLabel?: string;
17
+ };
18
+
19
+ const optionsWithAll = useMemo(() => ['all', ...(options || [])], [options]);
15
20
 
16
21
  if (!options?.length) {
17
22
  return null;
@@ -20,10 +25,16 @@ const FilterSwitchAdapter = (props: FilterSwitchAdapterProps) => {
20
25
  const value = state[category]?.[0] || '';
21
26
 
22
27
  return (
23
- <FilterSwitchGroup onChange={(newValue) => onFilterChange(category, [newValue])} value={value}>
28
+ <FilterSwitchGroup
29
+ onChange={(newValue) => onFilterChange(category, [newValue])}
30
+ options={optionsWithAll}
31
+ value={value}
32
+ >
33
+ <FilterSwitch key="all" value="all">
34
+ {allOptionLabel || 'All items'}
35
+ </FilterSwitch>
24
36
  {options.map((opt) => (
25
- <FilterSwitch key={opt} value={opt}>
26
- {iconsMap && iconsMap[opt] && <Icon color="icon/tertiary" name={iconsMap[opt]} size="16" />}
37
+ <FilterSwitch key={opt} value={opt} iconName={iconsMap && iconsMap[opt]}>
27
38
  {getOptionLabel(opt, optionsMap)}
28
39
  </FilterSwitch>
29
40
  ))}
@@ -60,13 +60,13 @@ const Pagination = ({
60
60
  ))}
61
61
  </Dropdown>
62
62
  </Box>
63
- <Divider color="pal.neutral.90" height="3rem" orientation="vertical" />
63
+ <Divider color="pal.neutral.90" height="2.5rem" orientation="vertical" />
64
64
  </>
65
65
  )}
66
66
  <Text as="span" color={textColor} marginRight="auto" textStyle="body/md/regular">
67
67
  {itemsStartIndex}-{itemsEndIndex} of {totalCount} items
68
68
  </Text>
69
- <Divider color="pal.neutral.90" height="3rem" orientation="vertical" />
69
+ <Divider color="pal.neutral.90" height="2.5rem" orientation="vertical" />
70
70
  <Box alignItems="center" display="flex" gap="0.5rem">
71
71
  <Dropdown
72
72
  onChange={({ target }) => setPage(Number(target.value))}
@@ -66,9 +66,9 @@ const schemeMap: Record<TagColors, MappedColor> = {
66
66
  };
67
67
 
68
68
  const baseStyle = definePartsStyle((props) => {
69
- const { colorScheme = 'neutral' } = props;
69
+ const { colorScheme = 'neutral', isDisabled, isLoading } = props;
70
70
  const { color, isFilled } = schemeMap[colorScheme as TagColors];
71
- const normalTextColor = color === 'neutral' ? 'text.primary' : `sys.${color}.strong`;
71
+ const normalTextColor = color === 'neutral' ? 'text.body' : `sys.${color}.strong`;
72
72
  const scheme = {
73
73
  backgroundColor: `sys.${color}.${isFilled ? 'bold' : 'subtle'}`,
74
74
  borderColor: `sys.${color}.${isFilled ? 'bold' : 'muted'}`,
@@ -108,6 +108,7 @@ const baseStyle = definePartsStyle((props) => {
108
108
  },
109
109
  },
110
110
  icon: {
111
+ color: colorScheme === 'neutral' && !isDisabled && !isLoading ? 'icon.secondary' : undefined,
111
112
  marginInlineEnd: '4',
112
113
  },
113
114
  label: {
@@ -19,12 +19,13 @@ type VariantLabel =
19
19
  | { variant?: undefined; label?: ReactNode }
20
20
  | { variant: 'dynamic'; label?: ReactNode }
21
21
  | { variant: 'fixed'; label: ReactNode };
22
- export type ToggleProps = Omit<FormControlProps, 'label' | 'onBlur' | 'onChange'> & {
22
+ export type ToggleProps = Omit<FormControlProps, 'label' | 'onBlur' | 'onChange' | 'isReadOnly'> & {
23
23
  'data-testid'?: string;
24
24
  defaultChecked?: SwitchProps['defaultChecked'];
25
25
  id?: SwitchProps['id'];
26
26
  isChecked?: SwitchProps['isChecked'];
27
27
  isDisabled?: SwitchProps['isDisabled'];
28
+ isReadOnly?: SwitchProps['isReadOnly'] | 'toggle';
28
29
  isLoading?: boolean;
29
30
  helperText?: ReactNode;
30
31
  name?: string;
@@ -75,8 +76,8 @@ const Toggle = forwardRef<ToggleProps, 'div'>((props, ref) => {
75
76
  defaultChecked,
76
77
  id,
77
78
  isChecked,
78
- isDisabled,
79
- isReadOnly,
79
+ isDisabled: isDisabled || isReadOnly === 'toggle',
80
+ isReadOnly: isReadOnly === true,
80
81
  isLoading,
81
82
  name,
82
83
  onBlur,
@@ -102,7 +103,7 @@ const Toggle = forwardRef<ToggleProps, 'div'>((props, ref) => {
102
103
  const switchComponent = () => {
103
104
  if (tooltip)
104
105
  return (
105
- <Tooltip {...tooltip}>
106
+ <Tooltip {...tooltip} shouldWrapChildren>
106
107
  <ToggleComponent {...switchProps} />
107
108
  </Tooltip>
108
109
  );
@@ -41,7 +41,12 @@ export const SettingsCardOnOffValue = (props: Pick<ToggleProps, 'isChecked' | 'v
41
41
  );
42
42
  };
43
43
 
44
- export const SettingsCardToggle = (props: Omit<ToggleProps, 'variant'> & { label: ReactNode }) => {
44
+ interface SettingsCardToggleProps extends Omit<ToggleProps, 'variant' | 'isReadOnly'> {
45
+ label: ReactNode;
46
+ isReadOnly?: boolean | 'toggle';
47
+ }
48
+
49
+ export const SettingsCardToggle = (props: SettingsCardToggleProps) => {
45
50
  const { data } = useSettingsCardStyle();
46
51
  return (
47
52
  <Box __css={data}>