Skip to content

Commit

Permalink
Merge pull request #850 from contiamo/feature/contact
Browse files Browse the repository at this point in the history
**Feature:** Add `Contact` component to for user metadata
  • Loading branch information
Tejas Kumar authored Dec 5, 2018
2 parents b5ce9cd + f0e6aff commit 2fee773
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 16 deletions.
40 changes: 40 additions & 0 deletions src/Contact/Contact.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from "react"

import styled from "../utils/styled"

export interface ContactProps {
name: string
meta?: string
}

const Container = styled("div")`
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
overflow: auto hidden;
`

const Heading = styled("h6")<{ hasMeta: boolean }>`
margin: 0 0 ${({ theme, hasMeta }) => (hasMeta ? theme.space.base : 0)}px 0;
font-size: ${({ theme }) => theme.font.size.small}px;
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${({ theme }) => theme.color.text.dark};
line-height: 1;
`

const Meta = styled("p")`
margin: 0;
font-size: ${({ theme }) => theme.font.size.fineprint}px;
color: ${({ theme }) => theme.color.text.lightest};
line-height: 1;
`

const Contact: React.SFC<ContactProps> = ({ name, meta, ...props }) => (
<Container {...props}>
<Heading hasMeta={Boolean(meta)}>{name}</Heading>
<Meta>{meta}</Meta>
</Container>
)

export default Contact
25 changes: 25 additions & 0 deletions src/Contact/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Sometimes, we'd like to show someone's name and email address. This component lets us show a contact in a consistent way.

## Basic Usage

```jsx
<>
<Contact name="Luke Cage" meta="harlems.hero@gmail.com" />
<br />
<Contact name="Danny Rand" />
<br />
<Contact name="Matt Murdock" meta="+1 173 712 9124" />
</>
```

## With Avatar

In some cases, this component will be required to pair with an `Avatar`. Consider,

```jsx
initialState = { name: "Kenye Wheelest", email: "kweezy@notformidablelabs.com" }
;<div style={{ display: "flex" }}>
<Avatar style={{ marginRight: 8 }} name={state.name} />
<Contact name={state.name} meta={state.email} />
</div>
```
40 changes: 27 additions & 13 deletions src/ContextMenu/ContextMenu.Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export interface Props {
}

export interface IContextMenuItem<TValue = any> {
label: string
label: string | React.ReactElement<any>
description?: string
icon?: IconProps["name"]
icon?: IconProps["name"] | React.ReactElement<any>
iconColor?: keyof OperationalStyleConstants["color"]
onClick?: ContextMenuProps["onClick"]
value?: TValue
Expand Down Expand Up @@ -106,21 +106,35 @@ const Content: React.SFC<{ value: StringOrItem }> = ({ value }) => {
)
}

const InPlaceIcon = (props: Props) =>
typeof props.item !== "string" ? (
<ContextMenuIcon
iconlocation_={props.iconLocation}
color={props.item.iconColor}
left={props.iconLocation === "left" || !props.iconLocation}
name={props.item.icon as IconName}
/>
) : null
const ContextMenuItemIcon: React.SFC<Pick<Props, "item" | "iconLocation">> = props => {
// If item is just a string,
if (typeof props.item === "string") {
return <></>
}

// If it's an object with an icon property
if (typeof props.item.icon === "string") {
return (
<ContextMenuIcon
iconlocation_={props.iconLocation}
color={props.item.iconColor}
left={props.iconLocation === "left" || !props.iconLocation}
name={props.item.icon as IconName}
/>
)
}

// If it's an object with a React Element as a property
return <>{props.item.icon}</>
}

const ContextMenuItem: React.SFC<Props> = props => (
<Container {...props} condensed={props.condensed}>
{(!props.iconLocation || props.iconLocation === "left") && <InPlaceIcon {...props} />}
{(!props.iconLocation || props.iconLocation === "left") && (
<ContextMenuItemIcon iconLocation={props.iconLocation} item={props.item} />
)}
<Content value={props.item} />
{props.iconLocation === "right" && <InPlaceIcon {...props} />}
{props.iconLocation === "right" && <ContextMenuItemIcon iconLocation={props.iconLocation} item={props.item} />}
</Container>
)

Expand Down
58 changes: 58 additions & 0 deletions src/ContextMenu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,64 @@ const menuItems = ["Menu 1", "Menu 2", "Menu 3"]
</ContextMenu>
```

#### Usage with React Nodes as Labels

In some cases, you might want your `label` to be a little bit more clever than just a string. This example shows a `ContextMenu` with a JSX element as its `label`.

```jsx
/* Anything can be a label now, even some DIV you style yourself */
const MyLabelContainer = ({ children, style }) => (
<div style={{ marginRight: 8, padding: "8px 0", ...style }}>{children}</div>
)

