Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/slimy-plums-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-github-permalink": minor
---

Add Stack Overflow link components
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Display Github permalinks as codeblocks.

Display Github issue links.

Display Stack Overflow question links.

![screenshot of the tool in action - dark mode ](./screenshot-permalink-dark.png)
![screenshot of the tool in action - light mode ](./screenshot-permalink-light.png)
![screenshot of the tool in action - dark mode ](./screenshot-issuelink-dark.png)
Expand All @@ -27,9 +29,9 @@ This package is compatible with Next 13+ and the components can be used as RSCs

Three variants of each component are exported

- GithubPermalink/GithubIssueLink - Client component - It fetches the data as on the client in a useEffect. ie. Data won't be retrieved until application has loaded in user's browser.
- GithubPermalinkBase/GithubIssueLinkBase - this is the base component - it does no data fetching on its own.
- GithubPermalinkRsc/GithubIssueLinkRsc - This is an RSC.
- GithubPermalink/GithubIssueLink/StackOverflowLink - Client component - It fetches the data as on the client in a useEffect. ie. Data won't be retrieved until application has loaded in user's browser.
- GithubPermalinkBase/GithubIssueLinkBase/StackOverflowLinkBase - this is the base component - it does no data fetching on its own.
- GithubPermalinkRsc/GithubIssueLinkRsc/StackOverflowLinkRsc - This is an RSC.



Expand Down Expand Up @@ -112,6 +114,26 @@ export function MyApp() {
}
```

## Stack Overflow Links

### Usage
```jsx
import { StackOverflowLink } from 'react-github-permalink';
import "react-github-permalink/dist/github-permalink.css"; // Or provide your own styles

export function MyApp() {
return <StackOverflowLink questionLink='https://stackoverflow.com/questions/64572466/how-to-use-react-context-in-typescript' />
}
```

StackOverflowLink also has an inline variant:

```jsx
export function MyApp() {
return <StackOverflowLink questionLink='https://stackoverflow.com/questions/64572466/how-to-use-react-context-in-typescript' variant="inline"/>
}
```

## Rate Limits and Authentication

By default the components make unauthenticated requests against Github's API. The rate limit for such requests is 60/hour and only publicly visible repositories are available.
Expand All @@ -126,6 +148,7 @@ The global configuration object has this signature
type BaseConfiguration = {
getDataFn: (permalink: string, githubToken?: string | undefined, onError?: ((err: unknown) => void) | undefined) => Promise<GithubPermalinkDataResponse>;
getIssueFn: (issueLink: string, githubToken?: string | undefined, onError?: ((err: unknown) => void) | undefined) => Promise<GithubIssueLinkDataResponse>;
getStackOverflowFn: (questionLink: string, onError?: ((err: unknown) => void) | undefined) => Promise<StackOverflowLinkDataResponse>;
githubToken: string | undefined;
onError: ((e: unknown) => void) | undefined;
}
Expand Down
92 changes: 49 additions & 43 deletions src/library/GithubIssueLink/GithubIssueLinkBase.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import { PropsWithChildren } from "react";
import { GithubSvg } from "../GithubSvg/GithubSvg";
import { GithubSvg } from "../images/GithubSvg/GithubSvg";
import { GithubIssueLinkDataResponse } from "../config/GithubPermalinkContext";
import { ErrorMessages } from "../ErrorMessages/ErrorMessages";
import { Reactions } from "../common/Reactions/Reactions";
Expand All @@ -17,60 +17,66 @@ export type GithubIssueLinkBaseProps = {


export function GithubIssueLinkBase(props: GithubIssueLinkBaseProps) {
const { data, variant ="block", issueLink} = props;

if (variant === "inline"){
if(data.status === "ok"){
return <Inline href={issueLink} text={`${data.owner}/${data.repo}#${data.issueNumber} ${data.issueTitle}`}/>
}
else {
return <Inline href={issueLink} text={issueLink}/>
}
}
const { data, variant = "block", issueLink } = props;

if (variant === "inline") {
if (data.status === "ok") {
return <GithubIssueLinkInner {...props} header={<>
<div className="react-github-issuelink-repo">
<GithubSvg />
<p>
{data.owner}/{data.repo}
</p>
</div>

<div className="react-github-issuelink-body">
<p><span className="react-github-issuelink-title">{data.issueTitle}</span><span className="react-github-issuelink-number"> #{data.issueNumber}</span> </p>

{data.issueState === "open" ? <div className="react-github-issuelink-status open"><svg aria-hidden="true" height="12" viewBox="0 0 16 16" version="1.1" width="12" data-view-component="true" >
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path>
</svg><span> Open</span></div> : <div className="react-github-issuelink-status closed"><svg aria-hidden="true" height="12" viewBox="0 0 16 16" version="1.1" width="12" data-view-component="true" >
<path d="M11.28 6.78a.75.75 0 0 0-1.06-1.06L7.25 8.69 5.78 7.22a.75.75 0 0 0-1.06 1.06l2 2a.75.75 0 0 0 1.06 0l3.5-3.5Z"></path><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-1.5 0a6.5 6.5 0 1 0-13 0 6.5 6.5 0 0 0 13 0Z"></path>
</svg><span> Closed</span></div>}


</div>
<div>
{data.reactions && <Reactions reactions={data.reactions}/>}
</div>
</>}>
</GithubIssueLinkInner>

}

return <GithubIssueLinkInner {...props}>
<ErrorMessages data={data} />
</GithubIssueLinkInner>
return <Inline href={issueLink} text={`${data.owner}/${data.repo}#${data.issueNumber} ${data.issueTitle}`} />
}
else {
return <Inline href={issueLink} text={issueLink} />
}
}

