-
-
Notifications
You must be signed in to change notification settings - Fork 427
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(components): add Clipboard (#1413)
* feat(components): add Clipboard * PR Build error fixed * docs typo mistake * docs typo mistake * added missing use client in example * export naming fix * small change * Example updated * moved copyToClipboard function to helpers for the reusability (DRY) * error handling added to helper function
- Loading branch information
1 parent
c8dba76
commit 26401bc
Showing
18 changed files
with
531 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"flowbite-react": minor | ||
--- | ||
|
||
feat(components): add "Clipboard" |
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,42 @@ | ||
--- | ||
title: React Clipboard - Flowbite | ||
description: Use the clipboard component to copy text, data or lines of code to the clipboard with a single click based on various styles and examples coded with Tailwind CSS and Flowbite | ||
--- | ||
|
||
The copy to clipboard component allows you to copy text, lines of code, contact details or any other data to the clipboard with a single click on a trigger element such as a button. This component can be used to copy text from an input field, textarea, code block or even address fields in a form element. | ||
|
||
These components are built with Tailwind CSS and Flowbite React and can be found on the internet on websites such as Bitly, Cloudflare, Amazon AWS and almost all open-source projects and documentations. | ||
|
||
Import the component from `flowbite-react` to use the clipboard element: | ||
|
||
```jsx | ||
import { Clipboard } from "flowbite-react"; | ||
``` | ||
|
||
## Default copy to clipboard | ||
|
||
Use this example to copy the content of an input text field by clicking on a button and update the button text. | ||
|
||
<Example name="clipboard.root" /> | ||
|
||
## Input with copy button | ||
|
||
This example can be used to copy the content of an input field by clicking on a button with an icon positioned inside the form element and also show a tooltip with a message when the text has been copied. | ||
|
||
<Example name="clipboard.withIcon" /> | ||
|
||
## Copy button with text | ||
|
||
Use this example to show a copy button inside the input field with a text label and icon that updates to a success state when the text has been copied. | ||
|
||
<Example name="clipboard.withIconText" /> | ||
|
||
## Theme | ||
|
||
To learn more about how to customize the appearance of components, please see the [Theme docs](/docs/customize/theme). | ||
|
||
<Theme name="clipboard" /> | ||
|
||
## References | ||
|
||
- [Flowbite Datepicker](https://flowbite.com/docs/components/clipboard/) |
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,59 @@ | ||
"use client"; | ||
|
||
import { Clipboard } from "flowbite-react"; | ||
import type { CodeData } from "~/components/code-demo"; | ||
|
||
const code = ` | ||
"use client"; | ||
import { Clipboard } from "flowbite-react" | ||
export function Component() { | ||
return ( | ||
<div className="grid w-full max-w-[23rem] grid-cols-8 gap-2"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input id="npm-install" type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard valueToCopy="npm install flowbite-react" label="Copy" /> | ||
</div> | ||
) | ||
} | ||
`; | ||
|
||
export function Component() { | ||
return ( | ||
<div className="grid w-full max-w-[23rem] grid-cols-8 gap-2"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard valueToCopy="npm install flowbite-react" label="Copy" /> | ||
</div> | ||
); | ||
} | ||
|
||
export const root: CodeData = { | ||
type: "single", | ||
code: [ | ||
{ | ||
fileName: "client", | ||
language: "tsx", | ||
code, | ||
}, | ||
], | ||
githubSlug: "clipboard/clipboard.root.tsx", | ||
component: <Component />, | ||
}; |
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,65 @@ | ||
"use client"; | ||
|
||
import { Clipboard } from "flowbite-react"; | ||
import type { CodeData } from "~/components/code-demo"; | ||
|
||
const code = ` | ||
"use client"; | ||
import { Clipboard } from "flowbite-react" | ||
export function Component() { | ||
return ( | ||
<div className="grid w-full max-w-64"> | ||
<div className="relative"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard.WithIcon valueToCopy="npm install flowbite-react" /> | ||
</div> | ||
</div> | ||
) | ||
} | ||
`; | ||
|
||
export function Component() { | ||
return ( | ||
<div className="grid w-full max-w-64"> | ||
<div className="relative"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard.WithIcon valueToCopy="npm install flowbite-react" /> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export const withIcon: CodeData = { | ||
type: "single", | ||
code: [ | ||
{ | ||
fileName: "client", | ||
language: "tsx", | ||
code, | ||
}, | ||
], | ||
githubSlug: "clipboard/clipboard.withIcon.tsx", | ||
component: <Component />, | ||
}; |
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,65 @@ | ||
"use client"; | ||
|
||
import { Clipboard } from "flowbite-react"; | ||
import type { CodeData } from "~/components/code-demo"; | ||
|
||
const code = ` | ||
"use client"; | ||
import { Clipboard } from "flowbite-react" | ||
export function Component() { | ||
return ( | ||
<div className="grid w-full max-w-80"> | ||
<div className="relative"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 px-2.5 py-4 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard.WithIconText valueToCopy="npm install flowbite-react" /> | ||
</div> | ||
</div> | ||
) | ||
} | ||
`; | ||
|
||
export function Component() { | ||
return ( | ||
<div className="grid w-full max-w-80"> | ||
<div className="relative"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 px-2.5 py-4 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard.WithIconText valueToCopy="npm install flowbite-react" /> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export const withIconText: CodeData = { | ||
type: "single", | ||
code: [ | ||
{ | ||
fileName: "client", | ||
language: "tsx", | ||
code, | ||
}, | ||
], | ||
githubSlug: "clipboard/clipboard.withIconText.tsx", | ||
component: <Component />, | ||
}; |
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,3 @@ | ||
export { root } from "./clipboard.root"; | ||
export { withIcon } from "./clipboard.withIcon"; | ||
export { withIconText } from "./clipboard.withIconText"; |
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
72 changes: 72 additions & 0 deletions
72
packages/ui/src/components/Clipboard/Clipboard.stories.tsx
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,72 @@ | ||
import type { Meta, StoryFn } from "@storybook/react"; | ||
// import { FaClipboardList } from "react-icons/fa6"; | ||
import type { ClipboardProps } from "./Clipboard"; | ||
import { Clipboard } from "./Clipboard"; | ||
import type { ClipboardWithIconProps } from "./ClipboardWithIcon"; | ||
import type { ClipboardWithIconTextProps } from "./ClipboardWithIconText"; | ||
|
||
export default { | ||
title: "Components/Clipboard", | ||
component: Clipboard, | ||
} as Meta; | ||
|
||
const DefaultTemplate: StoryFn<ClipboardProps> = () => ( | ||
<div className="grid w-full max-w-[23rem] grid-cols-8 gap-2"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard valueToCopy="npm install flowbite-react" label="Copy" /> | ||
</div> | ||
); | ||
|
||
export const Default = DefaultTemplate.bind({}); | ||
|
||
const CopyIconTemplate: StoryFn<ClipboardWithIconProps> = () => ( | ||
<div className="grid w-full max-w-64"> | ||
<div className="relative"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard.WithIcon valueToCopy="npm install flowbite-react" /> | ||
</div> | ||
</div> | ||
); | ||
|
||
export const CopyIcon = CopyIconTemplate.bind({}); | ||
|
||
const CopyIconTextTemplate: StoryFn<ClipboardWithIconTextProps> = () => ( | ||
<div className="grid w-full max-w-80"> | ||
<div className="relative"> | ||
<label htmlFor="npm-install" className="sr-only"> | ||
Label | ||
</label> | ||
<input | ||
id="npm-install" | ||
type="text" | ||
className="col-span-6 block w-full rounded-lg border border-gray-300 bg-gray-50 px-2.5 py-4 text-sm text-gray-500 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:placeholder:text-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" | ||
value="npm install flowbite-react" | ||
disabled | ||
readOnly | ||
/> | ||
<Clipboard.WithIconText valueToCopy="npm install flowbite-react" /> | ||
</div> | ||
</div> | ||
); | ||
|
||
export const CopyIconText = CopyIconTextTemplate.bind({}); |
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,58 @@ | ||
"use client"; | ||
|
||
import { forwardRef, useState, type ComponentProps, type ReactNode } from "react"; | ||
import { twMerge } from "tailwind-merge"; | ||
import { mergeDeep } from "../../helpers/merge-deep"; | ||
import { getTheme } from "../../theme-store"; | ||
import type { DeepPartial } from "../../types"; | ||
import { Tooltip } from "../Tooltip"; | ||
import { ClipboardWithIcon } from "./ClipboardWithIcon"; | ||
import type { FlowbiteClipboardWithIconTheme } from "./ClipboardWithIcon"; | ||
import { ClipboardWithIconText } from "./ClipboardWithIconText"; | ||
import type { FlowbiteClipboardWithIconTextTheme } from "./ClipboardWithIconText"; | ||
import { copyToClipboard } from "./helpers"; | ||
|
||
export interface FlowbiteClipboardTheme { | ||
button: { | ||
base: string; | ||
label: string; | ||
}; | ||
withIcon: FlowbiteClipboardWithIconTheme; | ||
withIconText: FlowbiteClipboardWithIconTextTheme; | ||
} | ||
|
||
export interface ClipboardProps extends ComponentProps<"button"> { | ||
valueToCopy: string; | ||
label?: ReactNode; | ||
theme?: DeepPartial<FlowbiteClipboardTheme>; | ||
} | ||
|
||
const ClipboardComponent = forwardRef<HTMLButtonElement, ClipboardProps>( | ||
({ className, valueToCopy, label, theme: customTheme = {}, ...rest }, ref) => { | ||
const [isJustCopied, setIsJustCopied] = useState(false); | ||
|
||
const theme = mergeDeep(getTheme().clipboard.button, customTheme); | ||
|
||
return ( | ||
<Tooltip content={isJustCopied ? "Copied" : "Copy to clipboard"} className="[&_*]:cursor-pointer"> | ||
<button | ||
className={twMerge(theme.base, className)} | ||
onClick={() => copyToClipboard(valueToCopy, setIsJustCopied)} | ||
{...rest} | ||
ref={ref} | ||
> | ||
<span className={theme.label}>{label}</span> | ||
</button> | ||
</Tooltip> | ||
); | ||
}, | ||
); | ||
|
||
ClipboardComponent.displayName = "Clipboard"; | ||
ClipboardWithIcon.displayName = "Clipboard.WithIcon"; | ||
ClipboardWithIconText.displayName = "Clipboard.WithIconText"; | ||
|
||
export const Clipboard = Object.assign(ClipboardComponent, { | ||
WithIcon: ClipboardWithIcon, | ||
WithIconText: ClipboardWithIconText, | ||
}); |
Oops, something went wrong.