const menuItems = [
{
label: (
<MyLabelContainer>
<Contact name="Tejas Kumar" meta="youare@cool.com" />
</MyLabelContainer>
),
icon: "Add",
},
{
label: (
<MyLabelContainer>
<Contact name="Peter Szerzo" meta="peter@norway.com" />
</MyLabelContainer>
),
icon: (
<div style={{ marginLeft: "auto" }}>
<Hint right>User already exists</Hint>
</div>
),
},
{
label: (
<MyLabelContainer>
<Contact name="Sibelius Seraphini" meta="sibelius@seraphini.com" />
</MyLabelContainer>
),
icon: "Add",
},
{
label: (
<MyLabelContainer>
<Contact
name={`Arnold "Governator" Schwarzennegger`}
meta="arnoldgovernatorschwarzennegger@thegovernmentofcalifornia.usa🇺🇸"
/>
</MyLabelContainer>
),
icon: "Add",
},
]
;<>
<ContextMenu iconLocation="right" items={menuItems} onClick={item => alert(`clicked`)}>
<Button>See Users</Button>
</ContextMenu>
</>
```

#### Large number of items

The context menu doesn't grow past a certain maximum height, but scrolls in its container instead.
Expand Down
27 changes: 25 additions & 2 deletions src/Hint/Hint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,46 @@ export interface HintProps extends DefaultProps {
* Indicates that this component is right of other content, and adds an appropriate left margin.
*/
right?: boolean
tooltipPosition?: "left" | "top" | "right" | "bottom" | "smart"
}

const Container = styled("div")<{ left?: HintProps["left"]; right?: HintProps["right"] }>(({ left, right, theme }) => ({
position: "relative",
display: "inline-block",
display: "inline-flex",
verticalAlign: "middle",
alignItems: "center",
color: theme.color.text.lightest,
marginRight: left ? theme.space.base : 0,
marginLeft: right ? theme.space.base : 0,
...hoverTooltip,
}))

const HintTooltip: React.SFC<{ position: HintProps["tooltipPosition"] }> = props => {
switch (props.position) {
case "right":
return <Tooltip right {...props} />
case "top":
return <Tooltip top {...props} />
case "bottom":
return <Tooltip bottom {...props} />
case "left":
return <Tooltip left {...props} />
case "smart":
return <Tooltip smart {...props} />
default:
return null
}
}

const Hint: React.SFC<HintProps> = props => (
<Container {...props}>
<Icon name="Question" size={12} />
<Tooltip right>{props.children}</Tooltip>
<HintTooltip position={props.tooltipPosition!}>{props.children}</HintTooltip>
</Container>
)

Hint.defaultProps = {
tooltipPosition: "left",
}

export default Hint
2 changes: 1 addition & 1 deletion src/TopbarSelect/TopbarSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface TopbarSelectProps {
/** Menu items, conforming to the ContextMenu API */
items: ContextMenuProps["items"]
/** Change handler */
onChange?: (newLabel: string) => void
onChange?: (newLabel: string | React.ReactElement<any>) => void
}

const TopbarSelectContainer = styled("div")`
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export { default as CardSection, CardSectionProps, DragAndDropFeedback } from ".
export { default as Checkbox, CheckboxProps } from "./Checkbox/Checkbox"
export { default as Chip, ChipProps } from "./Chip/Chip"
export { default as Code, CodeProps } from "./Code/Code"
export { default as Contact, ContactProps } from "./Contact/Contact"
export { default as ContextMenu, ContextMenuProps } from "./ContextMenu/ContextMenu"
export { default as DatePicker, DatePickerProps } from "./DatePicker/DatePicker"
export { default as Debug } from "./Debug/Debug"
Expand Down
7 changes: 7 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,19 @@ const separatorColors = {
* A specialized color palette for typography.
*/
const textColors = {
/** `#333` */
dark: "#333",
/** `#545454` */
default: "#545454",
/** `#c0c0c0` */
disabled: "#c0c0c0",
/** `#666` */
light: "#666",
/** `#747474` */
lighter: "#747474",
/** `#909090` */
lightest: "#909090",
/** primary color */
action: primaryColor,
white: whiteColor,
}
Expand Down

0 comments on commit 2fee773

Please sign in to comment.