Skip to content

Commit

Permalink
v2.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AlemTuzlak committed Sep 13, 2023
1 parent de26962 commit bf83dbc
Show file tree
Hide file tree
Showing 21 changed files with 213 additions and 93 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Remix Development Tools is an open-source package designed to enhance your devel

## What's new?

## v2.4.0
- Route boundaries locked behind a feature flag
- Panel position settings allow you to change the panel position to either top or bottom of the screen
- ErrorBoundaries are more precise now
## v2.3.0
Route tree view support!
View all your routes in a node tree! You can also open routes in your browser from the tree view.
Expand Down Expand Up @@ -53,7 +57,7 @@ Key features include:
- **URL Parameters**: Easily view and analyze the URL parameters associated with the current page.
- **Server Responses**: Inspect and review the server responses received by the application for the current page.
- **Loader Data**: Monitor and track the loader data used by the application during page rendering.
- **Outlet boundaries** Activate the **Show Route Boundaries** option to see each Outlet and route boundaries by coloring the background. This needs to make a GET request to the current route when the dev tools are mounted for the first time to work properly and hence it is locked behind a flag. You can enable it by passing `showRouteBoundaries` prop to `true` in the `RemixDevTools` component.
- **Outlet boundaries** Activate the **Show Route Boundaries** option to see each Outlet and route boundaries by coloring the background. It is locked behind a flag. You can enable it by passing `useRouteBoundaries` prop to `true` in the `RemixDevTools`,first parameter of `initClient` set to `true` and second parameter of `initServer` set to `true`. This feature is experimental and can cause issues in certain scenarios. It will be considered stable with v3.0 release but until then use at your own risk.

### Routes Tab

