@bitrise/bitkit 12.67.0 → 12.69.0-alpha-filter.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.67.0",
4
+ "version": "12.69.0-alpha-filter.1",
5
5
  "repository": "git@github.com:bitrise-io/bitkit.git",
6
6
  "main": "src/index.ts",
7
7
  "license": "UNLICENSED",
@@ -45,6 +45,7 @@ export interface InputProps extends UsedFormControlProps, UsedChakraInputProps {
45
45
  infoTooltipProps?: TooltipProps;
46
46
  isLoading?: boolean;
47
47
  helperText?: ReactNode;
48
+ inputHeight?: number;
48
49
  inputRef?: Ref<HTMLInputElement>;
49
50
  inputStyle?: SystemStyleObject;
50
51
  label?: ReactNode;
@@ -69,6 +70,7 @@ const Input = forwardRef<InputProps, 'div'>((props, ref) => {
69
70
  isDisabled,
70
71
  infoText,
71
72
  infoTooltipProps,
73
+ inputHeight,
72
74
  inputRef,
73
75
  inputStyle,
74
76
  isInvalid,
@@ -117,7 +119,7 @@ const Input = forwardRef<InputProps, 'div'>((props, ref) => {
117
119
  ref: inputRef,
118
120
  role,
119
121
  step,
120
- sx: inputStyle,
122
+ sx: { height: inputHeight, ...inputStyle },
121
123
  type,
122
124
  value,
123
125
  };
@@ -141,9 +143,12 @@ const Input = forwardRef<InputProps, 'div'>((props, ref) => {
141
143
  }
142
144
  };
143
145
 
146
+ const showLabel = label || !!infoText || (withCounter && maxLength);
147
+ const iconSize = inputHeight && inputHeight < 40 ? 16 : 24;
148
+
144
149
  return (
145
150
  <FormControl {...formControlProps}>
146
- <Box display="flex" gap="4" alignItems="center" marginBlockEnd="4">
151
+ <Box display={showLabel ? 'flex' : 'none'} gap="4" alignItems="center" marginBlockEnd="4">
147
152
  {label && (
148
153
  <FormLabel
149
154
  requiredIndicator={null as any}
@@ -177,20 +182,30 @@ const Input = forwardRef<InputProps, 'div'>((props, ref) => {
177
182
 
178
183
  <InputWrapper {...inputWrapperProps}>
179
184
  {leftIconName && (
180
- <InputLeftElement margin="12px" pointerEvents="none">
181
- <Icon color={leftIconColor} name={leftIconName} />
185
+ <InputLeftElement
186
+ marginLeft={inputHeight ? (inputHeight - iconSize) / 2 : 12}
187
+ top="50%"
188
+ transform="translateY(-50%)"
189
+ pointerEvents="none"
190
+ >
191
+ <Icon color={leftIconColor} name={leftIconName} size={`${iconSize}`} />
182
192
  </InputLeftElement>
183
193
  )}
184
194
  <ChakraInput
185
- paddingLeft={leftIconName ? '43px' : '11px'}
195
+ paddingLeft={leftIconName ? (inputHeight ? (inputHeight - iconSize) / 2 : 12) + iconSize + 4 : '11px'}
186
196
  paddingRight={inputRightPadding}
187
197
  {...inputProps}
188
198
  onChange={onInputChange}
189
199
  />
190
200
  {rightAddon && <RightContentWrapper>{rightAddon}</RightContentWrapper>}
191
201
  {!rightAddon && rightIconName && (
192
- <InputRightElement margin="12px" pointerEvents="none">
193
- <Icon name={rightIconName} />
202
+ <InputRightElement
203
+ marginRight={inputHeight ? (inputHeight - iconSize) / 2 : 12}
204
+ top="50%"
205
+ transform="translateY(-50%)"
206
+ pointerEvents="none"
207
+ >
208
+ <Icon name={rightIconName} size={`${iconSize}`} />
194
209
  </InputRightElement>
195
210
  )}
196
211
  </InputWrapper>
@@ -0,0 +1,363 @@
1
+ import { Fragment, useState } from 'react';
2
+ import { Grid, GridItem, SystemStyleObject, Tooltip, useClipboard, useToast } from '@chakra-ui/react';
3
+ /* eslint-disable import/no-extraneous-dependencies */
4
+ import { Unstyled } from '@storybook/blocks';
5
+ import Box from '../Box/Box';
6
+ import Card from '../Card/Card';
7
+ import Icon, { TypeIconName } from '../Icon/Icon';
8
+ import Text from '../Text/Text';
9
+ import Table from '../Table/Table';
10
+ import Tbody from '../Table/Tbody';
11
+ import Td from '../Table/Td';
12
+ import Tr from '../Table/Tr';
13
+ import Th from '../Table/Th';
14
+ import Thead from '../Table/Thead';
15
+ import Provider from '../Provider/Provider';
16
+ import Input from '../Form/Input/Input';
17
+ import FilterSwitchGroup from '../Form/FilterSwitch/FilterSwitchGroup';
18
+ import FilterSwitch from '../Form/FilterSwitch/FilterSwitch';
19
+ import * as bigIcons from './24x24';
20
+ import { FigmaIcon, figmaIcons } from './figmaIcons';
21
+
22
+ const iconSizeTooltipSytle: SystemStyleObject = {
23
+ backgroundColor: 'purple.50',
24
+ color: 'neutral.100',
25
+ borderRadius: '3px',
26
+ position: 'absolute',
27
+ fontSize: '8px',
28
+ padding: '2px 4px',
29
+ lineHeight: '8px',
30
+ display: 'none',
31
+ };
32
+
33
+ const iconSizeTooltipHoverStyle: SystemStyleObject = {
34
+ svg: {
35
+ border: '1px dotted transparent',
36
+ },
37
+ '&:hover svg.icon-preview': {
38
+ borderColor: 'purple.50',
39
+ },
40
+ '&:hover .icon-size-tooltip': {
41
+ display: 'block',
42
+ },
43
+ };
44
+
45
+ const IconSizeTooltip = ({ size }: { size: '24' | '16' }) => {
46
+ return (
47
+ <Box
48
+ className="icon-size-tooltip"
49
+ sx={{
50
+ ...iconSizeTooltipSytle,
51
+ marginLeft: size === '24' ? '-1px' : '-5px',
52
+ marginTop: size === '24' ? '2px' : '0px',
53
+ }}
54
+ >
55
+ {`${size}px`}
56
+ </Box>
57
+ );
58
+ };
59
+
60
+ const IconCard = ({ figmaIcon, sx }: { figmaIcon: FigmaIcon; sx?: SystemStyleObject }) => {
61
+ const { onCopy } = useClipboard(figmaIcon.iconName, {});
62
+ const toast = useToast();
63
+ const name = (figmaIcon.iconName === '' ? 'WarningColored' : figmaIcon.iconName) as TypeIconName;
64
+ return (
65
+ <Tooltip
66
+ label={
67
+ <Text textAlign="center">
68
+ {figmaIcon.iconName || 'Missing!'}
69
+ <br />
70
+ click to copy <Icon size="16" color="white" name="Duplicate" />
71
+ </Text>
72
+ }
73
+ >
74
+ <Card
75
+ float="left"
76
+ width={150}
77
+ marginRight={8}
78
+ marginBottom={8}
79
+ color={name.match(/White$/) ? 'neutral.100' : 'text.body'}
80
+ background={name.match(/White$/) ? 'brand.primary' : 'neutral.100'}
81
+ sx={{
82
+ '&:hover': {
83
+ borderColor: 'purple.50',
84
+ },
85
+ ...iconSizeTooltipHoverStyle,
86
+ ...sx,
87
+ }}
88
+ >
89
+ <Grid
90
+ onClick={() => {
91
+ onCopy();
92
+ toast.closeAll();
93
+ toast({
94
+ title: (
95
+ <>
96
+ Copied <code>{figmaIcon.iconName}</code>!
97
+ </>
98
+ ),
99
+ });
100
+ }}
101
+ cursor="pointer"
102
+ alignContent="space-around"
103
+ gridTemplateColumns="1f 1f"
104
+ padding={8}
105
+ paddingTop={16}
106
+ gap="16"
107
+ alignItems="center"
108
+ >
109
+ <Box marginX="auto" opacity={figmaIcon.iconName === '' ? 0 : 1}>
110
+ <Icon className="icon-preview" name={name} size="24" />
111
+ <IconSizeTooltip size="24" />
112
+ </Box>
113
+ <Box marginX="auto" opacity={figmaIcon.iconName === '' ? 0 : 1}>
114
+ <Icon className="icon-preview" name={name} size="16" />
115
+ <IconSizeTooltip size="16" />
116
+ </Box>
117
+ <GridItem as={Text} hasEllipsis textAlign="center" fontSize="2" lineHeight="1.25rem" colSpan={2}>
118
+ {figmaIcon.iconName || '???'}
119
+ {figmaIcon.figmaToken && (
120
+ <>
121
+ <br />
122
+ <Box as="span" textAlign="center" fontSize="2" lineHeight="1.25rem" opacity={0.5}>
123
+ {figmaIcon.figmaToken}
124
+ </Box>
125
+ </>
126
+ )}
127
+ </GridItem>
128
+ </Grid>
129
+ </Card>
130
+ </Tooltip>
131
+ );
132
+ };
133
+
134
+ const IconGrid = ({ categoryName, icons }: { categoryName: string; icons: FigmaIcon[] }) => (
135
+ <>
136
+ <Box fontSize="small" opacity={0.5}>
137
+ {categoryName}
138
+ </Box>
139
+ <Box marginBottom="16" overflow="auto">
140
+ {icons.map((figmaIcon) => {
141
+ return (
142
+ <Fragment key={figmaIcon.figmaToken !== '???' ? figmaIcon.figmaToken : figmaIcon.iconName}>
143
+ <IconCard figmaIcon={figmaIcon} sx={figmaIcon.css || {}} />
144
+ </Fragment>
145
+ );
146
+ })}
147
+ </Box>
148
+ </>
149
+ );
150
+
151
+ const IconRow = ({ figmaIcon, sx }: { figmaIcon: FigmaIcon; sx?: SystemStyleObject }) => {
152
+ const { onCopy } = useClipboard(figmaIcon.iconName, {});
153
+ const toast = useToast();
154
+ const name = (figmaIcon.iconName === '' ? 'WarningColored' : figmaIcon.iconName) as TypeIconName;
155
+
156
+ const cellStyle = {
157
+ color: name.match(/White$/) ? 'neutral.100' : 'text.body',
158
+ backgroundColor: name.match(/White$/) ? 'purple.10' : 'neutral.100',
159
+ ...sx,
160
+ };
161
+ const cellStyleHover = {
162
+ backgroundColor: name.match(/White$/) ? 'purple.20' : 'neutral.95',
163
+ };
164
+
165
+ return (
166
+ <Tr
167
+ className="icon-row"
168
+ sx={{
169
+ ...iconSizeTooltipHoverStyle,
170
+ '&.icon-row:hover td': cellStyleHover,
171
+ }}
172
+ >
173
+ <Td sx={cellStyle} width={32}>
174
+ <Icon className="icon-preview" name={name} size="24" opacity={figmaIcon.iconName === '' ? 0 : 1} />
175
+ {figmaIcon.iconName !== '' && <IconSizeTooltip size="24" />}
176
+ </Td>
177
+ <Td sx={cellStyle} width={32}>
178
+ <Icon className="icon-preview" name={name} size="16" opacity={figmaIcon.iconName === '' ? 0 : 1} />
179
+ {figmaIcon.iconName !== '' && <IconSizeTooltip size="16" />}
180
+ </Td>
181
+ <Td sx={cellStyle} width="20%">
182
+ {figmaIcon.iconName || '???'}
183
+ <Icon
184
+ cursor="pointer"
185
+ onClick={() => {
186
+ onCopy();
187
+ toast.closeAll();
188
+ toast({
189
+ title: (
190
+ <>
191
+ Copied <code>{figmaIcon.iconName}</code>!
192
+ </>
193
+ ),
194
+ });
195
+ }}
196
+ marginLeft="8"
197
+ name="Duplicate"
198
+ size="16"
199
+ sx={{
200
+ path: {
201
+ fill: 'neutral.80',
202
+ },
203
+ }}
204
+ />
205
+ </Td>
206
+ <Td sx={cellStyle} width="20%">
207
+ <Box opacity={0.5}>{figmaIcon.figmaToken || '???'}</Box>
208
+ </Td>
209
+ <Td fontSize={2} sx={cellStyle}>
210
+ {figmaIcon.tags || ''}
211
+ </Td>
212
+ </Tr>
213
+ );
214
+ };
215
+
216
+ const IconTable = ({ categoryName, icons }: { categoryName: string; icons: FigmaIcon[] }) => (
217
+ <Fragment key={categoryName}>
218
+ <Thead>
219
+ <Tr>
220
+ <Th colSpan={5}>
221
+ <Box fontSize="small" opacity={0.5}>
222
+ {categoryName}
223
+ </Box>
224
+ </Th>
225
+ </Tr>
226
+ </Thead>
227
+ <Tbody>
228
+ {icons.map((figmaIcon) => {
229
+ return (
230
+ <Fragment key={figmaIcon.figmaToken !== '???' ? figmaIcon.figmaToken : figmaIcon.iconName}>
231
+ <IconRow figmaIcon={figmaIcon} sx={figmaIcon.css || {}} />
232
+ </Fragment>
233
+ );
234
+ })}
235
+ </Tbody>
236
+ </Fragment>
237
+ );
238
+
239
+ const findToken = (iconName: TypeIconName) => {
240
+ const filteredIcons = Object.values(figmaIcons)
241
+ .reduce((accumulator, value) => accumulator.concat(value), [])
242
+ .filter((icon) => icon.iconName === iconName);
243
+ if (filteredIcons.length > 0) return filteredIcons[0];
244
+ return null;
245
+ };
246
+
247
+ const getOldIcons = () =>
248
+ Object.keys(bigIcons)
249
+ .filter((icon) => {
250
+ const iconName = icon as TypeIconName;
251
+ const figmaToken = findToken(iconName);
252
+ if (figmaToken) return false;
253
+ return true;
254
+ })
255
+ .map((icon) => {
256
+ const iconName = icon as TypeIconName;
257
+ return {
258
+ iconName,
259
+ figmaToken: '???',
260
+ };
261
+ });
262
+
263
+ const searchIcons = (search: string) => {
264
+ const searchRegExp = new RegExp(`${search}`, 'im');
265
+ return Object.values(figmaIcons)
266
+ .reduce((accumulator, value) => accumulator.concat(value), [])
267
+ .concat(getOldIcons())
268
+ .filter((icon) => {
269
+ if (icon.iconName.match(searchRegExp)) return true;
270
+ if (icon.figmaToken.match(searchRegExp)) return true;
271
+ if (icon.tags && icon.tags.match(searchRegExp)) return true;
272
+ return false;
273
+ });
274
+ };
275
+
276
+ export const GridView = (args: { search: string }) => {
277
+ const { search } = args;
278
+
279
+ if (search) {
280
+ return <IconGrid key="Search" categoryName="Search results" icons={searchIcons(search)} />;
281
+ }
282
+
283
+ const figmaIconGrids = Object.keys(figmaIcons).map((categoryName) => (
284
+ <IconGrid key={categoryName} categoryName={categoryName} icons={figmaIcons[categoryName]} />
285
+ ));
286
+ const oldIconGrid = <IconGrid key="Uncategorized" categoryName="Uncategorized" icons={getOldIcons()} />;
287
+
288
+ return (
289
+ <>
290
+ {figmaIconGrids}
291
+ {oldIconGrid}
292
+ </>
293
+ );
294
+ };
295
+
296
+ export const TableView = (args: { search: string }) => {
297
+ const { search } = args;
298
+
299
+ if (search) {
300
+ return (
301
+ <Table>
302
+ <IconTable key="Search" categoryName="Search results" icons={searchIcons(search)} />
303
+ </Table>
304
+ );
305
+ }
306
+
307
+ const figmaIconTable = Object.keys(figmaIcons).map((categoryName) => (
308
+ <IconTable key={categoryName} categoryName={categoryName} icons={figmaIcons[categoryName]} />
309
+ ));
310
+ const oldIconTable = <IconTable key="Uncategorized" categoryName="Uncategorized" icons={getOldIcons()} />;
311
+
312
+ return (
313
+ <Table>
314
+ {figmaIconTable}
315
+ {oldIconTable}
316
+ </Table>
317
+ );
318
+ };
319
+
320
+ export const Icons = () => {
321
+ const [search, setSearch] = useState('');
322
+ const [layout, setLayout] = useState('grid');
323
+
324
+ const textWrapperStyle = {
325
+ marginBottom: '32',
326
+ maxWidth: '31.25rem',
327
+ };
328
+ return (
329
+ <Unstyled>
330
+ <Provider>
331
+ <Box sx={textWrapperStyle}>
332
+ <Text as="h2" size="8" sx={{ marginBottom: '24' }}>
333
+ Icons
334
+ </Text>
335
+ </Box>
336
+ <Box display="flex" alignItems="top">
337
+ <Box>
338
+ <FilterSwitchGroup value={layout} onChange={setLayout}>
339
+ <FilterSwitch value="grid">
340
+ <Icon name="MenuGrid" size="16" />
341
+ </FilterSwitch>
342
+ <FilterSwitch value="table">
343
+ <Icon name="MenuHamburger" size="16" />
344
+ </FilterSwitch>
345
+ </FilterSwitchGroup>
346
+ </Box>
347
+ <Box width="100%" marginLeft={16}>
348
+ <Input
349
+ leftIconName="Magnifier"
350
+ placeholder="Search icons..."
351
+ value={search}
352
+ inputHeight={38}
353
+ onChange={(event) => setSearch(event.target.value)}
354
+ />
355
+ </Box>
356
+ </Box>
357
+ <br />
358
+ {layout === 'grid' ? <GridView search={search} /> : null}
359
+ {layout === 'table' ? <TableView search={search} /> : null}
360
+ </Provider>
361
+ </Unstyled>
362
+ );
363
+ };
@@ -1,9 +1,11 @@
1
+ import { SystemStyleObject } from '@chakra-ui/styled-system';
1
2
  import { TypeIconName } from '../Icon/Icon';
2
3
 
3
4
  export type FigmaIcon = {
4
5
  iconName: TypeIconName | '';
5
6
  figmaToken: string;
6
7
  tags?: string;
8
+ css?: SystemStyleObject;
7
9
  };
8
10
 
9
11
  export const figmaIcons: {
@@ -269,10 +271,48 @@ export const figmaIcons: {
269
271
  { iconName: 'Wow', figmaToken: 'wow' },
270
272
  ],
271
273
  'UI Components': [
272
- { iconName: '', figmaToken: 'table-sort: Not sorted' },
273
- { iconName: 'Sort', figmaToken: 'table-sort: Hover' },
274
- { iconName: '', figmaToken: 'table-sort: Up' },
275
- { iconName: '', figmaToken: 'table-sort: Descending' },
274
+ {
275
+ iconName: 'Sort',
276
+ figmaToken: 'table-sort: Not sorted',
277
+ css: {
278
+ path: {
279
+ fill: 'neutral.80',
280
+ },
281
+ },
282
+ },
283
+ {
284
+ iconName: 'Sort',
285
+ figmaToken: 'table-sort: Hover',
286
+ css: {
287
+ path: {
288
+ fill: 'neutral.10',
289
+ },
290
+ },
291
+ },
292
+ {
293
+ iconName: 'Sort',
294
+ figmaToken: 'table-sort: Up',
295
+ css: {
296
+ path: {
297
+ fill: 'neutral.80',
298
+ },
299
+ 'path:first-of-type': {
300
+ fill: 'neutral.10',
301
+ },
302
+ },
303
+ },
304
+ {
305
+ iconName: 'Sort',
306
+ figmaToken: 'table-sort: Descending',
307
+ css: {
308
+ path: {
309
+ fill: 'neutral.80',
310
+ },
311
+ 'path:last-of-type': {
312
+ fill: 'neutral.10',
313
+ },
314
+ },
315
+ },
276
316
  { iconName: 'DropdownArrows', figmaToken: 'dropdown-arrows' },
277
317
  ],
278
318
  };