-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Nemobot
committed
Feb 17, 2022
1 parent
c98d76f
commit c7edc5e
Showing
11 changed files
with
499 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export type IllustrationProps = { | ||
size: 32 | 40 | 80 | 160; | ||
} & ( | ||
| { | ||
style: "color"; | ||
} | ||
| { | ||
style: "outline"; | ||
color: "default" | "disabled"; | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { SVGAttributes } from "react"; | ||
import { vars } from "../vars.css"; | ||
import { IllustrationProps } from "./IllustrationProps"; | ||
|
||
export function svgIllustrationProps(props: IllustrationProps): SVGAttributes<SVGElement> { | ||
return { | ||
...sizeToDimensions(props.size), | ||
fill: props.style === "outline" ? outlineColor(props.color) : undefined, | ||
viewBox: "0 0 80 80", | ||
}; | ||
} | ||
|
||
function outlineColor(color: (IllustrationProps & { style: "outline" })["color"]): string { | ||
switch (color) { | ||
case "default": | ||
return vars.foregroundColor.foregroundSecondary; | ||
case "disabled": | ||
return vars.foregroundColor.foregroundDisabled; | ||
} | ||
} | ||
|
||
function sizeToDimensions(size: IllustrationProps["size"]): { width: number; height: number } { | ||
switch (size) { | ||
case 32: | ||
return { width: 32, height: 32 }; | ||
case 40: | ||
return { width: 40, height: 40 }; | ||
case 80: | ||
return { width: 80, height: 80 }; | ||
case 160: | ||
return { width: 160, height: 160 }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { bentoSprinkles } from "../internal"; | ||
import { strictRecipe } from "../util/strictRecipe"; | ||
|
||
export const listItemRecipe = strictRecipe({ | ||
variants: { | ||
interactive: { | ||
true: bentoSprinkles({ | ||
cursor: { default: "pointer", disabled: "notAllowed" }, | ||
background: { | ||
hover: "primaryTransparentHoverBackground", | ||
focus: "primaryTransparentFocusBackground", | ||
}, | ||
}), | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Omit } from "../util/Omit"; | ||
import { BentoSprinkles, Stack, Inset } from "../internal"; | ||
import { createListItem, ListItemConfig, ListItemProps } from "./createListItem"; | ||
|
||
export type ListSize = "medium" | "large"; | ||
|
||
type Props = { | ||
size: ListSize; | ||
items: Omit<ListItemProps, "size">[]; | ||
dividers?: boolean; | ||
}; | ||
|
||
export type ListConfig = { | ||
paddingY: BentoSprinkles["paddingY"]; | ||
item: ListItemConfig; | ||
}; | ||
|
||
export function createList( | ||
config: ListConfig = { | ||
paddingY: 8, | ||
item: { | ||
paddingX: 16, | ||
paddingY: { | ||
medium: 8, | ||
large: 16, | ||
}, | ||
fontSize: { | ||
firstLine: "medium", | ||
secondLine: "small", | ||
overline: "small", | ||
}, | ||
internalSpacing: 16, | ||
iconSize: { | ||
leading: 24, | ||
trailing: 16, | ||
illustration: 32, | ||
}, | ||
}, | ||
} | ||
) { | ||
const ListItem = createListItem(config.item); | ||
return function List({ size, items, dividers }: Props) { | ||
return ( | ||
<Inset spaceY={config.paddingY}> | ||
<Stack space={0} as="ul" dividers={dividers}> | ||
{items.map((liProps) => ( | ||
<ListItem key={liProps.label} {...liProps} size={size} /> | ||
))} | ||
</Stack> | ||
</Inset> | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import { useLink } from "@react-aria/link"; | ||
import { ComponentProps, useRef } from "react"; | ||
import { Body, Label, LocalizedString, useLinkComponent } from ".."; | ||
import { Box, Columns, Column, Inset, Stack, BentoSprinkles } from "../internal"; | ||
import { IconProps } from "../Icons/IconProps"; | ||
import { IllustrationProps } from "../Illustrations/IllustrationProps"; | ||
import { listItemRecipe } from "./ListItem.css"; | ||
import { ListSize } from "./createList"; | ||
|
||
type Kind = | ||
| { | ||
kind: "overline"; | ||
overline: LocalizedString; | ||
label: LocalizedString; | ||
} | ||
| { | ||
kind: "single-line"; | ||
label: LocalizedString; | ||
} | ||
| { | ||
kind: "two-line"; | ||
label: LocalizedString; | ||
secondLine: LocalizedString; | ||
}; | ||
|
||
type LeftItem = | ||
| { | ||
icon?: never; | ||
illustration?: never; | ||
} | ||
| { | ||
icon: (props: IconProps) => JSX.Element; | ||
illustration?: never; | ||
} | ||
| { | ||
icon?: never; | ||
illustration: (props: IllustrationProps) => JSX.Element; | ||
}; | ||
|
||
type RightItem = { | ||
trailingIcon?: (props: IconProps) => JSX.Element; | ||
}; | ||
|
||
export type Props = Kind & | ||
LeftItem & | ||
RightItem & { | ||
disabled?: boolean; | ||
size: ListSize; | ||
} & ( | ||
| { | ||
onPress?: () => void; | ||
href?: never; | ||
} | ||
| { | ||
href?: string; | ||
onPress?: never; | ||
} | ||
); | ||
|
||
export type { Props as ListItemProps }; | ||
|
||
type ListItemSizeConfig<T> = { | ||
[k in ListSize]: T; | ||
}; | ||
export type ListItemConfig = { | ||
paddingX: BentoSprinkles["paddingX"]; | ||
paddingY: ListItemSizeConfig<BentoSprinkles["paddingY"]>; | ||
fontSize: { | ||
firstLine: ComponentProps<typeof Body>["size"]; | ||
secondLine: ComponentProps<typeof Body>["size"]; | ||
overline: ComponentProps<typeof Label>["size"]; | ||
}; | ||
internalSpacing: BentoSprinkles["gap"]; | ||
iconSize: { | ||
leading: IconProps["size"]; | ||
trailing: IconProps["size"]; | ||
illustration: IllustrationProps["size"]; | ||
}; | ||
}; | ||
|
||
export function createListItem(config: ListItemConfig) { | ||
return function ListItem(props: Props) { | ||
const linkRef = useRef<HTMLElement>(null); | ||
|
||
const { | ||
linkProps: { color, ...linkProps }, | ||
} = useLink( | ||
{ | ||
onPress: props.onPress, | ||
isDisabled: props.disabled, | ||
elementType: props.href ? "a" : "div", | ||
}, | ||
linkRef | ||
); | ||
|
||
const LinkComponent = useLinkComponent(); | ||
|
||
return ( | ||
<Box | ||
as="li" | ||
className={listItemRecipe({ interactive: !!props.onPress || !!props.href })} | ||
disabled={props.disabled} | ||
> | ||
<Box | ||
ref={linkRef} | ||
as={props.href ? LinkComponent : "div"} | ||
{...linkProps} | ||
href={props.href} | ||
display="block" | ||
> | ||
<Inset spaceX={config.paddingX} spaceY={config.paddingY[props.size]}> | ||
<Columns space={config.internalSpacing} alignY="center"> | ||
{renderLeft(props)} | ||
{renderContent(props)} | ||
{renderRight(props)} | ||
</Columns> | ||
</Inset> | ||
</Box> | ||
</Box> | ||
); | ||
}; | ||
|
||
function renderLeft(props: Props) { | ||
if (props.illustration) { | ||
return ( | ||
<Column width="content"> | ||
{props.illustration({ | ||
size: config.iconSize.illustration, | ||
style: "outline", | ||
color: props.disabled ? "disabled" : "default", | ||
})} | ||
</Column> | ||
); | ||
} | ||
|
||
if (props.icon) { | ||
return ( | ||
<Column width="content"> | ||
{props.icon({ | ||
size: config.iconSize.leading, | ||
color: props.disabled ? "disabled" : "default", | ||
})} | ||
</Column> | ||
); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
function renderRight(props: Props) { | ||
if (props.trailingIcon) { | ||
return ( | ||
<Column width="content"> | ||
{props.trailingIcon({ | ||
size: config.iconSize.trailing, | ||
color: props.disabled ? "disabled" : "default", | ||
})} | ||
</Column> | ||
); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
function renderContent(props: Props) { | ||
switch (props.kind) { | ||
case "single-line": | ||
return <SingleLine {...props} />; | ||
case "two-line": | ||
return <TwoLine {...props} />; | ||
case "overline": | ||
return <Overline {...props} />; | ||
} | ||
} | ||
|
||
function SingleLine(props: Props & { kind: "single-line" }) { | ||
return ( | ||
<Body size={config.fontSize.firstLine} color={props.disabled ? "disabled" : "default"}> | ||
{props.label} | ||
</Body> | ||
); | ||
} | ||
|
||
function TwoLine(props: Props & { kind: "two-line" }) { | ||
return ( | ||
<Stack space={4} align="left"> | ||
<Body size={config.fontSize.firstLine} color={props.disabled ? "disabled" : "default"}> | ||
{props.label} | ||
</Body> | ||
<Body size={config.fontSize.secondLine} color={props.disabled ? "disabled" : "secondary"}> | ||
{props.secondLine} | ||
</Body> | ||
</Stack> | ||
); | ||
} | ||
|
||
function Overline(props: Props & { kind: "overline" }) { | ||
return ( | ||
<Stack space={4} align="left"> | ||
<Label | ||
size={config.fontSize.overline} | ||
color={props.disabled ? "disabled" : "secondary"} | ||
uppercase | ||
> | ||
{props.overline} | ||
</Label> | ||
<Body size={config.fontSize.firstLine} color={props.disabled ? "disabled" : "default"}> | ||
{props.label} | ||
</Body> | ||
</Stack> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.