Expand Down Expand Up @@ -218,6 +222,7 @@ export default function App() {
The `RemixDevTools` component accepts the following props:
- `requireUrlFlag`: Requires rdt=true to be present in the URL search to open the Remix Development Tools. Defaults to `false`.
- `plugins`: Allows you to provide additional tabs (plugins) to the Remix Development Tools. Defaults to `[]`.
- `useRouteBoundaries`: Allows you to enable the route boundaries feature. Defaults to `false`. This feature is experimental and can cause issues in certain scenarios. It will be considered stable with v3.0 release but until then use at your own risk.


## Plugins
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "remix-development-tools",
"description": "Remix development tools.",
"author": "Alem Tuzlak",
"version": "2.3.1",
"version": "2.4.0",
"license": "MIT",
"keywords": [
"remix",
Expand Down
7 changes: 4 additions & 3 deletions src/RemixDevTools/RemixDevTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,21 @@ function useHydrated() {
export interface RemixDevToolsProps {
// Whether the dev tools require a url flag to be shown
requireUrlFlag?: boolean;

// Whether to use route boundaries to show them on the UI
useRouteBoundaries?: boolean;
// Additional tabs to add to the dev tools
plugins?: Tab[];
}

const RemixDevTools = ({ requireUrlFlag, plugins }: RemixDevToolsProps) => {
const RemixDevTools = ({ requireUrlFlag, plugins, useRouteBoundaries }: RemixDevToolsProps) => {
const hydrated = useHydrated();
const url = useLocation().search;

if (!hydrated) return null;
if (requireUrlFlag && !url.includes("rdt=true")) return null;

return (
<RDTContextProvider>
<RDTContextProvider useRouteBoundaries={useRouteBoundaries}>
<InjectedStyles />
<DevTools plugins={plugins} />
</RDTContextProvider>
Expand Down
25 changes: 24 additions & 1 deletion src/RemixDevTools/components/RouteInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export const RouteInfo = ({ route, className, openNewRoute, onClose }: RouteInfo
const { routeWildcards, routeViewMode } = settings;
const { hasWildcard, path, pathToOpen } = constructRoutePath(route, routeWildcards);
const isTreeView = routeViewMode === "tree";
const hasParentErrorBoundary =
route.errorBoundary.errorBoundaryId && route.errorBoundary.errorBoundaryId !== route.id;
const hasErrorBoundary = route.errorBoundary.hasErrorBoundary;
return (
<div className={clsx(className, "rdt-relative")}>
{isTreeView && (
Expand All @@ -42,7 +45,27 @@ export const RouteInfo = ({ route, className, openNewRoute, onClose }: RouteInfo
<div className="rdt-flex rdt-gap-2">
<Tag color={route.hasLoader ? "GREEN" : "RED"}>Loader</Tag>
<Tag color={route.hasAction ? "GREEN" : "RED"}>Action</Tag>
<Tag color={route.hasErrorBoundary ? "GREEN" : "RED"}>ErrorBoundary</Tag>

<div
className={clsx(
"rdt-flex rdt-gap-2 rdt-rounded-md rdt-border",
!hasErrorBoundary ? "rdt-border-red-500" : "rdt-border-green-500"
)}
>
<Tag
className={clsx(hasErrorBoundary && "rdt-rounded-br-none rdt-rounded-tr-none")}
color={hasErrorBoundary ? "GREEN" : "RED"}
>
ErrorBoundary
</Tag>
{hasErrorBoundary ? (
<div className="rdt-mr-2">
{hasParentErrorBoundary
? `Covered by parent ErrorBoundary: ${route.errorBoundary.errorBoundaryId}`
: "Contains ErrorBoundary"}
</div>
) : null}
</div>
</div>
</div>
{hasWildcard && (
Expand Down
2 changes: 1 addition & 1 deletion src/RemixDevTools/components/RouteNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const RouteNode = ({
const parent = hierarchyPointNode.parent?.data;
const parentName = parent && parent?.name !== "/" ? parent.name : "";
const name = nodeDatum.name.replace(parentName, "") ?? "/";
const route = nodeDatum.attributes as any as ExtendedRoute;
const route = { ...nodeDatum, ...nodeDatum.attributes } as any as ExtendedRoute;
return (
<g className="rdt-flex">
<circle
Expand Down
9 changes: 4 additions & 5 deletions src/RemixDevTools/components/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,7 @@ const SelectLabel = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn(
"rdt-py-1.5 rdt-pl-8 rdt-pr-2 rdt-font-sans rdt-text-sm",
className
)}
className={cn("rdt-py-1.5 rdt-pl-8 rdt-pr-2 rdt-font-sans rdt-text-sm", className)}
{...props}
/>
));
Expand Down Expand Up @@ -119,16 +116,18 @@ const SelectWithOptions = <T extends string>({
onSelect,
hint,
value,
className,
}: {
placeholder?: string;
value?: T;
label?: string;
hint?: string;
options: { value: T; label: string }[];
onSelect: (value: T) => void;
className?: string;
}) => {
return (
<Stack gap="sm">
<Stack className={className} gap="sm">
{label && <Label>{label}</Label>}
<Select value={value} onValueChange={onSelect}>
<SelectTrigger className="rdt-w-full">
Expand Down
12 changes: 8 additions & 4 deletions src/RemixDevTools/context/RDTContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ RDTContext.displayName = "RDTContext";

interface ContextProps {
children: React.ReactNode;
useRouteBoundaries?: boolean;
}

export const setIsDetachedIfRequired = () => {
Expand Down Expand Up @@ -66,21 +67,24 @@ export const getSettings = () => {
};
};

export const getExistingStateFromStorage = (): RemixDevToolsState => {
export const getExistingStateFromStorage = (useRouteBoundaries?: boolean) => {
const existingState = getStorageItem(REMIX_DEV_TOOLS_STATE);
const settings = getSettings();
const { detachedWindow, detachedWindowOwner } = detachedModeSetup();
return {
const state: RemixDevToolsState = {
...initialState,
...(existingState ? JSON.parse(existingState) : {}),
settings,
useRouteBoundaries,
detachedWindow,
detachedWindowOwner,
};

return state;
};

export const RDTContextProvider = ({ children }: ContextProps) => {
const [state, dispatch] = useReducer(rdtReducer, getExistingStateFromStorage());
export const RDTContextProvider = ({ children, useRouteBoundaries }: ContextProps) => {
const [state, dispatch] = useReducer<typeof rdtReducer>(rdtReducer, getExistingStateFromStorage(useRouteBoundaries));
const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);
useListenToRouteChange();
useRemoveBody(state);
Expand Down
4 changes: 4 additions & 0 deletions src/RemixDevTools/context/rdtReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export type RemixDevToolsState = {
hoveredRoute: string;
isHoveringRoute: boolean;
routeViewMode: "list" | "tree";
panelLocation: "top" | "bottom";
};
useRouteBoundaries: boolean;
persistOpen: boolean;
detachedWindow: boolean;
detachedWindowOwner: boolean;
Expand All @@ -65,10 +67,12 @@ export const initialState: RemixDevToolsState = {
hoveredRoute: "",
isHoveringRoute: false,
routeViewMode: "tree",
panelLocation: "bottom",
},
persistOpen: false,
detachedWindow: false,
detachedWindowOwner: false,
useRouteBoundaries: false,
};

export type ReducerActions = Pick<RemixDevToolsActions, "type">["type"];
Expand Down
6 changes: 5 additions & 1 deletion src/RemixDevTools/hooks/useOutletAugment.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useEffect } from "react";
import { InvisibleBoundary } from "../init/project";
import { useRDTContext } from "../context/useRDTContext";

const isHooked = Symbol("isHooked") as any;

export function useOutletAugment() {
// TODO Remove once stabilized
const { state } = useRDTContext();
useEffect(() => {
if (!state.useRouteBoundaries) return;
if (window.__remixRouteModules[isHooked]) return;

window.__remixRouteModules = new Proxy(window.__remixRouteModules, {
Expand Down Expand Up @@ -35,5 +39,5 @@ export function useOutletAugment() {
return value;
},
});
}, []);
}, [state.useRouteBoundaries]);
}
6 changes: 3 additions & 3 deletions src/RemixDevTools/hooks/useResize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useSettingsContext } from "../context/useRDTContext";

const useResize = () => {
const { setSettings, settings } = useSettingsContext();
const { height, maxHeight, minHeight } = settings;
const { height, maxHeight, minHeight, panelLocation } = settings;
const [isResizing, setIsResizing] = useState(false);

const enableResize = useCallback(() => {
Expand All @@ -18,7 +18,7 @@ const useResize = () => {
(e: MouseEvent) => {
if (isResizing) {
window.getSelection()?.removeAllRanges(); // Prevent text selection
const newHeight = window.innerHeight - e.clientY; // Calculate the new height based on the mouse position
const newHeight = panelLocation === "top" ? e.clientY : window.innerHeight - e.clientY; // Calculate the new height based on the mouse position

//const newHeight = e.clientY; // You may want to add some offset here from props

Expand All @@ -35,7 +35,7 @@ const useResize = () => {
setSettings({ height: newHeight });
}
},
[isResizing, maxHeight, minHeight, setSettings]
[isResizing, maxHeight, minHeight, setSettings, panelLocation]
);

useEffect(() => {
Expand Down
15 changes: 13 additions & 2 deletions src/RemixDevTools/hooks/useSetRouteBoundaries.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { useCallback, useEffect } from "react";
import { ROUTE_BOUNDARY_GRADIENTS } from "../context/rdtReducer";
import { useSettingsContext, useDetachedWindowControls } from "../context/useRDTContext";
import { useSettingsContext, useDetachedWindowControls, useRDTContext } from "../context/useRDTContext";
import { useAttachListener } from "./useAttachListener";

export const useSetRouteBoundaries = () => {
const { settings, setSettings } = useSettingsContext();
const { detachedWindow } = useDetachedWindowControls();
// TODO Remove once stabilized
const { state } = useRDTContext();
const applyOrRemoveClasses = useCallback(
(isHovering?: boolean) => {
// TODO Remove once stabilized
if (!state.useRouteBoundaries) return;
// Overrides the hovering so the classes are force removed if needed
const hovering = isHovering ?? settings.isHoveringRoute;
// Classes to apply/remove
Expand All @@ -32,10 +36,12 @@ export const useSetRouteBoundaries = () => {
}
}
},
[settings.hoveredRoute, settings.isHoveringRoute, settings.routeBoundaryGradient]
[settings.hoveredRoute, state.useRouteBoundaries, settings.isHoveringRoute, settings.routeBoundaryGradient]
);
// Mouse left the document => remove classes => set isHovering to false so that detached mode removes as well
useAttachListener("mouseleave", "document", () => {
// TODO Remove once stabilized
if (!state.useRouteBoundaries) return;
applyOrRemoveClasses();
if (!detachedWindow) {
return;
Expand All @@ -46,6 +52,8 @@ export const useSetRouteBoundaries = () => {
});
// Mouse is scrolling => remove classes => set isHovering to false so that detached mode removes as well
useAttachListener("wheel", "window", () => {
// TODO Remove once stabilized
if (!state.useRouteBoundaries) return;
applyOrRemoveClasses(false);
if (!detachedWindow) {
return;
Expand All @@ -56,6 +64,8 @@ export const useSetRouteBoundaries = () => {
});
// We apply/remove classes on state change which happens in Page tab
useEffect(() => {
// TODO Remove once stabilized
if (!state.useRouteBoundaries) return;
if (!settings.isHoveringRoute && !settings.hoveredRoute) return;
applyOrRemoveClasses();
if (!settings.isHoveringRoute && !detachedWindow) {
Expand All @@ -65,6 +75,7 @@ export const useSetRouteBoundaries = () => {
});
}
}, [
state.useRouteBoundaries,
settings.hoveredRoute,
settings.isHoveringRoute,
settings.routeBoundaryGradient,
Expand Down
6 changes: 4 additions & 2 deletions src/RemixDevTools/init/project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export const InvisibleBoundary = ({ path }: { path: string }) => {
return <div className={clsx("rdt-invisible rdt-absolute rdt-hidden rdt-h-0 rdt-w-0", path)} />;
};

export const initServer = (context: EntryContext) => {
export const initServer = (context: EntryContext, useRouteBoundaries = false) => {
if (!useRouteBoundaries) return context;
return {
...context,
routeModules: Object.entries(context.routeModules).reduce((acc, [key, value]) => {
Expand Down Expand Up @@ -33,7 +34,8 @@ export const initServer = (context: EntryContext) => {
};
};

export const initClient = () => {
export const initClient = (useRouteBoundaries = false) => {
if (!useRouteBoundaries) return;
window.__remixRouteModules = Object.keys(window.__remixRouteModules).reduce((acc, key) => {
const value = window.__remixRouteModules[key];
if (key === "root") {
Expand Down
36 changes: 26 additions & 10 deletions src/RemixDevTools/layout/MainPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const useResizeDetachedPanel = () => {
const MainPanel = ({ children, isOpen, isEmbedded = false, className }: MainPanelProps) => {
const { settings } = useSettingsContext();
const { detachedWindow } = useDetachedWindowControls();
const { height } = settings;
const { height, panelLocation } = settings;
const { enableResize, disableResize, isResizing } = useResize();
useResizeDetachedPanel();

Expand All @@ -38,19 +38,35 @@ const MainPanel = ({ children, isOpen, isEmbedded = false, className }: MainPane
"rdt-duration-600 rdt-box-border rdt-flex rdt-w-screen rdt-flex-col rdt-overflow-auto rdt-bg-[#212121] rdt-text-white rdt-opacity-0 rdt-transition-all",
isOpen ? "rdt-opacity-100 rdt-drop-shadow-2xl" : "rdt-h-0",
isResizing && "rdt-cursor-grabbing ",
!isEmbedded && "rdt-fixed rdt-bottom-0 rdt-left-0",
!isEmbedded
? `rdt-fixed rdt-left-0 ${
panelLocation === "bottom" ? "rdt-bottom-0" : "rdt-top-0 rdt-border-b-2 rdt-border-[#212121]"
}`
: "",
className
)}
>
<div
onMouseDown={enableResize}
onMouseUp={disableResize}
className={clsx(
"rdt-absolute rdt-z-50 rdt-h-1 rdt-w-full",
isResizing ? "rdt-cursor-grabbing" : "rdt-cursor-ns-resize"
)}
/>
{panelLocation === "bottom" && (
<div
onMouseDown={enableResize}
onMouseUp={disableResize}
className={clsx(
"rdt-absolute rdt-z-50 rdt-h-1 rdt-w-full",
isResizing ? "rdt-cursor-grabbing" : "rdt-cursor-ns-resize"
)}
/>
)}
{children}
{panelLocation === "top" && (
<div
onMouseDown={enableResize}
onMouseUp={disableResize}
className={clsx(
"rdt-absolute rdt-bottom-0 rdt-z-50 rdt-h-1 rdt-w-full",
isResizing ? "rdt-cursor-grabbing" : "rdt-cursor-ns-resize"
)}
/>
)}
</div>
);
};
Expand Down
Loading

0 comments on commit bf83dbc

Please sign in to comment.