mirror of
https://github.com/tale/headplane.git
synced 2026-08-30 16:59:56 +00:00
feat: redesign code and attribute
This commit is contained in:
@@ -1,18 +1,23 @@
|
|||||||
import { CopyIcon } from '@primer/octicons-react';
|
import { Check, Copy } from 'lucide-react';
|
||||||
|
import cn from '~/utils/cn';
|
||||||
import toast from '~/utils/toast';
|
import toast from '~/utils/toast';
|
||||||
|
|
||||||
interface Props {
|
export interface AttributeProps {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
isCopyable?: boolean;
|
isCopyable?: boolean;
|
||||||
link?: string;
|
link?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Attribute({ name, value, link, isCopyable }: Props) {
|
export default function Attribute({
|
||||||
const canCopy = isCopyable ?? false;
|
name,
|
||||||
|
value,
|
||||||
|
link,
|
||||||
|
isCopyable,
|
||||||
|
}: AttributeProps) {
|
||||||
return (
|
return (
|
||||||
<dl className="flex gap-1 text-sm w-full">
|
<dl className="flex items-center w-full gap-x-1">
|
||||||
<dt className="w-1/2 shrink-0 min-w-0 truncate text-gray-700 dark:text-gray-300 py-1">
|
<dt className="font-semibold w-1/4 shrink-0 text-sm">
|
||||||
{link ? (
|
{link ? (
|
||||||
<a className="hover:underline" href={link}>
|
<a className="hover:underline" href={link}>
|
||||||
{name}
|
{name}
|
||||||
@@ -21,22 +26,42 @@ export default function Attribute({ name, value, link, isCopyable }: Props) {
|
|||||||
name
|
name
|
||||||
)}
|
)}
|
||||||
</dt>
|
</dt>
|
||||||
|
<dd
|
||||||
|
className={cn(
|
||||||
|
'rounded-lg truncate w-full px-2.5 py-1 text-sm',
|
||||||
|
'flex items-center gap-x-1',
|
||||||
|
'focus-within:outline-none focus-within:ring-2',
|
||||||
|
isCopyable && 'hover:bg-headplane-100 dark:hover:bg-headplane-800',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{isCopyable ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-full flex items-center gap-x-1 outline-none"
|
||||||
|
onClick={async (event) => {
|
||||||
|
const svgs = event.currentTarget.querySelectorAll('svg');
|
||||||
|
for (const svg of svgs) {
|
||||||
|
svg.toggleAttribute('data-copied', true);
|
||||||
|
}
|
||||||
|
|
||||||
{canCopy ? (
|
await navigator.clipboard.writeText(value);
|
||||||
<button
|
toast('Copied to clipboard');
|
||||||
type="button"
|
|
||||||
className="focus:outline-none flex items-center gap-x-1 truncate hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md"
|
setTimeout(() => {
|
||||||
onClick={async () => {
|
for (const svg of svgs) {
|
||||||
await navigator.clipboard.writeText(value);
|
svg.toggleAttribute('data-copied', false);
|
||||||
toast(`Copied ${name}`);
|
}
|
||||||
}}
|
}, 1000);
|
||||||
>
|
}}
|
||||||
<dd className="min-w-0 truncate px-2 py-1">{value}</dd>
|
>
|
||||||
<CopyIcon className="text-gray-600 dark:text-gray-200 pr-2 w-max h-3" />
|
{value}
|
||||||
</button>
|
<Check className="h-4.5 w-4.5 p-1 hidden data-[copied]:block" />
|
||||||
) : (
|
<Copy className="h-4.5 w-4.5 p-1 block data-[copied]:hidden" />
|
||||||
<dd className="min-w-0 truncate px-2 py-1">{value}</dd>
|
</button>
|
||||||
)}
|
) : (
|
||||||
|
value
|
||||||
|
)}
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-35
@@ -1,52 +1,53 @@
|
|||||||
import { CheckIcon, CopyIcon } from '@primer/octicons-react';
|
import { Check, Copy } from 'lucide-react';
|
||||||
import { HTMLProps, useState } from 'react';
|
import { HTMLProps } from 'react';
|
||||||
import cn from '~/utils/cn';
|
import cn from '~/utils/cn';
|
||||||
import toast from '~/utils/toast';
|
import toast from '~/utils/toast';
|
||||||
|
|
||||||
interface Props extends HTMLProps<HTMLSpanElement> {
|
export interface CodeProps extends HTMLProps<HTMLSpanElement> {
|
||||||
isCopyable?: boolean;
|
isCopyable?: boolean;
|
||||||
|
children: string | string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Code(props: Props) {
|
export default function Code({ isCopyable, children, className }: CodeProps) {
|
||||||
const [isCopied, setIsCopied] = useState(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<code
|
||||||
<code
|
className={cn(
|
||||||
className={cn(
|
'bg-headplane-100 dark:bg-headplane-800 px-1 py-0.5 font-mono',
|
||||||
'bg-ui-100 dark:bg-ui-800 p-0.5 rounded-md',
|
'rounded-lg focus-within:outline-none focus-within:ring-2',
|
||||||
props.className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{props.children}
|
{isCopyable ? (
|
||||||
</code>
|
|
||||||
{props.isCopyable ? (
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className="inline-flex items-center gap-x-1"
|
||||||
'ml-1 p-1 rounded-md',
|
onClick={async (event) => {
|
||||||
'bg-ui-100 dark:bg-ui-800',
|
const text = Array.isArray(children)
|
||||||
'text-ui-500 dark:text-ui-400',
|
? children.join(' ')
|
||||||
'inline-flex items-center justify-center',
|
: children;
|
||||||
)}
|
|
||||||
onClick={() => {
|
const svgs = event.currentTarget.querySelectorAll('svg');
|
||||||
if (!props.children) {
|
for (const svg of svgs) {
|
||||||
throw new Error('Made copyable without children');
|
svg.toggleAttribute('data-copied', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.clipboard.writeText(props.children.join(''));
|
await navigator.clipboard.writeText(text);
|
||||||
toast('Copied to clipboard');
|
toast('Copied to clipboard');
|
||||||
setIsCopied(true);
|
|
||||||
setTimeout(() => setIsCopied(false), 1000);
|
setTimeout(() => {
|
||||||
|
for (const svg of svgs) {
|
||||||
|
svg.toggleAttribute('data-copied', false);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isCopied ? (
|
{children}
|
||||||
<CheckIcon className="h-3 w-3" />
|
<Check className="h-4.5 w-4.5 p-1 hidden data-[copied]:block" />
|
||||||
) : (
|
<Copy className="h-4.5 w-4.5 p-1 block data-[copied]:hidden" />
|
||||||
<CopyIcon className="h-3 w-3" />
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
) : undefined}
|
) : (
|
||||||
</>
|
children
|
||||||
|
)}
|
||||||
|
</code>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user