Skip to content
Merged
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
116 changes: 0 additions & 116 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,122 +193,6 @@ actions: [

See [`packages/react-grab/src/types.ts`](https://github.com/aidenybai/react-grab/blob/main/packages/react-grab/src/types.ts) for the full `Plugin`, `PluginHooks`, and `PluginConfig` interfaces.

## Primitives

Use primitives to build your own element selector from scratch. Unlike plugins, primitives are standalone utility functions that don't depend on React Grab being initialized.

If you're using primitives to build a custom UI and don't want the default React Grab overlay, disable auto-initialization before importing `react-grab`:

```html
<script>
window.__REACT_GRAB_DISABLED__ = true;
</script>
```

Here's a simple example of how to build your own element selector with hover highlight and one-click inspection:

```bash
npm install react-grab@latest
```

```tsx
import { useState } from "react";
import {
getElementContext,
freeze,
unfreeze,
openFile,
type ReactGrabElementContext,
} from "react-grab/primitives";

const useElementSelector = (
onSelect: (context: ReactGrabElementContext) => void,
) => {
const [isActive, setIsActive] = useState(false);

const startSelecting = () => {
setIsActive(true);

const highlightOverlay = document.createElement("div");
Object.assign(highlightOverlay.style, {
position: "fixed",
pointerEvents: "none",
zIndex: "999999",
border: "2px solid #3b82f6",
transition: "all 75ms ease-out",
display: "none",
});
document.body.appendChild(highlightOverlay);

const handleMouseMove = ({ clientX, clientY }: MouseEvent) => {
highlightOverlay.style.display = "none";
const target = document.elementFromPoint(clientX, clientY);
if (!target) return;
const { top, left, width, height } = target.getBoundingClientRect();
Object.assign(highlightOverlay.style, {
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${height}px`,
display: "block",
});
};

const handleClick = async ({ clientX, clientY }: MouseEvent) => {
highlightOverlay.style.display = "none";
const target = document.elementFromPoint(clientX, clientY);
teardown();
if (!target) return;
freeze();
onSelect(await getElementContext(target));
unfreeze();
};

const teardown = () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("click", handleClick, true);
highlightOverlay.remove();
setIsActive(false);
};

document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("click", handleClick, true);
};

return { isActive, startSelecting };
};

const ElementSelector = () => {
const [context, setContext] = useState<ReactGrabElementContext | null>(null);
const selector = useElementSelector(setContext);

return (
<div>
<button onClick={selector.startSelecting} disabled={selector.isActive}>
{selector.isActive ? "Selecting…" : "Select Element"}
</button>
{context && (
<div>
<p>Component: {context.componentName}</p>
<p>Selector: {context.selector}</p>
<pre>{context.stackString}</pre>
<button
onClick={() => {
const frame = context.stack[0];
if (frame?.fileName) openFile(frame.fileName, frame.lineNumber);
}}
>
Open in Editor
</button>
</div>
)}
</div>
);
};
```

See [`packages/react-grab/src/primitives.ts`](https://github.com/aidenybai/react-grab/blob/main/packages/react-grab/src/primitives.ts) for the full `ReactGrabElementContext`, `getElementContext`, `freeze`, `unfreeze`, and `openFile` primitives.

## Resources & Contributing Back

Want to try it out? Check out [our demo](https://react-grab.com).
Expand Down
116 changes: 0 additions & 116 deletions packages/grab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,122 +193,6 @@ actions: [

See [`packages/react-grab/src/types.ts`](https://github.com/aidenybai/react-grab/blob/main/packages/react-grab/src/types.ts) for the full `Plugin`, `PluginHooks`, and `PluginConfig` interfaces.

## Primitives

Use primitives to build your own element selector from scratch. Unlike plugins, primitives are standalone utility functions that don't depend on React Grab being initialized.

If you're using primitives to build a custom UI and don't want the default React Grab overlay, disable auto-initialization before importing `react-grab`:

```html
<script>
window.__REACT_GRAB_DISABLED__ = true;
</script>
```

Here's a simple example of how to build your own element selector with hover highlight and one-click inspection:

```bash
npm install grab@latest
```

```tsx
import { useState } from "react";
import {
getElementContext,
freeze,
unfreeze,
openFile,
type ReactGrabElementContext,
} from "grab/primitives";