if (data.status === "ok") {
return <GithubIssueLinkFrame {...props} header={<>
<div className="react-github-issuelink-repo">
<GithubSvg />
<p>
{data.owner}/{data.repo}
</p>
</div>

<div className="react-github-issuelink-body">
<p><span className="react-github-issuelink-title">{data.issueTitle}</span><span className="react-github-issuelink-number"> #{data.issueNumber}</span> </p>

{data.issueState === "open" ? <div className="react-github-issuelink-status open"><svg aria-hidden="true" height="12" viewBox="0 0 16 16" version="1.1" width="12" data-view-component="true" >
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path>
</svg><span> Open</span></div> : <div className="react-github-issuelink-status closed"><svg aria-hidden="true" height="12" viewBox="0 0 16 16" version="1.1" width="12" data-view-component="true" >
<path d="M11.28 6.78a.75.75 0 0 0-1.06-1.06L7.25 8.69 5.78 7.22a.75.75 0 0 0-1.06 1.06l2 2a.75.75 0 0 0 1.06 0l3.5-3.5Z"></path><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-1.5 0a6.5 6.5 0 1 0-13 0 6.5 6.5 0 0 0 13 0Z"></path>
</svg><span> Closed</span></div>}


</div>
<div>
{data.reactions && <Reactions reactions={data.reactions} />}
</div>
</>}>
</GithubIssueLinkFrame>

}


return <GithubIssueLinkFrame {...props}>
<ErrorMessages data={data} />
</GithubIssueLinkFrame>

}


function GithubIssueLinkInner(props: PropsWithChildren<{
/**
* This thing is displayed regardless of whether it has errored or not.
* @param props
* @returns
*/
function GithubIssueLinkFrame(props: PropsWithChildren<{
header?: React.ReactNode
} & {
issueLink: string;
className?: string;
}>) {

const {issueLink, className =''} = props;
const { issueLink, className = '' } = props;

return <div className={`rgp-base react-github-issuelink ${className}`}>
<a href={issueLink}>
Expand Down
16 changes: 8 additions & 8 deletions src/library/GithubPermalink/GithubPermalinkBase.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GithubPermalinkDataResponse, } from "../config/GithubPermalinkContext";
import { ErrorMessages } from "../ErrorMessages/ErrorMessages";
import { GithubSvg } from "../GithubSvg/GithubSvg";
import { GithubSvg } from "../images/GithubSvg/GithubSvg";
import { PropsWithChildren } from "react";
import { SyntaxHighlight } from "../SyntaxHighlight/SyntaxHighlight";
import { formatForLineExclusions } from "./formatLineExclusions";
Expand Down Expand Up @@ -34,32 +34,32 @@ export function GithubPermalinkBase(props: GithubPermalinkBaseProps) {

return acc + "\n" + cur.lines.join("\n");
}, '')
return <GithubPermalinkInner {...props} clipboard={clipboard} header={<>
return <GithubPermalinkFrame {...props} clipboard={clipboard} header={<>
<a href={permalink} className="file-link">{`${data.owner}/${data.repo}/${data.path}`}</a>
<p>{data.lineFrom === data.lineTo ? <>Line {data.lineFrom}</> : <>Lines {data.lineFrom} to {data.lineTo}</>} in <a className="commit-link" href={data.commitUrl}>{data.commit.slice(0, 7)}</a></p>
</>}>

{formatedLineExclusions.map((v) => {
if (v.isExclude) {
return <SyntaxHighlight className="hide-line-numbers" text={excludeText} startingLineNumber={v.from} key={v.from}/>
return <SyntaxHighlight className="hide-line-numbers" text={excludeText} startingLineNumber={v.from} key={v.from} />

}

return <SyntaxHighlight text={v.lines.join("\n")} startingLineNumber={v.from} key={v.from}/>
return <SyntaxHighlight text={v.lines.join("\n")} startingLineNumber={v.from} key={v.from} />

})}

</GithubPermalinkInner>
</GithubPermalinkFrame>

}

return <GithubPermalinkInner {...props}>
return <GithubPermalinkFrame {...props}>
<ErrorMessages data={data} />
</GithubPermalinkInner>
</GithubPermalinkFrame>
}


function GithubPermalinkInner(props: PropsWithChildren<{
function GithubPermalinkFrame(props: PropsWithChildren<{
header?: React.ReactNode
clipboard?: string;
} & GithubPermalinkBaseProps>) {
Expand Down
Loading