Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Compiler #75

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ jobs:
- name: Install dependencies
run: npm ci

- name: ESLint
run: npm run lint

- name: Prettier
run: ./node_modules/.bin/prettier --check --ignore-path .gitignore .
10 changes: 5 additions & 5 deletions app/components/copy.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { copyText } from "~/utils/copy";
import { DocumentDuplicateIcon } from "@heroicons/react/24/outline";
import { useCallback, useRef, useState } from "react";
import { useRef, useState } from "react";
import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/tooltip";

interface Props {
readonly content: string;
readonly content: string | (() => string);
}
export default function Copy({ content }: Props) {
const text = "Copy to clipboard";
const [tooltipText, setTooltipText] = useState(text);
const buttonRef = useRef<HTMLButtonElement>(null);

const onClick = useCallback(() => {
copyText(content)
const onClick = () => {
copyText(typeof content === "string" ? content : content())
.then(() => setTooltipText("Copied"))
.catch((e) => console.error("Could not copy to clipboard.", e));

Expand All @@ -23,7 +23,7 @@ export default function Copy({ content }: Props) {
// re-set tooltip text
setTooltipText(text);
}, 1500);
}, [content]);
};

return (
<Tooltip>
Expand Down
15 changes: 4 additions & 11 deletions app/components/dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
useCallback,
type ChangeEvent,
type SelectHTMLAttributes,
} from "react";
import { type ChangeEvent, type SelectHTMLAttributes } from "react";
import { classNames } from "~/common";

