Skip to content

Commit

Permalink
chore: use text input as anchor element, refactor and cleanup, ref wo…
Browse files Browse the repository at this point in the history
…rking only on second render #2025
  • Loading branch information
marek-mihok committed Aug 28, 2023
1 parent 0e4e1e6 commit 1d0f007
Showing 1 changed file with 21 additions and 23 deletions.
44 changes: 21 additions & 23 deletions ui/src/copyable_text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { clas, cssVar, pc } from './theme'
const
BUTTON_HEIGHT = 24,
BUTTON_WIDTH = 34,
CORNER_OFFSET = 6
CORNER_OFFSET = 4

const
css = stylesheet({
Expand All @@ -33,9 +33,6 @@ const
}
}
},
labelContainer: {
position: 'relative'
}
}),
fullHeightStyle = {
display: 'flex',
Expand All @@ -60,18 +57,19 @@ export interface CopyableText {
height?: S
}

export const ClipboardCopyButton = ({ value, anchorElementRef, showOnHover }: { value: S, anchorElementRef: any, showOnHover: B }) => {
type CopyButton = { value: S, anchorElement: HTMLElement | undefined, showOnHoverOnly?: B }

export const ClipboardCopyButton = ({ value, anchorElement, showOnHoverOnly = false }: CopyButton) => {
const
timeoutRef = React.useRef<U>(),
[copied, setCopied] = React.useState(false),
[visible, setVisible] = React.useState(!showOnHover),
[visible, setVisible] = React.useState(!showOnHoverOnly),
[position, setPosition] = React.useState({ x: 0, y: 0 }),
onClick = async () => {
const el = anchorElementRef.current
if (!el) return
if (!anchorElement) return
try {
if (document.queryCommandSupported('copy')) {
el.select() // TODO: Test, replace with componentRef
if (document.queryCommandSupported('copy') && anchorElement.tagName === 'input') {
(anchorElement as HTMLInputElement).select()
document.execCommand('copy')
window.getSelection()?.removeAllRanges()
}
Expand All @@ -84,12 +82,11 @@ export const ClipboardCopyButton = ({ value, anchorElementRef, showOnHover }: {
}

React.useEffect(() => {
const el = anchorElementRef.current
if (el) {
const rect = anchorElementRef.current.getBoundingClientRect()
if (anchorElement) {
const rect = anchorElement.getBoundingClientRect()
setPosition({ x: rect.left + rect.width, y: rect.top })
el.addEventListener('mouseenter', () => setVisible(true))
el.addEventListener('mouseleave', (ev: MouseEvent) => {
anchorElement.addEventListener('mouseenter', () => setVisible(true))
anchorElement.addEventListener('mouseleave', (ev: MouseEvent) => {
if ((ev.relatedTarget as HTMLElement)?.id === 'copybutton') return
setVisible(false)
})
Expand All @@ -105,7 +102,7 @@ export const ClipboardCopyButton = ({ value, anchorElementRef, showOnHover }: {
onClick={onClick}
iconProps={{ iconName: copied ? 'CheckMark' : 'Copy' }}
style={{ left: position.x - BUTTON_WIDTH - CORNER_OFFSET, top: position.y + CORNER_OFFSET }}
className={clas(css.btn, copied ? css.copiedBtn : '', showOnHover ? css.animate : '', visible ? css.visible : '')}
className={clas(css.btn, copied ? css.copiedBtn : '', showOnHoverOnly ? css.animate : '', visible ? css.visible : '')}
/>)
}

Expand All @@ -121,14 +118,11 @@ export const XCopyableText = ({ model }: { model: CopyableText }) => {
<Fluent.TextField
data-test={name}
componentRef={ref}
elementRef={domRef} // Temporary solution which will be replaced with ref once TextField is converted to a function component.
// Temporary solution which will be replaced with ref once TextField is converted to a function component.
elementRef={domRef}
value={value}
multiline={multiline}
onRenderLabel={() =>
<div className={css.labelContainer}>
<Fluent.Label>{label}</Fluent.Label>
</div>
}
label={label}
styles={{
root: {
...heightStyle,
Expand All @@ -140,7 +134,11 @@ export const XCopyableText = ({ model }: { model: CopyableText }) => {
}}
readOnly
/>
<ClipboardCopyButton anchorElementRef={domRef} showOnHover={!!multiline} value={value} />
<ClipboardCopyButton
// Use text input as anchorElement.
anchorElement={domRef.current?.children[0]?.children[1]}
showOnHoverOnly={!!multiline} value={value}
/>
</>
)
}

0 comments on commit 1d0f007

Please sign in to comment.