Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/add-a-feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [Propose feature](#propose-feature)
- [Prototype](#prototype)
- [Spec out the API](#spec-out-the-api)
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Simplify rendering when tooltip is not visible @jurokapsiar ([#1981](https://github.com/stardust-ui/react/pull/1981))
- Add `thumbtack`, `thumbtack-slash` and `question-circle` icons to Teams theme @codepretty ([#2000](https://github.com/stardust-ui/react/pull/2000))

### Documentation
- Copy to clipboard prototype - attached confirmation @jurokapsiar ([#1900](https://github.com/stardust-ui/react/pull/1900))


<!--------------------------------[ v0.39.0 ]------------------------------- -->
## [v0.39.0](https://github.com/stardust-ui/react/tree/v0.39.0) (2019-09-23)
[Compare changes](https://github.com/stardust-ui/react/compare/v0.38.1...v0.39.0)
Expand Down
70 changes: 25 additions & 45 deletions docs/src/prototypes/CopyToClipboard/CopyToClipboard.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,25 @@
import { Text, Tooltip, ShorthandValue, TextProps, TooltipProps } from '@stardust-ui/react'
import { ShorthandValue, Tooltip, TooltipProps } from '@stardust-ui/react'
import * as copyToClipboard from 'copy-to-clipboard'
import * as _ from 'lodash'
import * as React from 'react'

import { NotificationContext } from './NotificationProvider'
import { Notification, NotificationContext } from './NotificationProvider'

export type CopyToClipboardProps = {
tooltip?: ShorthandValue<TooltipProps>
attached?: boolean
pointing?: boolean
target?: HTMLElement
notification?: React.ReactNode
timeout?: number
value: string

noticeText?: ShorthandValue<TextProps>
promptText?: ShorthandValue<TextProps>

align?: TooltipProps['align']
position?: TooltipProps['position']

trigger: JSX.Element
}

const CopyToClipboard: React.FC<CopyToClipboardProps> = props => {
const {
align,
attached,
noticeText,
pointing,
position,
promptText,
timeout,
trigger,
value,
} = props
const { value, trigger, tooltip, attached, notification, timeout, target } = props

const setNotification = React.useContext(NotificationContext)
const [copied, setCopied] = React.useState<boolean>(false)
const [promptOpen, setPromptOpen] = React.useState<boolean>(false)
const timeoutId = React.useRef<number>()

React.useEffect(() => {
Expand All @@ -50,7 +34,7 @@ const CopyToClipboard: React.FC<CopyToClipboardProps> = props => {
(e: React.MouseEvent, ...args) => {
setCopied(true)
if (!attached) {
setNotification(Text.create(noticeText), timeout)
setNotification(notification, target, timeout)
}

copyToClipboard(value)
Expand All @@ -59,31 +43,27 @@ const CopyToClipboard: React.FC<CopyToClipboardProps> = props => {
[value],
)

const tooltipContent = copied
? { content: Text.create(noticeText), variables: { primary: true } }
: {
content: Text.create(promptText),
variables: { basic: true },
}
const tooltipOpen = (promptOpen && !copied) || (copied && attached)
return (
<Tooltip
align={align}
content={tooltipContent}
pointing={pointing}
position={position}
onOpenChange={(e, data) => setPromptOpen(data.open)}
open={tooltipOpen}
trigger={React.cloneElement(trigger, { onClick: handleTriggerClick })}
/>
)
const renderedTrigger = React.cloneElement(trigger, { onClick: handleTriggerClick })

if (copied && attached) {
return <Notification trigger={renderedTrigger} content={notification} />
}

if (copied || !tooltip) {
return renderedTrigger
}

return Tooltip.create(tooltip, {
overrideProps: {
trigger: renderedTrigger,
children: undefined, // force-reset `children` defined for `Tooltip` as it collides with the `trigger
},
})
}

CopyToClipboard.defaultProps = {
align: 'center',
noticeText: 'Copied to clipboard',
position: 'below',
promptText: 'Click to copy',
notification: 'Copied to clipboard',
tooltip: 'Click to copy',
timeout: 4000,
}

Expand Down
51 changes: 38 additions & 13 deletions docs/src/prototypes/CopyToClipboard/NotificationProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import { Portal, createComponent } from '@stardust-ui/react'
import { Portal, Tooltip, createComponent, TooltipProps } from '@stardust-ui/react'
import * as React from 'react'

type NotificationProps = {
children?: React.ReactNode
content: React.ReactNode
target?: HTMLElement
trigger?: JSX.Element
}

type NotificationContextValue = (value: React.ReactNode, timeout: number) => void
type NotificationContextValue = (
content: React.ReactNode,
target: HTMLElement | null,
timeout: number,
) => void

export const NotificationContext = React.createContext<NotificationContextValue>(() => {
throw new Error('No matching NotificationContext.Provider')
})

export const NotificationProvider: React.FC = props => {
const { children } = props
const [notification, setNotification] = React.useState<React.ReactNode>()
const [target, setTarget] = React.useState<HTMLElement | null>()
const timeoutId = React.useRef<number>()

const update = React.useCallback<NotificationContextValue>((value, timeout) => {
setNotification(value)
const update = React.useCallback<NotificationContextValue>((notification, target, timeout) => {
setNotification(notification)
setTarget(target)
timeoutId.current = window.setTimeout(() => {
setNotification(null)
setTarget(null)
}, timeout)
}, [])

Expand All @@ -28,23 +38,38 @@ export const NotificationProvider: React.FC = props => {

return (
<>
<Portal open={!!notification}>
<Notification>{notification}</Notification>
</Portal>
{!!notification && <Notification target={target} content={notification} />}
<NotificationContext.Provider value={update}>{children}</NotificationContext.Provider>
</>
)
}

export const Notification = createComponent<NotificationProps>({
displayName: 'Notification',
render: ({ children, stardust: { classes } }) => {
render: ({ target, trigger, content, stardust: { classes } }) => {
const tooltipProps: TooltipProps = {
content,
open: true,
pointing: false,
target,
trigger,
}

if (target || trigger) {
return Tooltip.create({ ...tooltipProps, offset: '0 10' })
}

return (
<div className={classes.root}>
<div className={classes.overlay}>
<div className={classes.content}>{children}</div>
<Portal open>
<div className={classes.root}>
<div className={classes.overlay}>
{Tooltip.create({
...tooltipProps,
trigger: <div className={classes.content} />,
})}
</div>
</div>
</div>
</Portal>
)
},
})
77 changes: 54 additions & 23 deletions docs/src/prototypes/CopyToClipboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as React from 'react'
import { Flex, Provider, Text, Button, Menu } from '@stardust-ui/react'
import { Flex, Provider, Text, Button, Menu, Ref } from '@stardust-ui/react'
import CopyToClipboard from './CopyToClipboard'
import { PrototypeSection, ComponentPrototype } from '../Prototypes'
import themeOverrides from './themeOverrides'
import { NotificationProvider } from './NotificationProvider'

type CopyToClipboardPrototypeProps = {
value: string
target?: HTMLElement
attached?: boolean
}

Expand All @@ -18,7 +19,7 @@ const CopyToClipboardPrototype: React.FC<CopyToClipboardPrototypeProps> = props

<CopyToClipboard
attached={props.attached}
pointing
target={props.target}
value={props.value}
trigger={<Button iconOnly icon="clipboard-copied-to" />}
/>
Expand All @@ -27,27 +28,51 @@ const CopyToClipboardPrototype: React.FC<CopyToClipboardPrototypeProps> = props
}

const CopyToClipboardInMenu: React.FC = props => {
const item = {
key: 'edit',
content: 'Edit',
menu: [
'Open File...',
'Save File...',
render =>
render('Copy text', (Component, props) => {
return <CopyToClipboard value={'Julius Caesar'} trigger={<Component {...props} />} />
}),
],
}

return <Menu items={[item]} />
}

const CopyToClipboardAttached: React.FC = props => {
const [target, setTarget] = React.useState<HTMLElement>(null)

const item = {
key: 'edit',
content: 'Edit',
menu: [
'Open File...',
'Save File...',
render =>
render('Copy text', (Component, props) => {
return (
<CopyToClipboard
target={target}
value="Julius Caesar"
trigger={<Component {...props} />}
/>
)
}),
],
}

const items = [
{
key: 'edit',
content: 'Edit',
menu: [
'Open File...',
'Save File...',
render =>
render('Copy text', (Component, props) => {
return (
<CopyToClipboard
position="after"
align="bottom"
pointing
value={'Julius Caesar'}
trigger={<Component {...props} />}
/>
)
}),
],
},
render =>
render(item, (Component, props) => (
<Ref innerRef={setTarget}>
<Component {...props} />
</Ref>
)),
]
return <Menu items={items} />
}
Expand All @@ -66,7 +91,7 @@ const CopyToClipboardPrototypes: React.FC = () => {
title="Attached"
description="Attached version of Copy to Clipboard prototype"
>
<CopyToClipboardPrototype attached value={commitID} />
<CopyToClipboardPrototype attached={true} value={commitID} />
</ComponentPrototype>
<ComponentPrototype
title="Not Attached"
Expand All @@ -80,6 +105,12 @@ const CopyToClipboardPrototypes: React.FC = () => {
>
<CopyToClipboardInMenu />
</ComponentPrototype>
<ComponentPrototype
title="In Menu Attached"
description="Copy to Clipboard can be attached to a different element"
>
<CopyToClipboardAttached />
</ComponentPrototype>
</NotificationProvider>
</Provider>
</PrototypeSection>
Expand Down
Loading