@ankhorage/zora 1.0.4 → 1.0.6
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/CHANGELOG.md +12 -0
- package/README.md +315 -5
- package/dist/components/avatar/Avatar.d.ts +4 -0
- package/dist/components/avatar/Avatar.d.ts.map +1 -0
- package/dist/components/avatar/Avatar.js +80 -0
- package/dist/components/avatar/Avatar.js.map +1 -0
- package/dist/components/avatar/index.d.ts +4 -0
- package/dist/components/avatar/index.d.ts.map +1 -0
- package/dist/components/avatar/index.js +3 -0
- package/dist/components/avatar/index.js.map +1 -0
- package/dist/components/avatar/resolveAvatarInitials.d.ts +2 -0
- package/dist/components/avatar/resolveAvatarInitials.d.ts.map +1 -0
- package/dist/components/avatar/resolveAvatarInitials.js +44 -0
- package/dist/components/avatar/resolveAvatarInitials.js.map +1 -0
- package/dist/components/avatar/types.d.ts +17 -0
- package/dist/components/avatar/types.d.ts.map +1 -0
- package/dist/components/avatar/types.js +2 -0
- package/dist/components/avatar/types.js.map +1 -0
- package/dist/components/avatar-group/AvatarGroup.d.ts +4 -0
- package/dist/components/avatar-group/AvatarGroup.d.ts.map +1 -0
- package/dist/components/avatar-group/AvatarGroup.js +26 -0
- package/dist/components/avatar-group/AvatarGroup.js.map +1 -0
- package/dist/components/avatar-group/index.d.ts +3 -0
- package/dist/components/avatar-group/index.d.ts.map +1 -0
- package/dist/components/avatar-group/index.js +2 -0
- package/dist/components/avatar-group/index.js.map +1 -0
- package/dist/components/avatar-group/types.d.ts +22 -0
- package/dist/components/avatar-group/types.d.ts.map +1 -0
- package/dist/components/avatar-group/types.js +2 -0
- package/dist/components/avatar-group/types.js.map +1 -0
- package/dist/components/chip/Chip.d.ts +4 -0
- package/dist/components/chip/Chip.d.ts.map +1 -0
- package/dist/components/chip/Chip.js +54 -0
- package/dist/components/chip/Chip.js.map +1 -0
- package/dist/components/chip/index.d.ts +3 -0
- package/dist/components/chip/index.d.ts.map +1 -0
- package/dist/components/chip/index.js +2 -0
- package/dist/components/chip/index.js.map +1 -0
- package/dist/components/chip/resolveChipColors.d.ts +10 -0
- package/dist/components/chip/resolveChipColors.d.ts.map +1 -0
- package/dist/components/chip/resolveChipColors.js +47 -0
- package/dist/components/chip/resolveChipColors.js.map +1 -0
- package/dist/components/chip/types.d.ts +26 -0
- package/dist/components/chip/types.d.ts.map +1 -0
- package/dist/components/chip/types.js +2 -0
- package/dist/components/chip/types.js.map +1 -0
- package/dist/components/chip-group/ChipGroup.d.ts +4 -0
- package/dist/components/chip-group/ChipGroup.d.ts.map +1 -0
- package/dist/components/chip-group/ChipGroup.js +32 -0
- package/dist/components/chip-group/ChipGroup.js.map +1 -0
- package/dist/components/chip-group/index.d.ts +3 -0
- package/dist/components/chip-group/index.d.ts.map +1 -0
- package/dist/components/chip-group/index.js +2 -0
- package/dist/components/chip-group/index.js.map +1 -0
- package/dist/components/chip-group/types.d.ts +31 -0
- package/dist/components/chip-group/types.d.ts.map +1 -0
- package/dist/components/chip-group/types.js +2 -0
- package/dist/components/chip-group/types.js.map +1 -0
- package/dist/components/input/Input.d.ts.map +1 -1
- package/dist/components/input/Input.js +3 -2
- package/dist/components/input/Input.js.map +1 -1
- package/dist/components/input/index.d.ts +1 -1
- package/dist/components/input/index.d.ts.map +1 -1
- package/dist/components/input/index.js.map +1 -1
- package/dist/components/input/types.d.ts +15 -2
- package/dist/components/input/types.d.ts.map +1 -1
- package/dist/components/input/types.js.map +1 -1
- package/dist/components/search-bar/SearchBar.d.ts +4 -0
- package/dist/components/search-bar/SearchBar.d.ts.map +1 -0
- package/dist/components/search-bar/SearchBar.js +18 -0
- package/dist/components/search-bar/SearchBar.js.map +1 -0
- package/dist/components/search-bar/index.d.ts +3 -0
- package/dist/components/search-bar/index.d.ts.map +1 -0
- package/dist/components/search-bar/index.js +2 -0
- package/dist/components/search-bar/index.js.map +1 -0
- package/dist/components/search-bar/types.d.ts +14 -0
- package/dist/components/search-bar/types.d.ts.map +1 -0
- package/dist/components/search-bar/types.js +2 -0
- package/dist/components/search-bar/types.js.map +1 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/patterns/filter-bar/FilterBar.d.ts +4 -0
- package/dist/patterns/filter-bar/FilterBar.d.ts.map +1 -0
- package/dist/patterns/filter-bar/FilterBar.js +12 -0
- package/dist/patterns/filter-bar/FilterBar.js.map +1 -0
- package/dist/patterns/filter-bar/index.d.ts +3 -0
- package/dist/patterns/filter-bar/index.d.ts.map +1 -0
- package/dist/patterns/filter-bar/index.js +2 -0
- package/dist/patterns/filter-bar/index.js.map +1 -0
- package/dist/patterns/filter-bar/types.d.ts +9 -0
- package/dist/patterns/filter-bar/types.d.ts.map +1 -0
- package/dist/patterns/filter-bar/types.js +2 -0
- package/dist/patterns/filter-bar/types.js.map +1 -0
- package/dist/patterns/list/List.d.ts +4 -0
- package/dist/patterns/list/List.d.ts.map +1 -0
- package/dist/patterns/list/List.js +35 -0
- package/dist/patterns/list/List.js.map +1 -0
- package/dist/patterns/list/ListRow.d.ts +4 -0
- package/dist/patterns/list/ListRow.d.ts.map +1 -0
- package/dist/patterns/list/ListRow.js +108 -0
- package/dist/patterns/list/ListRow.js.map +1 -0
- package/dist/patterns/list/ListSection.d.ts +4 -0
- package/dist/patterns/list/ListSection.d.ts.map +1 -0
- package/dist/patterns/list/ListSection.js +14 -0
- package/dist/patterns/list/ListSection.js.map +1 -0
- package/dist/patterns/list/index.d.ts +5 -0
- package/dist/patterns/list/index.d.ts.map +1 -0
- package/dist/patterns/list/index.js +4 -0
- package/dist/patterns/list/index.js.map +1 -0
- package/dist/patterns/list/resolveListSeparator.d.ts +5 -0
- package/dist/patterns/list/resolveListSeparator.d.ts.map +1 -0
- package/dist/patterns/list/resolveListSeparator.js +6 -0
- package/dist/patterns/list/resolveListSeparator.js.map +1 -0
- package/dist/patterns/list/types.d.ts +55 -0
- package/dist/patterns/list/types.d.ts.map +1 -0
- package/dist/patterns/list/types.js +2 -0
- package/dist/patterns/list/types.js.map +1 -0
- package/package.json +1 -1
- package/src/components/avatar/Avatar.tsx +133 -0
- package/src/components/avatar/index.ts +3 -0
- package/src/components/avatar/resolveAvatarInitials.test.ts +27 -0
- package/src/components/avatar/resolveAvatarInitials.ts +46 -0
- package/src/components/avatar/types.ts +20 -0
- package/src/components/avatar-group/AvatarGroup.tsx +74 -0
- package/src/components/avatar-group/index.ts +2 -0
- package/src/components/avatar-group/types.ts +24 -0
- package/src/components/chip/Chip.tsx +95 -0
- package/src/components/chip/index.ts +2 -0
- package/src/components/chip/resolveChipColors.ts +65 -0
- package/src/components/chip/types.ts +29 -0
- package/src/components/chip-group/ChipGroup.tsx +66 -0
- package/src/components/chip-group/index.ts +2 -0
- package/src/components/chip-group/types.ts +36 -0
- package/src/components/input/Input.tsx +17 -1
- package/src/components/input/index.ts +1 -1
- package/src/components/input/types.ts +19 -2
- package/src/components/search-bar/SearchBar.tsx +50 -0
- package/src/components/search-bar/index.ts +2 -0
- package/src/components/search-bar/types.ts +14 -0
- package/src/index.ts +22 -1
- package/src/patterns/filter-bar/FilterBar.tsx +25 -0
- package/src/patterns/filter-bar/index.ts +2 -0
- package/src/patterns/filter-bar/types.ts +10 -0
- package/src/patterns/list/List.tsx +72 -0
- package/src/patterns/list/ListRow.tsx +193 -0
- package/src/patterns/list/ListSection.tsx +36 -0
- package/src/patterns/list/index.ts +11 -0
- package/src/patterns/list/resolveListSeparator.test.ts +18 -0
- package/src/patterns/list/resolveListSeparator.ts +8 -0
- package/src/patterns/list/types.ts +67 -0
- package/src/showcaseCoverage.test.ts +9 -0
- package/src/theme/themeScopeStructure.test.ts +4 -0
|
@@ -3,7 +3,23 @@ import type * as Surface from '@ankhorage/surface';
|
|
|
3
3
|
import type { ZoraControlSize } from '../../internal/recipes';
|
|
4
4
|
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
5
5
|
|
|
6
|
-
export interface
|
|
6
|
+
export interface InputTrailingAction {
|
|
7
|
+
icon: Surface.ButtonIconSpec;
|
|
8
|
+
label: string;
|
|
9
|
+
onPress: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type InputTrailingProps =
|
|
13
|
+
| {
|
|
14
|
+
trailingIcon?: Surface.ButtonIconSpec;
|
|
15
|
+
trailingAction?: never;
|
|
16
|
+
}
|
|
17
|
+
| {
|
|
18
|
+
trailingIcon?: never;
|
|
19
|
+
trailingAction?: InputTrailingAction;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface InputBaseProps
|
|
7
23
|
extends
|
|
8
24
|
ZoraBaseProps,
|
|
9
25
|
Omit<
|
|
@@ -12,5 +28,6 @@ export interface InputProps
|
|
|
12
28
|
> {
|
|
13
29
|
size?: ZoraControlSize;
|
|
14
30
|
leadingIcon?: Surface.ButtonIconSpec;
|
|
15
|
-
trailingIcon?: Surface.ButtonIconSpec;
|
|
16
31
|
}
|
|
32
|
+
|
|
33
|
+
export type InputProps = InputBaseProps & InputTrailingProps;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
4
|
+
import { Input, type InputTrailingAction } from '../input';
|
|
5
|
+
import type { SearchBarProps } from './types';
|
|
6
|
+
|
|
7
|
+
function SearchBarInner({
|
|
8
|
+
themeId: _themeId,
|
|
9
|
+
mode: _mode,
|
|
10
|
+
testID,
|
|
11
|
+
value,
|
|
12
|
+
onValueChange,
|
|
13
|
+
placeholder = 'Search',
|
|
14
|
+
onSubmit,
|
|
15
|
+
onClear,
|
|
16
|
+
clearable = true,
|
|
17
|
+
size = 'l',
|
|
18
|
+
disabled,
|
|
19
|
+
readOnly,
|
|
20
|
+
}: SearchBarProps) {
|
|
21
|
+
const trailingAction: InputTrailingAction | undefined =
|
|
22
|
+
clearable && value.length > 0
|
|
23
|
+
? {
|
|
24
|
+
icon: { name: 'close-circle' },
|
|
25
|
+
label: 'Clear search',
|
|
26
|
+
onPress: () => {
|
|
27
|
+
onValueChange('');
|
|
28
|
+
onClear?.();
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
: undefined;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Input
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
leadingIcon={{ name: 'search-outline' }}
|
|
37
|
+
onChangeText={onValueChange}
|
|
38
|
+
onSubmitEditing={onSubmit ? () => onSubmit(value) : undefined}
|
|
39
|
+
placeholder={placeholder}
|
|
40
|
+
readOnly={readOnly}
|
|
41
|
+
returnKeyType="search"
|
|
42
|
+
size={size}
|
|
43
|
+
testID={testID}
|
|
44
|
+
trailingAction={trailingAction}
|
|
45
|
+
value={value}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const SearchBar = withZoraThemeScope(SearchBarInner);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ZoraControlSize } from '../../internal/recipes';
|
|
2
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
3
|
+
|
|
4
|
+
export interface SearchBarProps extends ZoraBaseProps {
|
|
5
|
+
value: string;
|
|
6
|
+
onValueChange: (value: string) => void;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
onSubmit?: (value: string) => void;
|
|
9
|
+
onClear?: () => void;
|
|
10
|
+
clearable?: boolean;
|
|
11
|
+
size?: ZoraControlSize;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export type { AvatarProps, AvatarShape, AvatarSize } from './components/avatar';
|
|
2
|
+
export { Avatar, resolveAvatarInitials } from './components/avatar';
|
|
3
|
+
export type { AvatarGroupItem, AvatarGroupProps } from './components/avatar-group';
|
|
4
|
+
export { AvatarGroup } from './components/avatar-group';
|
|
1
5
|
export type { BadgeProps } from './components/badge';
|
|
2
6
|
export { Badge } from './components/badge';
|
|
3
7
|
export type { ButtonProps } from './components/button';
|
|
@@ -6,6 +10,10 @@ export type { CardProps } from './components/card';
|
|
|
6
10
|
export { Card } from './components/card';
|
|
7
11
|
export type { CheckboxGroupOption, CheckboxGroupProps, CheckboxProps } from './components/checkbox';
|
|
8
12
|
export { Checkbox, CheckboxGroup } from './components/checkbox';
|
|
13
|
+
export type { ChipProps } from './components/chip';
|
|
14
|
+
export { Chip } from './components/chip';
|
|
15
|
+
export type { ChipGroupItem, ChipGroupProps } from './components/chip-group';
|
|
16
|
+
export { ChipGroup } from './components/chip-group';
|
|
9
17
|
export type { DrawerProps } from './components/drawer';
|
|
10
18
|
export { Drawer } from './components/drawer';
|
|
11
19
|
export type {
|
|
@@ -50,12 +58,14 @@ export type { IconProps } from './components/icon';
|
|
|
50
58
|
export { Icon } from './components/icon';
|
|
51
59
|
export type { IconButtonProps } from './components/icon-button';
|
|
52
60
|
export { IconButton } from './components/icon-button';
|
|
53
|
-
export type { InputProps } from './components/input';
|
|
61
|
+
export type { InputProps, InputTrailingAction } from './components/input';
|
|
54
62
|
export { Input } from './components/input';
|
|
55
63
|
export type { ModalProps } from './components/modal';
|
|
56
64
|
export { Modal } from './components/modal';
|
|
57
65
|
export type { RadioGroupOption, RadioGroupProps, RadioProps } from './components/radio';
|
|
58
66
|
export { Radio, RadioGroup } from './components/radio';
|
|
67
|
+
export type { SearchBarProps } from './components/search-bar';
|
|
68
|
+
export { SearchBar } from './components/search-bar';
|
|
59
69
|
export type { SelectOption, SelectProps } from './components/select';
|
|
60
70
|
export { Select } from './components/select';
|
|
61
71
|
export type { TabItem, TabsProps } from './components/tabs';
|
|
@@ -132,8 +142,19 @@ export type { DisclosureSectionProps } from './patterns/disclosure-section';
|
|
|
132
142
|
export { DisclosureSection } from './patterns/disclosure-section';
|
|
133
143
|
export type { EmptyStateAction, EmptyStateProps } from './patterns/empty-state';
|
|
134
144
|
export { EmptyState } from './patterns/empty-state';
|
|
145
|
+
export type { FilterBarProps } from './patterns/filter-bar';
|
|
146
|
+
export { FilterBar } from './patterns/filter-bar';
|
|
135
147
|
export type { InspectorFieldProps } from './patterns/inspector-field';
|
|
136
148
|
export { InspectorField } from './patterns/inspector-field';
|
|
149
|
+
export type {
|
|
150
|
+
ListChildrenProps,
|
|
151
|
+
ListItemsProps,
|
|
152
|
+
ListProps,
|
|
153
|
+
ListRowProps,
|
|
154
|
+
ListRowVariant,
|
|
155
|
+
ListSectionProps,
|
|
156
|
+
} from './patterns/list';
|
|
157
|
+
export { List, ListRow, ListSection } from './patterns/list';
|
|
137
158
|
export type { NoticeProps } from './patterns/notice';
|
|
138
159
|
export { Notice } from './patterns/notice';
|
|
139
160
|
export type { PanelProps } from './patterns/panel';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Box, Inline } from '../../foundation';
|
|
4
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
5
|
+
import type { FilterBarProps } from './types';
|
|
6
|
+
|
|
7
|
+
function FilterBarInner({
|
|
8
|
+
themeId: _themeId,
|
|
9
|
+
mode: _mode,
|
|
10
|
+
testID,
|
|
11
|
+
leading,
|
|
12
|
+
trailing,
|
|
13
|
+
children,
|
|
14
|
+
wrap = true,
|
|
15
|
+
}: FilterBarProps) {
|
|
16
|
+
return (
|
|
17
|
+
<Inline align="center" gap="s" testID={testID} wrap={wrap ? 'wrap' : 'nowrap'}>
|
|
18
|
+
{leading ? <Box>{leading}</Box> : null}
|
|
19
|
+
<Box flex={1}>{children}</Box>
|
|
20
|
+
{trailing ? <Box>{trailing}</Box> : null}
|
|
21
|
+
</Inline>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const FilterBar = withZoraThemeScope(FilterBarInner);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
4
|
+
|
|
5
|
+
export interface FilterBarProps extends ZoraBaseProps {
|
|
6
|
+
leading?: React.ReactNode;
|
|
7
|
+
trailing?: React.ReactNode;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
wrap?: boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Divider, Spacer, Stack } from '../../foundation';
|
|
4
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
5
|
+
import { ListRow } from './ListRow';
|
|
6
|
+
import { resolveListSeparator } from './resolveListSeparator';
|
|
7
|
+
import type { ListItemsProps, ListProps, ListRowProps, ListRowVariant } from './types';
|
|
8
|
+
|
|
9
|
+
function resolveRowVariant({
|
|
10
|
+
item,
|
|
11
|
+
defaultVariant,
|
|
12
|
+
}: {
|
|
13
|
+
item: ListRowProps;
|
|
14
|
+
defaultVariant: ListRowVariant;
|
|
15
|
+
}): ListRowVariant {
|
|
16
|
+
return item.variant ?? defaultVariant;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveRowCompact({
|
|
20
|
+
item,
|
|
21
|
+
compact,
|
|
22
|
+
}: {
|
|
23
|
+
item: ListRowProps;
|
|
24
|
+
compact: boolean | undefined;
|
|
25
|
+
}): boolean {
|
|
26
|
+
return item.compact ?? compact ?? false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ListItemsInner({
|
|
30
|
+
themeId: _themeId,
|
|
31
|
+
mode: _mode,
|
|
32
|
+
testID,
|
|
33
|
+
items,
|
|
34
|
+
rowVariant = 'divider',
|
|
35
|
+
compact,
|
|
36
|
+
}: ListItemsProps) {
|
|
37
|
+
return (
|
|
38
|
+
<Stack gap="none" testID={testID}>
|
|
39
|
+
{items.map((item, index) => {
|
|
40
|
+
const effectiveVariant = resolveRowVariant({ item, defaultVariant: rowVariant });
|
|
41
|
+
const separator = resolveListSeparator(effectiveVariant, index);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<React.Fragment key={`${index}`}>
|
|
45
|
+
{separator === 'divider' ? <Divider /> : null}
|
|
46
|
+
{separator === 'spacer' ? <Spacer size="s" /> : null}
|
|
47
|
+
<ListRow
|
|
48
|
+
{...item}
|
|
49
|
+
compact={resolveRowCompact({ item, compact })}
|
|
50
|
+
variant={effectiveVariant}
|
|
51
|
+
/>
|
|
52
|
+
</React.Fragment>
|
|
53
|
+
);
|
|
54
|
+
})}
|
|
55
|
+
</Stack>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ListInner(props: ListProps) {
|
|
60
|
+
if ('items' in props) {
|
|
61
|
+
return <ListItemsInner {...props} />;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { themeId: _themeId, mode: _mode, children, testID } = props;
|
|
65
|
+
return (
|
|
66
|
+
<Stack gap="none" testID={testID}>
|
|
67
|
+
{children}
|
|
68
|
+
</Stack>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const List = withZoraThemeScope(ListInner);
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { ButtonBase } from '@ankhorage/surface';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { Text } from '../../components/text';
|
|
5
|
+
import { Box, Inline, Spacer, Stack } from '../../foundation';
|
|
6
|
+
import { useZoraTheme } from '../../theme/useZoraTheme';
|
|
7
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
8
|
+
import type { ListRowProps, ListRowVariant } from './types';
|
|
9
|
+
|
|
10
|
+
function resolvePadding(compact: boolean) {
|
|
11
|
+
return compact ? { px: 'm' as const, py: 's' as const } : { px: 'm' as const, py: 'm' as const };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function resolveContainerStyles({
|
|
15
|
+
variant,
|
|
16
|
+
theme,
|
|
17
|
+
selected,
|
|
18
|
+
pressed,
|
|
19
|
+
hovered,
|
|
20
|
+
disabled,
|
|
21
|
+
}: {
|
|
22
|
+
variant: ListRowVariant;
|
|
23
|
+
theme: ReturnType<typeof useZoraTheme>['theme'];
|
|
24
|
+
selected: boolean;
|
|
25
|
+
pressed: boolean;
|
|
26
|
+
hovered: boolean;
|
|
27
|
+
disabled: boolean;
|
|
28
|
+
}) {
|
|
29
|
+
const borderColor = selected ? theme.semantics.border.focus : theme.semantics.border.default;
|
|
30
|
+
const dividerBorderColor = selected ? theme.semantics.border.focus : 'transparent';
|
|
31
|
+
|
|
32
|
+
if (variant === 'card') {
|
|
33
|
+
return {
|
|
34
|
+
bg: pressed
|
|
35
|
+
? theme.semantics.neutral.surfaceActive
|
|
36
|
+
: hovered
|
|
37
|
+
? theme.semantics.neutral.surfaceHover
|
|
38
|
+
: theme.semantics.surface.raised,
|
|
39
|
+
borderColor,
|
|
40
|
+
borderWidth: 1,
|
|
41
|
+
radius: 'l' as const,
|
|
42
|
+
opacity: disabled ? 0.72 : 1,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
bg: pressed
|
|
48
|
+
? theme.semantics.neutral.surfaceActive
|
|
49
|
+
: hovered
|
|
50
|
+
? theme.semantics.neutral.surfaceHover
|
|
51
|
+
: selected
|
|
52
|
+
? theme.semantics.neutral.surface
|
|
53
|
+
: 'transparent',
|
|
54
|
+
borderColor: dividerBorderColor,
|
|
55
|
+
borderWidth: selected ? 1 : 0,
|
|
56
|
+
radius: 'm' as const,
|
|
57
|
+
opacity: disabled ? 0.72 : 1,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function ListRowInner({
|
|
62
|
+
themeId: _themeId,
|
|
63
|
+
mode: _mode,
|
|
64
|
+
testID,
|
|
65
|
+
title,
|
|
66
|
+
description,
|
|
67
|
+
meta,
|
|
68
|
+
leading,
|
|
69
|
+
trailing,
|
|
70
|
+
action,
|
|
71
|
+
onPress,
|
|
72
|
+
selected = false,
|
|
73
|
+
disabled = false,
|
|
74
|
+
compact = false,
|
|
75
|
+
variant = 'divider',
|
|
76
|
+
}: ListRowProps) {
|
|
77
|
+
const { theme } = useZoraTheme();
|
|
78
|
+
const padding = resolvePadding(compact);
|
|
79
|
+
const isInteractive = Boolean(onPress) && !action;
|
|
80
|
+
|
|
81
|
+
const content = (
|
|
82
|
+
<Stack
|
|
83
|
+
align="center"
|
|
84
|
+
direction="row"
|
|
85
|
+
style={{
|
|
86
|
+
flex: 1,
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
{leading ? (
|
|
90
|
+
<>
|
|
91
|
+
<Box>{leading}</Box>
|
|
92
|
+
<Spacer axis="horizontal" size="m" />
|
|
93
|
+
</>
|
|
94
|
+
) : null}
|
|
95
|
+
|
|
96
|
+
<Box flex={1}>
|
|
97
|
+
<Stack gap="xxs">
|
|
98
|
+
<Text variant="body" weight={selected ? 'semiBold' : 'medium'}>
|
|
99
|
+
{title}
|
|
100
|
+
</Text>
|
|
101
|
+
{description ? (
|
|
102
|
+
<Text tone="muted" variant="bodySmall">
|
|
103
|
+
{description}
|
|
104
|
+
</Text>
|
|
105
|
+
) : null}
|
|
106
|
+
{meta ? (
|
|
107
|
+
<Text tone="subtle" variant="caption">
|
|
108
|
+
{meta}
|
|
109
|
+
</Text>
|
|
110
|
+
) : null}
|
|
111
|
+
</Stack>
|
|
112
|
+
</Box>
|
|
113
|
+
|
|
114
|
+
{trailing || action ? (
|
|
115
|
+
<>
|
|
116
|
+
<Spacer axis="horizontal" size="m" />
|
|
117
|
+
<Inline align="center" gap="s" wrap="nowrap">
|
|
118
|
+
{trailing}
|
|
119
|
+
{action}
|
|
120
|
+
</Inline>
|
|
121
|
+
</>
|
|
122
|
+
) : null}
|
|
123
|
+
</Stack>
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (!isInteractive) {
|
|
127
|
+
const styles = resolveContainerStyles({
|
|
128
|
+
variant,
|
|
129
|
+
theme,
|
|
130
|
+
selected,
|
|
131
|
+
pressed: false,
|
|
132
|
+
hovered: false,
|
|
133
|
+
disabled,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<Box
|
|
138
|
+
bg={styles.bg}
|
|
139
|
+
borderColor={styles.borderColor}
|
|
140
|
+
borderWidth={styles.borderWidth}
|
|
141
|
+
px={padding.px}
|
|
142
|
+
py={padding.py}
|
|
143
|
+
radius={styles.radius}
|
|
144
|
+
testID={testID}
|
|
145
|
+
style={{
|
|
146
|
+
opacity: styles.opacity,
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{content}
|
|
150
|
+
</Box>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<ButtonBase
|
|
156
|
+
accessibilityRole="button"
|
|
157
|
+
accessibilityState={{ disabled, selected }}
|
|
158
|
+
disabled={disabled}
|
|
159
|
+
onPress={onPress}
|
|
160
|
+
radius={variant === 'card' ? 'l' : 'm'}
|
|
161
|
+
testID={testID}
|
|
162
|
+
>
|
|
163
|
+
{(state) => {
|
|
164
|
+
const styles = resolveContainerStyles({
|
|
165
|
+
variant,
|
|
166
|
+
theme,
|
|
167
|
+
selected,
|
|
168
|
+
pressed: state.pressed,
|
|
169
|
+
hovered: state.hovered,
|
|
170
|
+
disabled: state.disabled,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<Box
|
|
175
|
+
bg={styles.bg}
|
|
176
|
+
borderColor={styles.borderColor}
|
|
177
|
+
borderWidth={styles.borderWidth}
|
|
178
|
+
px={padding.px}
|
|
179
|
+
py={padding.py}
|
|
180
|
+
radius={styles.radius}
|
|
181
|
+
style={{
|
|
182
|
+
opacity: styles.opacity,
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
{content}
|
|
186
|
+
</Box>
|
|
187
|
+
);
|
|
188
|
+
}}
|
|
189
|
+
</ButtonBase>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const ListRow = withZoraThemeScope(ListRowInner);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Stack } from '../../foundation';
|
|
4
|
+
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
5
|
+
import { SectionHeader } from '../section-header';
|
|
6
|
+
import { List } from './List';
|
|
7
|
+
import type { ListSectionProps } from './types';
|
|
8
|
+
|
|
9
|
+
function ListSectionInner({
|
|
10
|
+
themeId: _themeId,
|
|
11
|
+
mode: _mode,
|
|
12
|
+
testID,
|
|
13
|
+
title,
|
|
14
|
+
description,
|
|
15
|
+
eyebrow,
|
|
16
|
+
actions,
|
|
17
|
+
...props
|
|
18
|
+
}: ListSectionProps) {
|
|
19
|
+
const hasHeader = title !== undefined;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Stack gap="s" testID={testID}>
|
|
23
|
+
{hasHeader ? (
|
|
24
|
+
<SectionHeader
|
|
25
|
+
actions={actions}
|
|
26
|
+
description={description}
|
|
27
|
+
eyebrow={eyebrow}
|
|
28
|
+
title={title}
|
|
29
|
+
/>
|
|
30
|
+
) : null}
|
|
31
|
+
<List {...props} />
|
|
32
|
+
</Stack>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const ListSection = withZoraThemeScope(ListSectionInner);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { List } from './List';
|
|
2
|
+
export { ListRow } from './ListRow';
|
|
3
|
+
export { ListSection } from './ListSection';
|
|
4
|
+
export type {
|
|
5
|
+
ListChildrenProps,
|
|
6
|
+
ListItemsProps,
|
|
7
|
+
ListProps,
|
|
8
|
+
ListRowProps,
|
|
9
|
+
ListRowVariant,
|
|
10
|
+
ListSectionProps,
|
|
11
|
+
} from './types';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { resolveListSeparator } from './resolveListSeparator';
|
|
4
|
+
|
|
5
|
+
describe('resolveListSeparator', () => {
|
|
6
|
+
it('returns none for the first item', () => {
|
|
7
|
+
expect(resolveListSeparator('divider', 0)).toBe('none');
|
|
8
|
+
expect(resolveListSeparator('card', 0)).toBe('none');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('returns divider for divider rows', () => {
|
|
12
|
+
expect(resolveListSeparator('divider', 1)).toBe('divider');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('returns spacer for card rows', () => {
|
|
16
|
+
expect(resolveListSeparator('card', 1)).toBe('spacer');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ListRowVariant } from './types';
|
|
2
|
+
|
|
3
|
+
type ListSeparatorKind = 'none' | 'divider' | 'spacer';
|
|
4
|
+
|
|
5
|
+
export function resolveListSeparator(variant: ListRowVariant, index: number): ListSeparatorKind {
|
|
6
|
+
if (index === 0) return 'none';
|
|
7
|
+
return variant === 'divider' ? 'divider' : 'spacer';
|
|
8
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
4
|
+
|
|
5
|
+
export type ListRowVariant = 'divider' | 'card';
|
|
6
|
+
|
|
7
|
+
interface ListRowBaseProps extends ZoraBaseProps {
|
|
8
|
+
title: React.ReactNode;
|
|
9
|
+
description?: React.ReactNode;
|
|
10
|
+
meta?: React.ReactNode;
|
|
11
|
+
leading?: React.ReactNode;
|
|
12
|
+
trailing?: React.ReactNode;
|
|
13
|
+
selected?: boolean;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
compact?: boolean;
|
|
16
|
+
variant?: ListRowVariant;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ListRowPressableProps {
|
|
20
|
+
onPress: () => void;
|
|
21
|
+
action?: never;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ListRowActionProps {
|
|
25
|
+
action: React.ReactNode;
|
|
26
|
+
onPress?: never;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ListRowStaticProps {
|
|
30
|
+
action?: never;
|
|
31
|
+
onPress?: never;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type ListRowProps = ListRowBaseProps &
|
|
35
|
+
(ListRowPressableProps | ListRowActionProps | ListRowStaticProps);
|
|
36
|
+
|
|
37
|
+
export interface ListItemsProps extends ZoraBaseProps {
|
|
38
|
+
items: readonly ListRowProps[];
|
|
39
|
+
rowVariant?: ListRowVariant;
|
|
40
|
+
compact?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ListChildrenProps extends ZoraBaseProps {
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type ListProps = ListItemsProps | ListChildrenProps;
|
|
48
|
+
|
|
49
|
+
export interface ListSectionItemsProps extends ZoraBaseProps {
|
|
50
|
+
title?: React.ReactNode;
|
|
51
|
+
description?: React.ReactNode;
|
|
52
|
+
eyebrow?: React.ReactNode;
|
|
53
|
+
actions?: React.ReactNode;
|
|
54
|
+
items: readonly ListRowProps[];
|
|
55
|
+
rowVariant?: ListRowVariant;
|
|
56
|
+
compact?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ListSectionChildrenProps extends ZoraBaseProps {
|
|
60
|
+
title?: React.ReactNode;
|
|
61
|
+
description?: React.ReactNode;
|
|
62
|
+
eyebrow?: React.ReactNode;
|
|
63
|
+
actions?: React.ReactNode;
|
|
64
|
+
children: React.ReactNode;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type ListSectionProps = ListSectionItemsProps | ListSectionChildrenProps;
|
|
@@ -17,11 +17,15 @@ const IGNORED_DIRECTORY_NAMES = new Set([
|
|
|
17
17
|
|
|
18
18
|
const REQUIRED_SHOWCASE_COVERAGE = {
|
|
19
19
|
components: [
|
|
20
|
+
'Avatar',
|
|
21
|
+
'AvatarGroup',
|
|
20
22
|
'Badge',
|
|
21
23
|
'Button',
|
|
22
24
|
'Card',
|
|
23
25
|
'Checkbox',
|
|
24
26
|
'CheckboxGroup',
|
|
27
|
+
'Chip',
|
|
28
|
+
'ChipGroup',
|
|
25
29
|
'Drawer',
|
|
26
30
|
'Form',
|
|
27
31
|
'FormActions',
|
|
@@ -34,6 +38,7 @@ const REQUIRED_SHOWCASE_COVERAGE = {
|
|
|
34
38
|
'Modal',
|
|
35
39
|
'Radio',
|
|
36
40
|
'RadioGroup',
|
|
41
|
+
'SearchBar',
|
|
37
42
|
'Select',
|
|
38
43
|
'Tabs',
|
|
39
44
|
'Text',
|
|
@@ -72,6 +77,10 @@ const REQUIRED_SHOWCASE_COVERAGE = {
|
|
|
72
77
|
'ConfirmDialog',
|
|
73
78
|
'DisclosureSection',
|
|
74
79
|
'EmptyState',
|
|
80
|
+
'FilterBar',
|
|
81
|
+
'List',
|
|
82
|
+
'ListRow',
|
|
83
|
+
'ListSection',
|
|
75
84
|
'InspectorField',
|
|
76
85
|
'Notice',
|
|
77
86
|
'Panel',
|
|
@@ -96,6 +96,9 @@ const scopedComponentFiles = [
|
|
|
96
96
|
join(srcDir, 'patterns', 'empty-state', 'EmptyState.tsx'),
|
|
97
97
|
join(srcDir, 'patterns', 'form-field', 'FormField.tsx'),
|
|
98
98
|
join(srcDir, 'patterns', 'inspector-field', 'InspectorField.tsx'),
|
|
99
|
+
join(srcDir, 'patterns', 'list', 'List.tsx'),
|
|
100
|
+
join(srcDir, 'patterns', 'list', 'ListRow.tsx'),
|
|
101
|
+
join(srcDir, 'patterns', 'list', 'ListSection.tsx'),
|
|
99
102
|
join(srcDir, 'patterns', 'notice', 'Notice.tsx'),
|
|
100
103
|
join(srcDir, 'patterns', 'panel', 'Panel.tsx'),
|
|
101
104
|
join(srcDir, 'patterns', 'responsive-panel', 'ResponsivePanel.tsx'),
|
|
@@ -154,6 +157,7 @@ const scopedPropTypeFiles = [
|
|
|
154
157
|
join(srcDir, 'patterns', 'empty-state', 'types.ts'),
|
|
155
158
|
join(srcDir, 'patterns', 'form-field', 'types.ts'),
|
|
156
159
|
join(srcDir, 'patterns', 'inspector-field', 'types.ts'),
|
|
160
|
+
join(srcDir, 'patterns', 'list', 'types.ts'),
|
|
157
161
|
join(srcDir, 'patterns', 'notice', 'types.ts'),
|
|
158
162
|
join(srcDir, 'patterns', 'panel', 'types.ts'),
|
|
159
163
|
join(srcDir, 'patterns', 'responsive-panel', 'types.ts'),
|