From 6b19c566ec266ac3bc2c50c97dda4c7751b647b8 Mon Sep 17 00:00:00 2001 From: Pierre Berger Date: Sat, 3 Feb 2024 19:02:58 +0100 Subject: [PATCH 1/5] feat(resources): add ability to choose package manager --- app/lib/transformNpmCommand.ts | 21 ++++++ app/styles/resources.css | 2 +- app/ui/details-menu.tsx | 22 +++++- app/ui/resources.tsx | 128 +++++++++++++++++++++++++++------ 4 files changed, 146 insertions(+), 27 deletions(-) create mode 100644 app/lib/transformNpmCommand.ts diff --git a/app/lib/transformNpmCommand.ts b/app/lib/transformNpmCommand.ts new file mode 100644 index 00000000..0f9657ac --- /dev/null +++ b/app/lib/transformNpmCommand.ts @@ -0,0 +1,21 @@ +export type PackageManager = "npm" | "yarn" | "pnpm" | "bun"; + +export function transformNpmCommand( + prefix: string, + cmd: string, + packageManagerTarget: "yarn" | "bun" | "pnpm" | "npm", +) { + if (prefix === "npm") { + if (cmd.split(" ")[0] === "install" && packageManagerTarget === "yarn") { + return `${packageManagerTarget} ${cmd.replace("install", "add")}`; + } + return `${packageManagerTarget} ${cmd}`; + } + switch (packageManagerTarget) { + case "bun": + return `bunx ${cmd}`; + case "pnpm": + return `pnpm dlx ${cmd}`; + } + return `${prefix} ${cmd}`; +} diff --git a/app/styles/resources.css b/app/styles/resources.css index 936ff927..c5b0f00e 100644 --- a/app/styles/resources.css +++ b/app/styles/resources.css @@ -41,7 +41,7 @@ } & [data-code-block-copy] { - @apply absolute right-4 top-[1.125rem] h-5 w-5 cursor-pointer bg-white/80 opacity-0 dark:bg-gray-900/80; + @apply absolute top-[1.125rem] h-5 w-5 cursor-pointer bg-white/80 opacity-0 dark:bg-gray-900/80; } &:hover [data-code-block-copy], diff --git a/app/ui/details-menu.tsx b/app/ui/details-menu.tsx index a5c012ec..fc9b9f95 100644 --- a/app/ui/details-menu.tsx +++ b/app/ui/details-menu.tsx @@ -1,5 +1,7 @@ import { forwardRef, useState, useRef, useEffect } from "react"; +import type { PropsWithChildren } from "react"; import { useLocation, useNavigation } from "@remix-run/react"; +import cx from "clsx"; /** * An enhanced `
` component that's intended to be used as a menu (a bit @@ -77,10 +79,24 @@ export const DetailsMenu = forwardRef< }); DetailsMenu.displayName = "DetailsMenu"; -export function DetailsPopup({ children }: { children: React.ReactNode }) { +type DetailsPopupProps = { + className?: string; + childrenClassName?: string; +}; + +export function DetailsPopup({ + children, + className = "", + childrenClassName = "", +}: PropsWithChildren) { return ( -
-
+
+
{children}
diff --git a/app/ui/resources.tsx b/app/ui/resources.tsx index 03845322..65b3de17 100644 --- a/app/ui/resources.tsx +++ b/app/ui/resources.tsx @@ -1,6 +1,10 @@ import { useEffect, useState } from "react"; import { type Resource } from "~/lib/resources.server"; -import { Link, useSearchParams } from "@remix-run/react"; +import { transformNpmCommand } from "~/lib/transformNpmCommand"; +import type { PackageManager } from "~/lib/transformNpmCommand"; +import { DetailsMenu, DetailsPopup } from "./details-menu"; + +import { Form, Link, useSearchParams, useSubmit } from "@remix-run/react"; import cx from "clsx"; import iconsHref from "~/icons.svg"; @@ -67,6 +71,13 @@ export function InitCodeblock({ let [npxOrNpmMaybe, ...otherCode] = initCommand.trim().split(" "); let [copied, setCopied] = useState(false); + function handleCopied(copied: boolean, packageManager: PackageManager) { + setCopied(copied); + navigator.clipboard.writeText( + transformNpmCommand(npxOrNpmMaybe, otherCode.join(" "), packageManager), + ); + } + // Reset copied state after 4 seconds useEffect(() => { if (copied) { @@ -106,30 +117,101 @@ export function InitCodeblock({ -
+ ); +} + +type CopyCodeBlockProps = { + copied: boolean; + setCopied: (copied: boolean, packageManager: PackageManager) => void; +}; + +function CopyCodeBlock({ copied, setCopied }: CopyCodeBlockProps) { + const [isMenuOpen, setIsMenuOpen] = useState(false); + const [searchParams] = useSearchParams(); + + return ( + setIsMenuOpen((oldValue) => !oldValue)} + > + - {/* had to put these here instead of as a mask so we could add an opacity */} - - {copied ? ( - - ) : ( - - )} - - Copy code to clipboard - -
+ + {copied ? ( + + ) : ( + + )} + + Copy code to clipboard + + + +
+ + + setCopied(copied, "npm")} + /> + setCopied(copied, "yarn")} + /> + setCopied(copied, "pnpm")} + /> + setCopied(copied, "bun")} + /> + +
+ + ); +} + +type PackageManagerButtonProps = { + packageManager: PackageManager; + setCopied: (copied: boolean) => void; +}; + +function PackageManagerButton({ + packageManager, + setCopied, +}: PackageManagerButtonProps) { + const submit = useSubmit(); + return ( + ); } From 5cf5d08aecfa5b6f24f9e71a53d8d6f499cbf6af Mon Sep 17 00:00:00 2001 From: Pierre Berger Date: Thu, 8 Feb 2024 21:48:26 +0100 Subject: [PATCH 2/5] fix: dropdown position --- app/ui/resources.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/ui/resources.tsx b/app/ui/resources.tsx index 65b3de17..313bdce9 100644 --- a/app/ui/resources.tsx +++ b/app/ui/resources.tsx @@ -160,7 +160,10 @@ function CopyCodeBlock({ copied, setCopied }: CopyCodeBlockProps) { Copy code to clipboard - +
Date: Fri, 9 Feb 2024 17:52:22 +0100 Subject: [PATCH 3/5] style: update button position --- app/ui/resources.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/ui/resources.tsx b/app/ui/resources.tsx index 313bdce9..4ff96d7b 100644 --- a/app/ui/resources.tsx +++ b/app/ui/resources.tsx @@ -162,9 +162,9 @@ function CopyCodeBlock({ copied, setCopied }: CopyCodeBlockProps) { - + { + onClick={() => { submit({ preventScrollReset: false, }); From 8f2f1cc95521da0c2cd08af15f3cc6ba663062a7 Mon Sep 17 00:00:00 2001 From: Brooks Lybrand Date: Wed, 14 Feb 2024 16:27:31 -0600 Subject: [PATCH 4/5] Refactor to get a11y back and simplify code --- app/styles/resources.css | 4 +- app/ui/details-menu.tsx | 14 +++-- app/ui/resources.tsx | 110 +++++++++++++-------------------------- 3 files changed, 43 insertions(+), 85 deletions(-) diff --git a/app/styles/resources.css b/app/styles/resources.css index c5b0f00e..56a2d8a5 100644 --- a/app/styles/resources.css +++ b/app/styles/resources.css @@ -41,11 +41,11 @@ } & [data-code-block-copy] { - @apply absolute top-[1.125rem] h-5 w-5 cursor-pointer bg-white/80 opacity-0 dark:bg-gray-900/80; + @apply absolute right-4 top-[1.125rem] h-5 w-5 cursor-pointer bg-white/80 opacity-0 dark:bg-gray-900/80; } &:hover [data-code-block-copy], - & [data-code-block-copy]:focus { + & [data-code-block-copy]:focus-within { @apply opacity-100; } diff --git a/app/ui/details-menu.tsx b/app/ui/details-menu.tsx index fc9b9f95..dbbbcff7 100644 --- a/app/ui/details-menu.tsx +++ b/app/ui/details-menu.tsx @@ -81,21 +81,19 @@ DetailsMenu.displayName = "DetailsMenu"; type DetailsPopupProps = { className?: string; - childrenClassName?: string; }; export function DetailsPopup({ children, - className = "", - childrenClassName = "", + className, }: PropsWithChildren) { return ( -
+ // TODO: remove the width being defined here. Seems like it should be a min-width: fit-content or something, and anything more specific defined by the children passed in (or a className) +
{children}
diff --git a/app/ui/resources.tsx b/app/ui/resources.tsx index 4ff96d7b..601a0630 100644 --- a/app/ui/resources.tsx +++ b/app/ui/resources.tsx @@ -4,7 +4,7 @@ import { transformNpmCommand } from "~/lib/transformNpmCommand"; import type { PackageManager } from "~/lib/transformNpmCommand"; import { DetailsMenu, DetailsPopup } from "./details-menu"; -import { Form, Link, useSearchParams, useSubmit } from "@remix-run/react"; +import { Link, useSearchParams, useSubmit } from "@remix-run/react"; import cx from "clsx"; import iconsHref from "~/icons.svg"; @@ -70,12 +70,18 @@ export function InitCodeblock({ // Probably a more elegant solution, but this is what I've got let [npxOrNpmMaybe, ...otherCode] = initCommand.trim().split(" "); let [copied, setCopied] = useState(false); + const submit = useSubmit(); - function handleCopied(copied: boolean, packageManager: PackageManager) { - setCopied(copied); + function handleCopy(packageManager: PackageManager) { + setCopied(true); navigator.clipboard.writeText( transformNpmCommand(npxOrNpmMaybe, otherCode.join(" "), packageManager), ); + // This is a hack to close the details menu after clicking + submit(null, { + preventScrollReset: true, + replace: true, + }); } // Reset copied state after 4 seconds @@ -117,35 +123,24 @@ export function InitCodeblock({ - +
); } type CopyCodeBlockProps = { copied: boolean; - setCopied: (copied: boolean, packageManager: PackageManager) => void; + onCopy: (packageManager: PackageManager) => void; }; -function CopyCodeBlock({ copied, setCopied }: CopyCodeBlockProps) { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const [searchParams] = useSearchParams(); - +function CopyCodeBlock({ copied, onCopy }: CopyCodeBlockProps) { return ( - setIsMenuOpen((oldValue) => !oldValue)} - > + - + Copy code to clipboard - - - - - setCopied(copied, "npm")} - /> - setCopied(copied, "yarn")} - /> - setCopied(copied, "pnpm")} - /> - setCopied(copied, "bun")} - /> - - +
+ +
+ {(["npm", "yarn", "pnpm", "bun"] as const).map((packageManager) => ( + + ))} +
+
+
); } - -type PackageManagerButtonProps = { - packageManager: PackageManager; - setCopied: (copied: boolean) => void; -}; - -function PackageManagerButton({ - packageManager, - setCopied, -}: PackageManagerButtonProps) { - const submit = useSubmit(); - return ( - - ); -} From 715258cd1935285a248eee5ad6f1f32ea2f168a8 Mon Sep 17 00:00:00 2001 From: Brooks Lybrand Date: Thu, 15 Feb 2024 09:33:01 -0600 Subject: [PATCH 5/5] Adjust width override and fix submission hack for closing details --- app/routes/docs.$lang.$ref.tsx | 6 +++--- app/ui/details-menu.tsx | 16 ++++------------ app/ui/header.tsx | 2 +- app/ui/resources.tsx | 27 +++++++++++++-------------- 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/app/routes/docs.$lang.$ref.tsx b/app/routes/docs.$lang.$ref.tsx index d5e63890..8d5c908d 100644 --- a/app/routes/docs.$lang.$ref.tsx +++ b/app/routes/docs.$lang.$ref.tsx @@ -268,7 +268,7 @@ function VersionSelect() { -
+
{branches.map((branch) => { return ( @@ -390,7 +390,7 @@ function ColorSchemeToggle() { replace action="/_actions/color-scheme" method="post" - className="flex flex-col gap-px" + className="flex w-40 flex-col gap-px" > -
+
Docs Blog Showcase diff --git a/app/ui/details-menu.tsx b/app/ui/details-menu.tsx index dbbbcff7..eac4d956 100644 --- a/app/ui/details-menu.tsx +++ b/app/ui/details-menu.tsx @@ -1,5 +1,4 @@ import { forwardRef, useState, useRef, useEffect } from "react"; -import type { PropsWithChildren } from "react"; import { useLocation, useNavigation } from "@remix-run/react"; import cx from "clsx"; @@ -80,21 +79,14 @@ export const DetailsMenu = forwardRef< DetailsMenu.displayName = "DetailsMenu"; type DetailsPopupProps = { + children: React.ReactNode; className?: string; }; -export function DetailsPopup({ - children, - className, -}: PropsWithChildren) { +export function DetailsPopup({ children, className }: DetailsPopupProps) { return ( - // TODO: remove the width being defined here. Seems like it should be a min-width: fit-content or something, and anything more specific defined by the children passed in (or a className) -
-
+
+
{children}
diff --git a/app/ui/header.tsx b/app/ui/header.tsx index 664ddd31..0417e6e0 100644 --- a/app/ui/header.tsx +++ b/app/ui/header.tsx @@ -65,7 +65,7 @@ function HeaderMenuMobile({ className = "" }: { className: string }) { -