@bitrise/bitkit 13.273.0 → 13.275.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/Breadcrumb/Breadcrumb.theme.ts +3 -1
- package/src/Components/Dropdown/Dropdown.tsx +16 -6
- package/src/Components/Filter/Filter.storyData.ts +1 -0
- package/src/Components/Filter/Filter.types.ts +8 -0
- package/src/Components/Filter/FilterDate/FilterDate.tsx +9 -3
- package/src/Components/Filter/FilterSwitch/FilterSwitch.theme.ts +18 -17
- package/src/Components/Filter/FilterSwitch/FilterSwitch.tsx +51 -5
- package/src/Components/Filter/FilterSwitch/FilterSwitchGroup.tsx +13 -3
- package/src/Components/Filter/FilterSwitchAdapter/FilterSwitchAdapter.tsx +16 -5
- package/src/Components/Pagination/Pagination.tsx +2 -2
- package/src/Components/Tag/Tag.theme.ts +3 -2
package/package.json
CHANGED
|
@@ -12,7 +12,8 @@ const BreadcrumbTheme: SystemStyleObject = {
|
|
|
12
12
|
_hover: {
|
|
13
13
|
textDecoration: 'none',
|
|
14
14
|
},
|
|
15
|
-
color: '
|
|
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
|
};
|
|
@@ -146,7 +146,10 @@ function findOption<T>(
|
|
|
146
146
|
return null;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
const DropdownLabelCacheContext = createContext<
|
|
149
|
+
const DropdownLabelCacheContext = createContext<{
|
|
150
|
+
map: Map<unknown, ReactNode>;
|
|
151
|
+
fallback?: (value: unknown) => ReactNode;
|
|
152
|
+
} | null>(null);
|
|
150
153
|
|
|
151
154
|
const useLabelCache = () => {
|
|
152
155
|
const contextCache = useContext(DropdownLabelCacheContext);
|
|
@@ -157,11 +160,18 @@ const useLabelCache = () => {
|
|
|
157
160
|
if (!localCache.current) {
|
|
158
161
|
localCache.current = new Map();
|
|
159
162
|
}
|
|
160
|
-
return localCache.current;
|
|
163
|
+
return { map: localCache.current };
|
|
161
164
|
};
|
|
162
165
|
|
|
163
|
-
export const DropdownLabelCache = ({
|
|
164
|
-
|
|
166
|
+
export const DropdownLabelCache = ({
|
|
167
|
+
children,
|
|
168
|
+
fallback,
|
|
169
|
+
}: {
|
|
170
|
+
children: ReactNode;
|
|
171
|
+
fallback?: (value: unknown) => ReactNode;
|
|
172
|
+
}) => {
|
|
173
|
+
const map = useMemo(() => new Map<unknown, ReactNode>(), []);
|
|
174
|
+
const value = useMemo(() => ({ map, fallback }), [map, fallback]);
|
|
165
175
|
|
|
166
176
|
return <DropdownLabelCacheContext.Provider value={value}>{children}</DropdownLabelCacheContext.Provider>;
|
|
167
177
|
};
|
|
@@ -228,7 +238,7 @@ function useDropdown<T>({
|
|
|
228
238
|
onSearch: () => setActiveIndex(null),
|
|
229
239
|
});
|
|
230
240
|
|
|
231
|
-
const labelCache = useLabelCache();
|
|
241
|
+
const { map: labelCache, fallback } = useLabelCache();
|
|
232
242
|
|
|
233
243
|
// clear map when value is changed from the outside
|
|
234
244
|
useEffect(() => {
|
|
@@ -330,7 +340,7 @@ function useDropdown<T>({
|
|
|
330
340
|
}}
|
|
331
341
|
size="sm"
|
|
332
342
|
>
|
|
333
|
-
{labelCache.get(formValueItem) || formValueItem}
|
|
343
|
+
{labelCache.get(formValueItem) || fallback?.(formValueItem) || formValueItem}
|
|
334
344
|
</Tag>
|
|
335
345
|
);
|
|
336
346
|
})}
|
|
@@ -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={
|
|
63
|
+
selectable={dateRangeData.selectable || selectable}
|
|
58
64
|
onApply={onDateRangeApply}
|
|
59
65
|
onClear={value?.length ? onClearClick : undefined}
|
|
60
66
|
onClose={onClose}
|
|
61
|
-
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: '
|
|
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
|
|
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
|
-
|
|
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
|
-
{
|
|
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 {
|
|
4
|
+
export type FilterSwitchGroupProps = { options: string[] } & RadioGroupProps;
|
|
4
5
|
|
|
5
|
-
const FilterSwitchGroup = (
|
|
6
|
+
const FilterSwitchGroup = ({ options, value, ...rest }: FilterSwitchGroupProps) => {
|
|
6
7
|
const { container } = useMultiStyleConfig('FilterSwitch');
|
|
7
8
|
|
|
8
|
-
return
|
|
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
|
|
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="
|
|
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="
|
|
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.
|
|
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: {
|