diff --git a/src/components/Clippy.tsx b/src/components/Clippy.tsx new file mode 100644 index 0000000..bd397f1 --- /dev/null +++ b/src/components/Clippy.tsx @@ -0,0 +1,33 @@ +import * as React from 'react' +import { cx } from 'utils/cx' +import { copyElementContent } from 'utils/DOMHelper' + +type Props = { + codeSnippetElement: Element +} + +export function Clippy({ codeSnippetElement }: Props) { + const [status, setStatus] = React.useState<'normal' | 'success' | 'fail'>('normal') + React.useEffect(() => { + const timer = window.setTimeout(() => { + setStatus('normal') + }, 1000) + return () => window.clearTimeout(timer) + }, [status]) + + const onClippyClick = React.useCallback(function onClippyClick() { + if (copyElementContent(codeSnippetElement)) { + setStatus('success') + } else { + setStatus('fail') + } + }, []) + + return ( +
)
const target = e.target as Element
if (target.nodeName === 'PRE') {
- if (currentCodeSnippetElement !== target) {
- currentCodeSnippetElement = target
+ if (
+ target.previousSibling === null ||
+ !(target.previousSibling instanceof Element) ||
+ !target.previousSibling.classList.contains('clippy-wrapper')
+ ) {
/**
*
*
@@ -306,7 +261,12 @@ export function attachCopySnippet() {
*
*
*/
- if (target.parentNode) target.parentNode.insertBefore(clippy, target)
+ if (target.parentNode) {
+ const clippyElement = await renderReact(
+ React.createElement(Clippy, { codeSnippetElement: target }),
+ )
+ target.parentNode.insertBefore(clippyElement, target)
+ }
}
}
}),
diff --git a/src/utils/general.ts b/src/utils/general.ts
index 9fc3281..cde3a0d 100644
--- a/src/utils/general.ts
+++ b/src/utils/general.ts
@@ -1,16 +1,15 @@
+import { ReactElement } from 'react'
+import * as ReactDOM from 'react-dom'
import { TreeNode } from './VisibleNodesGenerator'
export function pick(source: T, keys: string[]): Partial {
if (keys && typeof keys === 'object') {
- return (Array.isArray(keys) ? keys : Object.keys(keys)).reduce(
- (copy, key) => {
- if (key in source) {
- copy[key as keyof T] = source[key as keyof T]
- }
- return copy
- },
- {} as Partial,
- )
+ return (Array.isArray(keys) ? keys : Object.keys(keys)).reduce((copy, key) => {
+ if (key in source) {
+ copy[key as keyof T] = source[key as keyof T]
+ }
+ return copy
+ }, {} as Partial)
}
return {} as Partial
}
@@ -114,20 +113,22 @@ export function parseURLSearch(search: string = window.location.search) {
}
export async function JSONRequest(url: string, data: any, extra: RequestInit = { method: 'post' }) {
- return (await fetch(url, {
- mode: 'cors',
- cache: 'no-cache',
- credentials: 'same-origin',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- },
- redirect: 'follow',
- referrerPolicy: 'no-referrer',
- method: extra.method || 'post',
- body: JSON.stringify(data),
- ...extra,
- })).json()
+ return (
+ await fetch(url, {
+ mode: 'cors',
+ cache: 'no-cache',
+ credentials: 'same-origin',
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
+ redirect: 'follow',
+ referrerPolicy: 'no-referrer',
+ method: extra.method || 'post',
+ body: JSON.stringify(data),
+ ...extra,
+ })
+ ).json()
}
export function searchKeyToRegexps(searchKey: string) {
@@ -140,3 +141,12 @@ export function searchKeyToRegexps(searchKey: string) {
return [/$^/] // matching nothing if failed transforming regexp
}
}
+
+export async function renderReact(element: ReactElement) {
+ return new Promise(resolve => {
+ const mount = document.createElement('div')
+ ReactDOM.render(element, mount, () => {
+ resolve(mount.childNodes[0])
+ })
+ })
+}