Input.tsx114 lines · main
1import { Copy } from 'lucide-react'
2import React, {
3 ComponentProps,
4 ComponentPropsWithoutRef,
5 ElementRef,
6 forwardRef,
7 useState,
8} from 'react'
9import {
10 Input as BaseInput,
11 cn,
12 copyToClipboard,
13 InputGroup,
14 InputGroupAddon,
15 InputGroupButton,
16 InputGroupInput,
17} from 'ui'
18
19export interface Props extends Omit<ComponentProps<typeof BaseInput>, 'onCopy'> {
20 copy?: boolean
21 showCopyOnHover?: boolean
22 onCopy?: () => void
23 icon?: any
24 reveal?: boolean
25 actions?: React.ReactNode
26 iconContainerClassName?: string
27 containerClassName?: string
28}
29
30const Input = forwardRef<
31 ElementRef<typeof BaseInput>,
32 ComponentPropsWithoutRef<typeof BaseInput> & Props
33>(
34 (
35 {
36 copy,
37 showCopyOnHover = false,
38 icon,
39 reveal = false,
40 actions,
41 onCopy,
42 iconContainerClassName,
43 containerClassName,
44 size = 'small',
45 ...props
46 }: Props,
47 ref
48 ) => {
49 const [copyLabel, setCopyLabel] = useState('Copy')
50 const [hidden, setHidden] = useState(true)
51
52 function _onCopy(value: any) {
53 copyToClipboard(value, () => {
54 /* clipboard successfully set */
55 setCopyLabel('Copied')
56 setTimeout(function () {
57 setCopyLabel('Copy')
58 }, 3000)
59 onCopy?.()
60 })
61 }
62
63 function onReveal() {
64 setHidden(false)
65 }
66
67 return (
68 <InputGroup className={containerClassName}>
69 <InputGroupInput
70 ref={ref}
71 onFocus={(event) => event.target.select()}
72 {...props}
73 size={size}
74 onCopy={onCopy}
75 type={reveal && hidden ? 'password' : props.type}
76 disabled={props.disabled}
77 className={props.className}
78 data-1p-ignore // 1Password
79 data-lpignore="true" // LastPass
80 data-form-type="other" // Dashlane
81 data-bwignore // Bitwarden
82 />
83 {icon && <InputGroupAddon align="inline-start">{icon}</InputGroupAddon>}
84 {copy || actions ? (
85 <InputGroupAddon
86 align="inline-end"
87 // Override defaults
88 className="pr-1 has-[>button]:mr-0 has-[>kbd]:mr-0"
89 >
90 {copy && !(reveal && hidden) ? (
91 <InputGroupButton
92 size="tiny"
93 type="default"
94 className={cn(showCopyOnHover && 'opacity-0 group-hover:opacity-100 transition')}
95 icon={<Copy size={16} className="text-foreground-muted" />}
96 onClick={() => _onCopy(props.value)}
97 >
98 {copyLabel}
99 </InputGroupButton>
100 ) : null}
101 {reveal && hidden ? (
102 <InputGroupButton size="tiny" type="default" onClick={onReveal}>
103 Reveal
104 </InputGroupButton>
105 ) : null}
106 {actions && actions}
107 </InputGroupAddon>
108 ) : null}
109 </InputGroup>
110 )
111 }
112)
113
114export { Input }