interface Props extends SelectHTMLAttributes<HTMLSelectElement> {
Expand All @@ -18,11 +14,6 @@ export default function Dropdown({
defaultValue,
value,
}: Props) {
const onChange = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => onOptionChange(e.target.value),
[onOptionChange],
);

return (
<select
id={id}
Expand All @@ -33,7 +24,9 @@ export default function Dropdown({
)}
defaultValue={defaultValue}
value={value}
onChange={onChange}
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
onOptionChange(e.target.value)
}
>
{options.map((it) => (
<option key={it.id} value={it.id}>
Expand Down
43 changes: 18 additions & 25 deletions app/components/json-viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Copy from "~/components/copy";
import { useCallback, useMemo, useState } from "react";
import { useState } from "react";
import { JSONTree } from "react-json-tree";
import IconButton from "~/components/icon-button";
import {
Expand All @@ -19,33 +19,26 @@ export default function JsonViewer({ json }: Props) {
const [shouldExpand, setShouldExpand] = useState<boolean | null>(null);
const [expandAfter, setExpandAfter] = useState(3);

const shouldExpandNodeInitially = useCallback<ShouldExpandNodeInitially>(
(keyPath, data, level) => {
if (shouldExpand !== null) {
return shouldExpand;
}
const shouldExpandNodeInitially: ShouldExpandNodeInitially = (
keyPath,
data,
level,
) => {
if (shouldExpand !== null) {
return shouldExpand;
}

return level < expandAfter;
},
[shouldExpand, expandAfter],
);

const expandAll = useCallback(() => setShouldExpand(true), [setShouldExpand]);
const collapseALl = useCallback(
() => setShouldExpand(false),
[setShouldExpand],
);
return level < expandAfter;
};

const incrementExpandAfter = useCallback(() => {
const incrementExpandAfter = () => {
setExpandAfter(Math.min(10, expandAfter + 1));
setShouldExpand(null);
}, [expandAfter]);
const decrementExpandAfter = useCallback(() => {
};
const decrementExpandAfter = () => {
setExpandAfter(Math.max(0, expandAfter - 1));
setShouldExpand(null);
}, [expandAfter]);

const serialized = useMemo(() => JSON.stringify(json), [json]);
};

return (
<Box>
Expand All @@ -68,16 +61,16 @@ export default function JsonViewer({ json }: Props) {
<IconButton
icon={ArrowsPointingInIcon}
label="Collapse all nodes"
onClick={collapseALl}
onClick={() => setShouldExpand(false)}
/>
<IconButton
icon={ArrowsPointingOutIcon}
label="Expand all nodes"
onClick={expandAll}
onClick={() => setShouldExpand(true)}
/>
</div>
<div className="flex flex-wrap items-center space-x-1 sm:pl-4">
<Copy content={serialized} />
<Copy content={() => JSON.stringify(json)} />
</div>
</div>
</BoxTitle>
Expand Down
25 changes: 7 additions & 18 deletions app/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import React, {
Fragment,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import React, { Fragment, useEffect, useRef, useState } from "react";
import {
Bars3BottomLeftIcon,
MagnifyingGlassIcon,
Expand All @@ -31,14 +25,6 @@
const location = useLocation();
const prevPath = useRef(location.pathname);

const toggleSidebar = useCallback(() => {
setSidebarOpen(!sidebarOpen);
}, [sidebarOpen, setSidebarOpen]);

const toggleSearch = useCallback(() => {
setSearchOpen(!searchOpen);
}, [searchOpen, setSearchOpen]);

// if url changes while the sidebar is open, close the sidebar
useEffect(() => {
if (sidebarOpen && prevPath.current !== location.pathname) {
Expand All @@ -49,6 +35,7 @@
}, [location.pathname, sidebarOpen, setSidebarOpen]);

// on command + k, open search bar
const toggleSearch = () => setSearchOpen(!searchOpen);
useKeyboardShortcut(["Meta", "K"], toggleSearch, keyboardShortcutOptions);
useKeyboardShortcut(["Control", "K"], toggleSearch, keyboardShortcutOptions);

Expand All @@ -73,14 +60,15 @@
<div className="hidden lg:block lg:max-w-md lg:flex-auto">
<button
type="button"
test-id="search-box"

Check failure on line 63 in app/components/layout.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unknown property 'test-id' found
className="hidden h-8 w-full items-center gap-2 rounded-full pl-2 pr-3 text-sm ring-1 transition bg-white/5 text-zinc-400 ring-inset ring-white/10 hover:ring-white/20 lg:flex outline-hidden"
onClick={toggleSearch}
>
<MagnifyingGlassIcon
className="h-5 w-5 stroke-current"
aria-hidden="true"
/>
Find something...
Find something
<kbd className="ml-auto text-2xs text-zinc-500">
<kbd className="font-sans">⌘</kbd>
<kbd className="font-sans">K</kbd>
Expand All @@ -92,7 +80,7 @@
type="button"
className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-white/5"
aria-label="Toggle navigation"
onClick={toggleSidebar}
onClick={() => setSidebarOpen(!sidebarOpen)}
>
{sidebarOpen ? (
<XMarkIcon
Expand All @@ -118,8 +106,9 @@
<div className="contents lg:hidden">
<button
type="button"
test-id="search-box"

Check failure on line 109 in app/components/layout.tsx

View workflow job for this annotation

GitHub Actions / Lint

Unknown property 'test-id' found
className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-white/5 lg:hidden focus:not-focus-visible:outline-hidden"
aria-label="Find something..."
aria-label="Find something"
onClick={toggleSearch}
>
<MagnifyingGlassIcon
Expand Down
6 changes: 2 additions & 4 deletions app/components/popular-utilities.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Link } from "react-router";
import { ArrowRightIcon } from "@heroicons/react/20/solid";
import { memo } from "react";
import { utilities } from "~/utilities";
import { ClientOnly } from "~/components/client-only";

Expand All @@ -20,16 +19,14 @@ function shuffle<T>(arr: T[]) {
return arr;
}

export default memo(PopularUtilities);

/**
* Since we are using a random() function to show popular utilities, react router fails to match server side
* rendered content with what the client does and throws a bunch of errors. We wrap it with <ClientOnly />
* to only render fully on the client side.
*
* @constructor
*/
function PopularUtilities() {
export default function PopularUtilities() {
return (
<ClientOnly
fallback={
Expand Down Expand Up @@ -69,6 +66,7 @@ interface PopularUtilityProps {
readonly description: string;
readonly path: string;
}

function PopularUtility({ name, description, path }: PopularUtilityProps) {
return (
<div>
Expand Down
48 changes: 20 additions & 28 deletions app/components/read-file.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ChangeEvent } from "react";
import { useCallback } from "react";
import { PaperClipIcon } from "@heroicons/react/24/outline";
import IconButton from "~/components/icon-button";

Expand All @@ -17,45 +16,38 @@ export default function ReadFile({
onLoad,
onError,
}: Props) {
const onButtonClick = useCallback(
() => document.getElementById("file-input")?.click(),
[],
);
const onChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
const files = e.target.files || [];
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
const files = e.target.files || [];

// stop, no file selected
if (files.length === 0) {
return;
}
// stop, no file selected
if (files.length === 0) {
return;
}

const filesArray = Array.from(files);
const filesArray = Array.from(files);

// ensure all files are under the size limit
filesArray.forEach((file) => {
const maxAllowedSize = 10 * 1024 * 1024;
// ensure all files are under the size limit
filesArray.forEach((file) => {
const maxAllowedSize = 10 * 1024 * 1024;

// stop if we are passed a certain limit
if (file.size > maxAllowedSize) {
if (onError) {
onError(`File is too large. Max size is ${maxAllowedSize} bytes.`);
}
return;
// stop if we are passed a certain limit
if (file.size > maxAllowedSize) {
if (onError) {
onError(`File is too large. Max size is ${maxAllowedSize} bytes.`);
}
});
return;
}
});

onLoad(filesArray);
},
[onLoad, onError],
);
onLoad(filesArray);
};

return (
<>
<IconButton
icon={PaperClipIcon}
label="Load file"
onClick={onButtonClick}
onClick={() => document.getElementById("file-input")?.click()}
tooltipPlacement="bottom"
/>
<input
Expand Down
Loading
Loading