Skip to content

Commit c89df13

Browse files
feat: button group with actions and some styles
1 parent e377252 commit c89df13

File tree

1 file changed

+94
-14
lines changed

1 file changed

+94
-14
lines changed

src/components/PageHeading.tsx

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import {useRouter} from 'next/router';
1919
import {IconCanary} from './Icon/IconCanary';
2020
import {IconExperimental} from './Icon/IconExperimental';
2121
import {IconCopy} from './Icon/IconCopy';
22-
import {Button} from './Button';
22+
import {IconChevron} from './Icon/IconChevron';
23+
import {Menu} from '@headlessui/react';
24+
import {IconNewPage} from './Icon/IconNewPage';
2325

2426
interface PageHeadingProps {
2527
title: string;
@@ -35,15 +37,21 @@ function CopyAsMarkdownButton() {
3537
const {asPath} = useRouter();
3638
const [copied, setCopied] = useState(false);
3739

40+
const cleanPath = asPath.split(/[?#]/)[0];
41+
const mdPath = cleanPath + '.md';
42+
const fullMdUrl =
43+
typeof window !== 'undefined'
44+
? `${window.location.origin}${mdPath}`
45+
: mdPath;
46+
3847
useEffect(() => {
3948
if (!copied) return;
4049
const timer = setTimeout(() => setCopied(false), 2000);
4150
return () => clearTimeout(timer);
4251
}, [copied]);
4352

4453
async function fetchPageBlob() {
45-
const cleanPath = asPath.split(/[?#]/)[0];
46-
const res = await fetch(cleanPath + '.md');
54+
const res = await fetch(mdPath);
4755
if (!res.ok) throw new Error('Failed to fetch');
4856
const text = await res.text();
4957
return new Blob([text], {type: 'text/plain'});
@@ -62,17 +70,89 @@ function CopyAsMarkdownButton() {
6270
}
6371

6472
return (
65-
<Button onClick={handleCopy} className="text-sm py-1 px-3">
66-
<IconCopy className="w-3.5 h-3.5 me-1.5" />
67-
{copied ? (
68-
'Copied!'
69-
) : (
70-
<>
71-
<span className="hidden sm:inline">Copy page</span>
72-
<span className="sm:hidden">Copy</span>
73-
</>
74-
)}
75-
</Button>
73+
<div className="relative inline-flex items-center rounded-full text-primary dark:text-primary-dark shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark">
74+
<button
75+
onMouseDown={(e) => {
76+
e.preventDefault();
77+
e.stopPropagation();
78+
}}
79+
onClick={handleCopy}
80+
disabled={copied}
81+
className="text-sm font-bold leading-tight py-1 p-3 inline-flex items-center rounded-s-full outline-none hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10 disabled:opacity-50 disabled:cursor-not-allowed">
82+
<IconCopy className="w-3.5 h-3.5 me-1.5" />
83+
{copied ? (
84+
'Copied!'
85+
) : (
86+
<>
87+
<span className="hidden sm:inline">Copy page</span>
88+
<span className="sm:hidden">Copy</span>
89+
</>
90+
)}
91+
</button>
92+
93+
<span
94+
className="w-px h-4 bg-gray-40/30 dark:bg-gray-60/30"
95+
aria-hidden="true"
96+
/>
97+
98+
{/* @ts-ignore */}
99+
<Menu>
100+
{/* @ts-ignore */}
101+
<Menu.Button as={React.Fragment}>
102+
<button
103+
onMouseDown={(e) => {
104+
e.preventDefault();
105+
e.stopPropagation();
106+
}}
107+
className="py-1 ps-2 pe-3 inline-flex items-center rounded-e-full outline-none hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10">
108+
<IconChevron displayDirection="down" className="w-3.5 h-3.5" />
109+
</button>
110+
</Menu.Button>
111+
112+
{/* @ts-ignore */}
113+
<Menu.Items
114+
transition
115+
className="absolute end-0 top-full mt-2 p-1 w-48 rounded-lg bg-white dark:bg-wash-dark border border-border dark:border-border-dark shadow-sm z-50 focus:outline-none">
116+
{/* @ts-ignore */}
117+
<Menu.Item>
118+
<a
119+
href={mdPath}
120+
target="_blank"
121+
rel="noreferrer"
122+
className="w-full font-bold text-start px-4 py-0.5 text-sm rounded-md inline-flex items-center justify-between hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10">
123+
View markdown
124+
<IconNewPage className="w-3 h-3" />
125+
</a>
126+
</Menu.Item>
127+
{/* @ts-ignore */}
128+
<Menu.Item>
129+
<a
130+
href={`https://chatgpt.com/?q=${encodeURIComponent(
131+
`Read from this URL: ${fullMdUrl} and explain it to me.`
132+
)}`}
133+
target="_blank"
134+
rel="noreferrer"
135+
className="w-full font-bold text-start px-4 py-0.5 text-sm rounded-md inline-flex items-center justify-between hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10">
136+
Open in ChatGPT
137+
<IconNewPage className="w-3 h-3" />
138+
</a>
139+
</Menu.Item>
140+
{/* @ts-ignore */}
141+
<Menu.Item>
142+
<a
143+
href={`https://claude.ai/new?q=${encodeURIComponent(
144+
`Read from this URL: ${fullMdUrl} and explain it to me.`
145+
)}`}
146+
target="_blank"
147+
rel="noreferrer"
148+
className="w-full font-bold text-start px-4 py-0.5 text-sm rounded-md inline-flex items-center justify-between hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/2 active:dark:bg-gray-60/10">
149+
Open in Claude
150+
<IconNewPage className="w-3 h-3" />
151+
</a>
152+
</Menu.Item>
153+
</Menu.Items>
154+
</Menu>
155+
</div>
76156
);
77157
}
78158

0 commit comments

Comments
 (0)