Skip to content

Commit

Permalink
feat(v0.5.9): ✨ add link hook to pass custom link renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
themashcodee committed Sep 20, 2024
1 parent 95e792f commit 893edac
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 29 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"repository": "https://github.com/themashcodee/slack-blocks-to-jsx.git",
"license": "MIT",
"version": "0.5.8",
"version": "0.5.9",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
Expand Down
26 changes: 2 additions & 24 deletions src/components/blocks/rich_text/rich_text_section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SlackDate } from "../../../utils/markdown_parser/sub_elements";
import { RichTextSectionBroadcast } from "./rich_text_section_broadcast";
import { RichTextSectionChannel } from "./rich_text_section_channel";
import { RichTextSectionEmoji } from "./rich_text_section_emoji";
import { RichTextSectionLink } from "./rich_text_section_link";
import { RichTextSectionUser } from "./rich_text_section_user";
import { RichTextSectionUserGroup } from "./rich_text_section_user_group";

Expand All @@ -19,6 +20,7 @@ export const RichTextSectionElement = (props: RichTextSectionElementProps) => {
if (element.type === "broadcast") return <RichTextSectionBroadcast {...element} />;
if (element.type === "emoji") return <RichTextSectionEmoji {...element} />;
if (element.type === "usergroup") return <RichTextSectionUserGroup {...element} />;
if (element.type === "link") return <RichTextSectionLink {...element} />;

if (element.type === "text") {
const { text, style } = element;
Expand All @@ -38,30 +40,6 @@ export const RichTextSectionElement = (props: RichTextSectionElementProps) => {
);
}

if (element.type === "link") {
const { url, style, text } = element;

// link elements may not have text prop when it is just a URL
const linkText = text || url;
return (
<a
target="_blank"
rel="noreferrer noopener"
href={url}
className={merge_classes([
"slack_blocks_to_jsx__rich_text_section_element_link",
"text-blue-primary hover:underline underline-offset-4",
style?.italic ? "italic" : "",
style?.strike ? "line-through" : "",
style?.code ? "slack_inline_code" : "",
style?.bold ? "font-medium" : "",
])}
>
{linkText}
</a>
);
}

if (element.type === "date") {
const { timestamp, format, style } = element;

Expand Down
52 changes: 52 additions & 0 deletions src/components/blocks/rich_text/rich_text_section_link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { RichTextSectionLink as RichTextSectionLinkType } from "../../../types";
import { useGlobalData } from "../../../store";
import { merge_classes } from "../../../utils";

type Props = RichTextSectionLinkType;

export const RichTextSectionLink = (props: Props) => {
const { url, style, text } = props;
const { hooks } = useGlobalData();

// link elements may not have text prop when it is just a URL
const linkText = text || url;

if (hooks.link) {
return (
<>
{hooks.link({
href: url,
target: "_blank",
rel: "noreferrer noopener",
className: merge_classes([
"slack_blocks_to_jsx__rich_text_section_element_link",
"text-blue-primary hover:underline underline-offset-4",
style?.italic ? "italic" : "",
style?.strike ? "line-through" : "",
style?.code ? "slack_inline_code" : "",
style?.bold ? "font-medium" : "",
]),
children: linkText,
})}
</>
);
}

return (
<a
target="_blank"
rel="noreferrer noopener"
href={url}
className={merge_classes([
"slack_blocks_to_jsx__rich_text_section_element_link",
"text-blue-primary hover:underline underline-offset-4",
style?.italic ? "italic" : "",
style?.strike ? "line-through" : "",
style?.code ? "slack_inline_code" : "",
style?.bold ? "font-medium" : "",
])}
>
{linkText}
</a>
);
};
31 changes: 27 additions & 4 deletions src/components/blocks/video.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState } from "react";
import { VideoBlock } from "../../types";
import { VideoBlock, TextObject as TextObjectType } from "../../types";
import { TextObject } from "../composition_objects";
import { useGlobalData } from "../../store";

type VideoProps = {
data: VideoBlock;
Expand Down Expand Up @@ -36,9 +37,7 @@ export const Video = (props: VideoProps) => {

<div className="flex flex-wrap items-center gap-1 slack_blocks_to_jsx__video_title">
{title_url ? (
<a href={title_url} className="text-blue-primary" target="_blank" rel="noreferrer">
<TextObject data={title} />
</a>
<RenderLink title={title} url={title_url} />
) : (
<p>
<TextObject data={title} />
Expand Down Expand Up @@ -87,3 +86,27 @@ export const Video = (props: VideoProps) => {
</div>
);
};

const RenderLink = ({ url, title }: { url: string; title: TextObjectType<"plain_text"> }) => {
const { hooks } = useGlobalData();

if (hooks.link) {
return (
<>
{hooks.link({
href: url,
children: <TextObject data={title} />,
className: "text-blue-primary",
rel: "noopener noreferrer",
target: "_blank",
})}
</>
);
}

return (
<a href={url} className="text-blue-primary" target="_blank" rel="noopener noreferrer">
<TextObject data={title} />
</a>
);
};
13 changes: 13 additions & 0 deletions src/store/useGlobalContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ type Emoji = {
skin_tone?: 1 | 2 | 3 | 4 | 5 | 6;
};

type LinkInput = {
href: string;
children: ReactNode;
className: string;
target?: "_blank" | "_self" | "_parent" | "_top";
rel?: string;
};

type Hooks = {
user?: (data: UserWithStyle) => ReactNode;
channel?: (data: ChannelWithStyle) => ReactNode;
Expand All @@ -55,6 +63,11 @@ type Hooks = {
link: string | null;
fallback: string;
}) => ReactNode;
/**
*
* This hook allows you to replace the anchor (a) tag with your own wrapper. It gets applied to rich_text_section links, links in mrkdown, slack date optional links, and video block title URLs.
*/
link?: (input: LinkInput) => ReactNode;
};

export type GlobalStore = {
Expand Down
25 changes: 25 additions & 0 deletions src/utils/markdown_parser/sub_elements/link.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useGlobalData } from "../../../store";
import { LinkSubElement } from "../types";
import { Delete } from "./delete";
import { Emphasis } from "./emphasis";
Expand All @@ -11,6 +12,30 @@ type Props = {

export const Link = (props: Props) => {
const { element } = props;
const { hooks } = useGlobalData();

if (hooks.link) {
return (
<>
{hooks.link({
href: element.url,
children: (
<>
{element.children.map((child, i) => {
if (child.type === "delete") return <Delete key={i} element={child} />;
if (child.type === "emphasis") return <Emphasis key={i} element={child} />;
if (child.type === "strong") return <Strong key={i} element={child} />;
if (child.type === "slack_emoji") return <SlackEmoji key={i} element={child} />;

return <Text key={i} element={child} />;
})}
</>
),
className: "slack_link",
})}
</>
);
}

return (
<a href={element.url} className="slack_link">
Expand Down
16 changes: 16 additions & 0 deletions src/utils/markdown_parser/sub_elements/slack_date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,24 @@ export const SlackDate = (props: Props) => {
};

const WrapWithLink = (props: { wrap: boolean; href: string; children: ReactNode }) => {
const { hooks } = useGlobalData();

if (!props.wrap) return <>{props.children}</>;

if (hooks.link) {
return (
<>
{hooks.link({
href: props.href,
children: props.children,
className: "text-blue-primary",
rel: "noopener noreferrer",
target: "_blank",
})}
</>
);
}

return (
<a href={props.href} target="_blank" rel="noopener noreferrer" className="text-blue-primary">
{props.children}
Expand Down

0 comments on commit 893edac

Please sign in to comment.