const useElementSelector = (
onSelect: (context: ReactGrabElementContext) => void,
) => {
const [isActive, setIsActive] = useState(false);

const startSelecting = () => {
setIsActive(true);

const highlightOverlay = document.createElement("div");
Object.assign(highlightOverlay.style, {
position: "fixed",
pointerEvents: "none",
zIndex: "999999",
border: "2px solid #3b82f6",
transition: "all 75ms ease-out",
display: "none",
});
document.body.appendChild(highlightOverlay);

const handleMouseMove = ({ clientX, clientY }: MouseEvent) => {
highlightOverlay.style.display = "none";
const target = document.elementFromPoint(clientX, clientY);
if (!target) return;
const { top, left, width, height } = target.getBoundingClientRect();
Object.assign(highlightOverlay.style, {
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${height}px`,
display: "block",
});
};

const handleClick = async ({ clientX, clientY }: MouseEvent) => {
highlightOverlay.style.display = "none";
const target = document.elementFromPoint(clientX, clientY);
teardown();
if (!target) return;
freeze();
onSelect(await getElementContext(target));
unfreeze();
};

const teardown = () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("click", handleClick, true);
highlightOverlay.remove();
setIsActive(false);
};

document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("click", handleClick, true);
};

return { isActive, startSelecting };
};

const ElementSelector = () => {
const [context, setContext] = useState<ReactGrabElementContext | null>(null);
const selector = useElementSelector(setContext);

return (
<div>
<button onClick={selector.startSelecting} disabled={selector.isActive}>
{selector.isActive ? "Selecting…" : "Select Element"}
</button>
{context && (
<div>
<p>Component: {context.componentName}</p>
<p>Selector: {context.selector}</p>
<pre>{context.stackString}</pre>
<button
onClick={() => {
const frame = context.stack[0];
if (frame?.fileName) openFile(frame.fileName, frame.lineNumber);
}}
>
Open in Editor
</button>
</div>
)}
</div>
);
};
```

See [`packages/react-grab/src/primitives.ts`](https://github.com/aidenybai/react-grab/blob/main/packages/react-grab/src/primitives.ts) for the full `ReactGrabElementContext`, `getElementContext`, `freeze`, `unfreeze`, and `openFile` primitives.

## Resources & Contributing Back

Want to try it out? Check out [our demo](https://react-grab.com).
Expand Down
116 changes: 0 additions & 116 deletions packages/react-grab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,122 +193,6 @@ actions: [

See [`packages/react-grab/src/types.ts`](https://github.com/aidenybai/react-grab/blob/main/packages/react-grab/src/types.ts) for the full `Plugin`, `PluginHooks`, and `PluginConfig` interfaces.

## Primitives

Use primitives to build your own element selector from scratch. Unlike plugins, primitives are standalone utility functions that don't depend on React Grab being initialized.

If you're using primitives to build a custom UI and don't want the default React Grab overlay, disable auto-initialization before importing `react-grab`:

```html
<script>
window.__REACT_GRAB_DISABLED__ = true;
</script>
```

Here's a simple example of how to build your own element selector with hover highlight and one-click inspection:

```bash
npm install react-grab@latest
```

```tsx
import { useState } from "react";
import {
getElementContext,
freeze,
unfreeze,
openFile,
type ReactGrabElementContext,
} from "react-grab/primitives";

const useElementSelector = (
onSelect: (context: ReactGrabElementContext) => void,
) => {
const [isActive, setIsActive] = useState(false);

const startSelecting = () => {
setIsActive(true);

const highlightOverlay = document.createElement("div");
Object.assign(highlightOverlay.style, {
position: "fixed",
pointerEvents: "none",
zIndex: "999999",
border: "2px solid #3b82f6",
transition: "all 75ms ease-out",
display: "none",
});
document.body.appendChild(highlightOverlay);

const handleMouseMove = ({ clientX, clientY }: MouseEvent) => {
highlightOverlay.style.display = "none";
const target = document.elementFromPoint(clientX, clientY);
if (!target) return;
const { top, left, width, height } = target.getBoundingClientRect();
Object.assign(highlightOverlay.style, {
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${height}px`,
display: "block",
});
};

const handleClick = async ({ clientX, clientY }: MouseEvent) => {
highlightOverlay.style.display = "none";
const target = document.elementFromPoint(clientX, clientY);
teardown();
if (!target) return;
freeze();
onSelect(await getElementContext(target));
unfreeze();
};

const teardown = () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("click", handleClick, true);
highlightOverlay.remove();
setIsActive(false);
};

document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("click", handleClick, true);
};

return { isActive, startSelecting };
};

const ElementSelector = () => {
const [context, setContext] = useState<ReactGrabElementContext | null>(null);
const selector = useElementSelector(setContext);

return (
<div>
<button onClick={selector.startSelecting} disabled={selector.isActive}>
{selector.isActive ? "Selecting…" : "Select Element"}
</button>
{context && (
<div>
<p>Component: {context.componentName}</p>
<p>Selector: {context.selector}</p>
<pre>{context.stackString}</pre>
<button
onClick={() => {
const frame = context.stack[0];
if (frame?.fileName) openFile(frame.fileName, frame.lineNumber);
}}
>
Open in Editor
</button>
</div>
)}
</div>
);
};
```

See [`packages/react-grab/src/primitives.ts`](https://github.com/aidenybai/react-grab/blob/main/packages/react-grab/src/primitives.ts) for the full `ReactGrabElementContext`, `getElementContext`, `freeze`, `unfreeze`, and `openFile` primitives.

## Resources & Contributing Back

Want to try it out? Check out [our demo](https://react-grab.com).
Expand Down
6 changes: 4 additions & 2 deletions packages/react-grab/e2e/edge-cases.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,10 @@ test.describe("Edge Cases", () => {
test.describe("Viewport Edge Cases", () => {
test("should handle elements outside viewport", async ({ reactGrab }) => {
await reactGrab.activate();
await reactGrab.scrollPage(1000);
await reactGrab.page.waitForTimeout(500);

const footer = reactGrab.page.locator("[data-testid='footer']");
await footer.scrollIntoViewIfNeeded();
await reactGrab.page.waitForTimeout(200);

await reactGrab.hoverElement("[data-testid='footer']");
await reactGrab.waitForSelectionBox();
Expand Down
Loading
Loading