mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-07-25 04:55:58 -04:00
react-twitter-embed ➡️ react-tweet
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import Code from "../Code";
|
||||
import CopyButton from "../CopyButton";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Block = styled("div", {
|
||||
position: "relative",
|
||||
@@ -87,7 +87,7 @@ const CornerCopyButton = styled(CopyButton, {
|
||||
transition: `background ${theme.transitions.fade}, border ${theme.transitions.fade}`,
|
||||
});
|
||||
|
||||
export type CodeBlockProps = ComponentProps<typeof Code> & {
|
||||
export type CodeBlockProps = ComponentPropsWithoutRef<typeof Code> & {
|
||||
highlight?: boolean;
|
||||
withCopyButton?: boolean;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import Giscus from "@giscus/react";
|
||||
import useTheme from "../../hooks/useTheme";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import { giscusConfig } from "../../lib/config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
import type { GiscusProps } from "@giscus/react";
|
||||
|
||||
const Wrapper = styled("div", {
|
||||
@@ -12,7 +12,7 @@ const Wrapper = styled("div", {
|
||||
minHeight: "360px",
|
||||
});
|
||||
|
||||
export type CommentsProps = ComponentProps<typeof Wrapper> & {
|
||||
export type CommentsProps = ComponentPropsWithoutRef<typeof Wrapper> & {
|
||||
title: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import innerText from "react-innertext";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { ClipboardOcticon, CheckOcticon } from "../Icons";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ReactNode, Ref, ComponentPropsWithoutRef, MouseEventHandler } from "react";
|
||||
import type { ReactNode, Ref, ComponentPropsWithoutRef, ElementRef, MouseEventHandler } from "react";
|
||||
|
||||
const Button = styled("button", {
|
||||
lineHeight: 1,
|
||||
@@ -37,10 +37,10 @@ export type CopyButtonProps = ComponentPropsWithoutRef<typeof Button> & {
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
const CopyButton = ({ source, timeout = 2000, ...rest }: CopyButtonProps, ref: Ref<HTMLButtonElement>) => {
|
||||
const CopyButton = ({ source, timeout = 2000, ...rest }: CopyButtonProps, ref: Ref<ElementRef<typeof Button>>) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy: MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
const handleCopy: MouseEventHandler<ElementRef<typeof Button>> = (e) => {
|
||||
// prevent unintentional double-clicks by unfocusing button
|
||||
e.currentTarget.blur();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from "../Link";
|
||||
import { HeartIcon, NextjsLogo } from "../Icons";
|
||||
import { styled, theme, keyframes } from "../../lib/styles/stitches.config";
|
||||
import * as config from "../../lib/config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Wrapper = styled("footer", {
|
||||
width: "100%",
|
||||
@@ -78,7 +78,7 @@ const Heart = styled("span", {
|
||||
},
|
||||
});
|
||||
|
||||
export type FooterProps = ComponentProps<typeof Wrapper>;
|
||||
export type FooterProps = ComponentPropsWithoutRef<typeof Wrapper>;
|
||||
|
||||
const Footer = ({ ...rest }: FooterProps) => {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Selfie from "../Selfie";
|
||||
import Menu from "../Menu";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Wrapper = styled("header", {
|
||||
width: "100%",
|
||||
@@ -40,7 +40,7 @@ const ResponsiveMenu = styled(Menu, {
|
||||
},
|
||||
});
|
||||
|
||||
export type HeaderProps = ComponentProps<typeof Wrapper>;
|
||||
export type HeaderProps = ComponentPropsWithoutRef<typeof Wrapper>;
|
||||
|
||||
const Header = ({ ...rest }: HeaderProps) => {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import innerText from "react-innertext";
|
||||
import HeadingAnchor from "../HeadingAnchor";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Anchor = styled(HeadingAnchor, {
|
||||
margin: "0 0.4em",
|
||||
@@ -52,7 +52,7 @@ const H = styled("h1", {
|
||||
},
|
||||
});
|
||||
|
||||
export type HeadingProps = ComponentProps<typeof H> & {
|
||||
export type HeadingProps = ComponentPropsWithoutRef<typeof H> & {
|
||||
level: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
divider?: boolean;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from "../Link";
|
||||
import { LinkIcon } from "../Icons";
|
||||
import { styled } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const AnchorLink = styled(Link, {
|
||||
lineHeight: 1,
|
||||
@@ -12,7 +12,7 @@ const Icon = styled(LinkIcon, {
|
||||
height: "0.8em",
|
||||
});
|
||||
|
||||
export type HeadingAnchorProps = Omit<ComponentProps<typeof AnchorLink>, "href"> & {
|
||||
export type HeadingAnchorProps = Omit<ComponentPropsWithoutRef<typeof AnchorLink>, "href"> & {
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const RoundedIFrame = styled("iframe", {
|
||||
width: "100%",
|
||||
@@ -9,7 +9,7 @@ const RoundedIFrame = styled("iframe", {
|
||||
borderRadius: theme.radii.corner,
|
||||
});
|
||||
|
||||
export type IFrameProps = ComponentProps<typeof RoundedIFrame> & {
|
||||
export type IFrameProps = ComponentPropsWithoutRef<typeof RoundedIFrame> & {
|
||||
src: string;
|
||||
height: number;
|
||||
width?: number; // defaults to 100%
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import NextImage from "next/image";
|
||||
import Link from "../Link";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
import type { ImageProps as NextImageProps, StaticImageData } from "next/image";
|
||||
|
||||
const DEFAULT_QUALITY = 60;
|
||||
@@ -22,7 +22,7 @@ const StyledImage = styled(NextImage, {
|
||||
borderRadius: theme.radii.corner,
|
||||
});
|
||||
|
||||
export type ImageProps = ComponentProps<typeof StyledImage> & {
|
||||
export type ImageProps = ComponentPropsWithoutRef<typeof StyledImage> & {
|
||||
href?: string; // optionally wrap image in a link
|
||||
inline?: boolean; // don't wrap everything in a `<div>` block
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import Footer from "../Footer";
|
||||
import { SkipToContentLink, SkipToContentTarget } from "../SkipToContent";
|
||||
import useTheme from "../../hooks/useTheme";
|
||||
import { styled, theme, darkTheme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Flex = styled("div", {
|
||||
display: "flex",
|
||||
@@ -34,7 +34,7 @@ const FlexedFooter = styled(Footer, {
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
export type LayoutProps = ComponentProps<typeof Flex> & {
|
||||
export type LayoutProps = ComponentPropsWithoutRef<typeof Flex> & {
|
||||
container?: boolean; // pass false to disable default `<main>` container styles with padding, etc.
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import NextLink from "next/link";
|
||||
import objStr from "obj-str";
|
||||
import { styled, theme, stitchesConfig } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const StyledLink = styled(NextLink, {
|
||||
color: theme.colors.link,
|
||||
@@ -34,7 +34,7 @@ const StyledLink = styled(NextLink, {
|
||||
},
|
||||
});
|
||||
|
||||
export type LinkProps = ComponentProps<typeof StyledLink> & {
|
||||
export type LinkProps = ComponentPropsWithoutRef<typeof StyledLink> & {
|
||||
openInNewTab?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { styled, theme, keyframes } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Wrapper = styled("div", {
|
||||
display: "inline-block",
|
||||
@@ -20,7 +20,7 @@ const Box = styled("div", {
|
||||
backgroundColor: theme.colors.mediumLight,
|
||||
});
|
||||
|
||||
export type LoadingProps = ComponentProps<typeof Wrapper> & {
|
||||
export type LoadingProps = ComponentPropsWithoutRef<typeof Wrapper> & {
|
||||
width: number; // of entire container, in pixels
|
||||
boxes?: number; // total number of boxes (default: 3)
|
||||
timing?: number; // staggered timing between each box's pulse, in seconds (default: 0.1s)
|
||||
|
||||
@@ -3,7 +3,7 @@ import MenuItem from "../MenuItem";
|
||||
import ThemeToggle from "../ThemeToggle";
|
||||
import { styled } from "../../lib/styles/stitches.config";
|
||||
import { menuItems } from "../../lib/config/menu";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Wrapper = styled("ul", {
|
||||
display: "inline-flex",
|
||||
@@ -38,7 +38,7 @@ const Item = styled("li", {
|
||||
},
|
||||
});
|
||||
|
||||
export type MenuProps = ComponentProps<typeof Wrapper>;
|
||||
export type MenuProps = ComponentPropsWithoutRef<typeof Wrapper>;
|
||||
|
||||
const Menu = ({ ...rest }: MenuProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Link from "../Link";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
import type { NoteFrontMatter } from "../../types";
|
||||
|
||||
const Title = styled("h1", {
|
||||
@@ -22,7 +22,8 @@ const TitleLink = styled(Link, {
|
||||
color: theme.colors.text,
|
||||
});
|
||||
|
||||
export type NoteTitleProps = Pick<NoteFrontMatter, "slug" | "title" | "htmlTitle"> & ComponentProps<typeof Title>;
|
||||
export type NoteTitleProps = Pick<NoteFrontMatter, "slug" | "title" | "htmlTitle"> &
|
||||
ComponentPropsWithoutRef<typeof Title>;
|
||||
|
||||
const NoteTitle = ({ slug, title, htmlTitle, ...rest }: NoteTitleProps) => {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from "../Link";
|
||||
import { OctocatOcticon } from "../Icons";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const GitHubLink = styled(Link, {
|
||||
margin: "0 0.4em",
|
||||
@@ -19,7 +19,7 @@ const Octocat = styled(OctocatOcticon, {
|
||||
fill: "currentColor",
|
||||
});
|
||||
|
||||
export type OctocatLinkProps = Omit<ComponentProps<typeof GitHubLink>, "href"> & {
|
||||
export type OctocatLinkProps = Omit<ComponentPropsWithoutRef<typeof GitHubLink>, "href"> & {
|
||||
repo: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "../Link";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const Title = styled("h1", {
|
||||
marginTop: 0,
|
||||
@@ -19,7 +19,7 @@ const TitleLink = styled(Link, {
|
||||
color: theme.colors.text,
|
||||
});
|
||||
|
||||
export type PageTitleProps = ComponentProps<typeof Title>;
|
||||
export type PageTitleProps = ComponentPropsWithoutRef<typeof Title>;
|
||||
|
||||
const PageTitle = ({ children, ...rest }: PageTitleProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from "../Link";
|
||||
import Image from "../Image";
|
||||
import { styled, theme } from "../../lib/styles/stitches.config";
|
||||
import { authorName } from "../../lib/config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import selfieJpg from "../../public/static/images/selfie.jpg";
|
||||
|
||||
@@ -47,7 +47,7 @@ const Name = styled("span", {
|
||||
},
|
||||
});
|
||||
|
||||
export type SelfieProps = Omit<ComponentProps<typeof Link>, "href">;
|
||||
export type SelfieProps = Omit<ComponentPropsWithoutRef<typeof Link>, "href">;
|
||||
|
||||
const Selfie = ({ ...rest }: SelfieProps) => {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { keyframes, styled } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentProps } from "react";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
const BlackBox = styled("div", {
|
||||
width: "100%",
|
||||
@@ -31,7 +31,7 @@ const Underscore = styled("span", {
|
||||
animation: `${keyframes({ "40%": { opacity: 0 } })} 1s step-end infinite`,
|
||||
});
|
||||
|
||||
export type TerminalProps = ComponentProps<typeof BlackBox>;
|
||||
export type TerminalProps = ComponentPropsWithoutRef<typeof BlackBox>;
|
||||
|
||||
// a DOS-style terminal box with dynamic text
|
||||
const Terminal = ({ children: message, ...rest }: TerminalProps) => {
|
||||
|
||||
@@ -1,40 +1,47 @@
|
||||
import { useState } from "react";
|
||||
import { TwitterTweetEmbed } from "react-twitter-embed";
|
||||
import useUpdateEffect from "../../hooks/useUpdateEffect";
|
||||
import { useEffect, useRef } from "react";
|
||||
import Image from "next/image";
|
||||
import { Tweet } from "react-tweet";
|
||||
import useTheme from "../../hooks/useTheme";
|
||||
import { styled } from "../../lib/styles/stitches.config";
|
||||
import type { ComponentPropsWithoutRef, ElementRef } from "react";
|
||||
|
||||
const Wrapper = styled("div", {
|
||||
// reserve a moderate amount of space for the widget, it takes a while to load...
|
||||
minHeight: "300px",
|
||||
minHeight: "300px", // help with layout shift
|
||||
|
||||
"& .react-tweet-theme": {
|
||||
"--tweet-container-margin": "1.5rem auto",
|
||||
},
|
||||
});
|
||||
|
||||
export type TweetEmbedProps = {
|
||||
id: string;
|
||||
options?: Record<string, unknown>;
|
||||
export type TweetEmbedProps = ComponentPropsWithoutRef<typeof Tweet> & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const TweetEmbed = ({ id, options, className }: TweetEmbedProps) => {
|
||||
const TweetEmbed = ({ id, className, ...rest }: TweetEmbedProps) => {
|
||||
const containerRef = useRef<ElementRef<typeof Wrapper>>(null);
|
||||
const { activeTheme } = useTheme();
|
||||
const [widgetTheme, setWidgetTheme] = useState(activeTheme);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
setWidgetTheme(activeTheme === "dark" ? activeTheme : "light");
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
// setting 'data-theme' attribute of parent div changes the tweet's theme (no re-render necessary)
|
||||
containerRef.current.dataset.theme = activeTheme;
|
||||
}
|
||||
}, [activeTheme]);
|
||||
|
||||
return (
|
||||
<Wrapper className={className}>
|
||||
<TwitterTweetEmbed
|
||||
tweetId={id}
|
||||
options={{
|
||||
dnt: true,
|
||||
align: "center",
|
||||
theme: widgetTheme,
|
||||
...options,
|
||||
<Wrapper ref={containerRef} className={className}>
|
||||
<Tweet
|
||||
key={`tweet-${id}`}
|
||||
id={id}
|
||||
apiUrl={`/api/tweet/?id=${id}`} // edge function at pages/api/tweet.ts
|
||||
components={{
|
||||
// https://react-tweet.vercel.app/twitter-theme/api-reference#custom-tweet-components
|
||||
// eslint-disable-next-line jsx-a11y/alt-text
|
||||
AvatarImg: (props) => <Image {...props} />,
|
||||
// eslint-disable-next-line jsx-a11y/alt-text
|
||||
MediaImg: (props) => <Image {...props} fill />,
|
||||
}}
|
||||
// changing this key forces the iframe URL to reformulate itself and update the theme:
|
||||
key={`tweet-${id}-${widgetTheme}`}
|
||||
{...rest}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import RFB from "@novnc/novnc/core/rfb";
|
||||
import Terminal from "../Terminal";
|
||||
import { styled } from "../../lib/styles/stitches.config";
|
||||
import type { Ref, ComponentPropsWithoutRef } from "react";
|
||||
import type { Ref, ComponentPropsWithoutRef, ElementRef } from "react";
|
||||
|
||||
const Display = styled(
|
||||
"div",
|
||||
@@ -50,7 +50,7 @@ const VNC = ({ server, style, ...rest }: VNCProps, ref: Ref<Partial<RFB>>) => {
|
||||
|
||||
// the actual connection and virtual screen (injected by noVNC when it's ready)
|
||||
const rfbRef = useRef<RFB | null>(null);
|
||||
const screenRef = useRef<HTMLDivElement>(null);
|
||||
const displayRef = useRef<ElementRef<typeof Display>>(null);
|
||||
|
||||
// ends the session forcefully
|
||||
const disconnect = useCallback(() => {
|
||||
@@ -121,7 +121,7 @@ const VNC = ({ server, style, ...rest }: VNCProps, ref: Ref<Partial<RFB>>) => {
|
||||
setLoaded(true);
|
||||
|
||||
// https://github.com/novnc/noVNC/blob/master/docs/API.md
|
||||
rfbRef.current = new RFB(screenRef.current as Element, server, {
|
||||
rfbRef.current = new RFB(displayRef.current as Element, server, {
|
||||
wsProtocols: ["binary", "base64"],
|
||||
});
|
||||
|
||||
@@ -164,7 +164,7 @@ const VNC = ({ server, style, ...rest }: VNCProps, ref: Ref<Partial<RFB>>) => {
|
||||
|
||||
{/* the VNC canvas is hidden until we've successfully connected to the socket */}
|
||||
<Display
|
||||
ref={screenRef}
|
||||
ref={displayRef}
|
||||
style={{
|
||||
display: !connected ? "none" : undefined,
|
||||
...style,
|
||||
|
||||
Reference in New Issue
Block a user