diff --git a/.changeset/active-element-solid2-migration.md b/.changeset/active-element-solid2-migration.md
new file mode 100644
index 000000000..5f8139a51
--- /dev/null
+++ b/.changeset/active-element-solid2-migration.md
@@ -0,0 +1,18 @@
+---
+"@solid-primitives/active-element": major
+---
+
+Migrate to Solid.js v2.0 (beta.12)
+
+## Breaking Changes
+
+**Peer dependencies**: `solid-js@^2.0.0-beta.12` and `@solidjs/web@^2.0.0-beta.12` are now required.
+
+- `makeFocusListener` and `createFocusSignal` have moved to `@solid-primitives/focus`. Import them from there instead:
+ ```ts
+ // Before
+ import { makeFocusListener, createFocusSignal } from "@solid-primitives/active-element";
+ // After
+ import { makeFocusListener, createFocusSignal } from "@solid-primitives/focus";
+ ```
+- `isServer` is now sourced from `@solidjs/web` internally (no user-facing API change)
diff --git a/.changeset/autofocus-solid2-migration.md b/.changeset/autofocus-solid2-migration.md
deleted file mode 100644
index a9c43b560..000000000
--- a/.changeset/autofocus-solid2-migration.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-"@solid-primitives/autofocus": major
----
-
-Migrate to Solid.js v2.0 (beta.10)
-
-## Breaking Changes
-
-**Peer dependencies**: `solid-js@^2.0.0-beta.10` and `@solidjs/web@^2.0.0-beta.10` are now required.
-
-- `autofocus` is now a **ref callback factory** (`use:autofocus` directive removed; Solid 2.0 no longer supports `use:` directives):
- ```tsx
- // Before
-
- // After
-
-
- // Before
-
- // After
-
- ```
-- `JSX` type is now imported from `@solidjs/web` (was `solid-js`)
-- `onMount` replaced by `onSettled` from `solid-js`
-- `createAutofocus` uses split `createEffect(compute, apply)` form with proper timeout cleanup on re-focus
diff --git a/.changeset/focus-solid2-migration.md b/.changeset/focus-solid2-migration.md
new file mode 100644
index 000000000..fb95c2c84
--- /dev/null
+++ b/.changeset/focus-solid2-migration.md
@@ -0,0 +1,40 @@
+---
+"@solid-primitives/focus": major
+---
+
+Migrate to Solid.js v2.0 (beta.12)
+
+## Breaking Changes
+
+**Peer dependencies**: `solid-js@^2.0.0-beta.12` and `@solidjs/web@^2.0.0-beta.12` are now required.
+
+- `autofocus` is now a **ref callback factory** (`use:autofocus` directive removed; Solid 2.0 no longer supports `use:` directives):
+ ```tsx
+ // Before
+
+ // After
+
+
+ // Before
+
+ // After
+
+ ```
+- `JSX` type is now imported from `@solidjs/web` (was `solid-js`)
+- `onMount` replaced by `onSettled` from `solid-js`
+- `createAutofocus` uses split `createEffect(compute, apply)` form with proper timeout cleanup on re-focus
+
+## New Primitives
+
+`makeFocusListener` and `createFocusSignal` have moved here from `@solid-primitives/active-element`:
+
+- **`makeFocusListener(target, callback, useCapture?)`** — attaches `focus`/`blur` listeners to an element, calling `callback` with the new boolean focus state. Returns a cleanup function.
+ ```ts
+ const clear = makeFocusListener(el, isFocused => console.log(isFocused));
+ clear(); // remove listeners
+ ```
+- **`createFocusSignal(target)`** — reactive signal that tracks whether `target` is focused.
+ ```ts
+ const isFocused = createFocusSignal(() => el);
+ isFocused(); // boolean
+ ```
diff --git a/.changeset/pre.json b/.changeset/pre.json
index ae0287918..c41689783 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -5,7 +5,7 @@
"@solid-primitives/active-element": "2.1.5",
"@solid-primitives/analytics": "0.2.1",
"@solid-primitives/audio": "1.4.4",
- "@solid-primitives/autofocus": "0.1.4",
+ "@solid-primitives/focus": "0.1.4",
"@solid-primitives/bounds": "0.1.5",
"@solid-primitives/broadcast-channel": "0.1.1",
"@solid-primitives/clipboard": "1.6.4",
diff --git a/README.md b/README.md
index 1ddd4dda8..210b3535f 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ The goal of Solid Primitives is to wrap client and server side functionality to
|----|----|----|----|----|----|
|
-
-# @solid-primitives/autofocus
-
-[](https://bundlephobia.com/package/@solid-primitives/autofocus)
-[](https://www.npmjs.com/package/@solid-primitives/autofocus)
-[](https://github.com/solidjs-community/solid-primitives#contribution-process)
-
-Primitives for autofocusing HTML elements.
-
-The native autofocus attribute only works on page load, which makes it incompatible with SolidJS. These primitives run on render, allowing autofocus on initial render as well as dynamically added components.
-
-- [`autofocus`](#autofocus) - Directive to autofocus an element on render.
-- [`createAutofocus`](#createautofocus) - Reactive primitive to autofocus an element on render.
-
-## Installation
-
-```bash
-npm install @solid-primitives/autofocus
-# or
-yarn add @solid-primitives/autofocus
-# or
-pnpm add @solid-primitives/autofocus
-```
-
-## `autofocus`
-
-### How to use it
-
-`autofocus` is a ref callback factory. It uses the native `autofocus` attribute to determine whether to focus the element.
-
-```tsx
-import { autofocus } from "@solid-primitives/autofocus";
-
-;
-```
-
-To conditionally enable autofocus, control the `autofocus` attribute directly — the `autofocus()` ref only focuses when the attribute is present, so removing it is sufficient to opt out:
-
-```tsx
-// Conditionally autofocus by toggling the attribute
-;
-```
-
-> **Note:** The `enabled` parameter was removed because it was redundant — the same effect is achieved by omitting the `autofocus` attribute. Previously, Solid directives always received an accessor argument whether you used it or not, which gave the impression an explicit toggle was necessary.
-
-### `createAutofocus`
-
-`createAutofocus` reactively autofocuses an element passed in as a signal.
-
-```tsx
-import { createAutofocus } from "@solid-primitives/autofocus";
-
-// Using ref
-let ref!: HTMLButtonElement;
-createAutofocus(() => ref);
-
-;
-
-// Using ref signal
-const [ref, setRef] = createSignal();
-createAutofocus(ref);
-
-;
-```
-
-## Demo
-
-You may see the working example here: https://primitives.solidjs.community/playground/autofocus/
-
-Source code: https://github.com/solidjs-community/solid-primitives/blob/main/packages/autofocus/dev/index.tsx
-
-## Changelog
-
-See [CHANGELOG.md](./CHANGELOG.md)
diff --git a/packages/autofocus/test/index.test.tsx b/packages/autofocus/test/index.test.tsx
deleted file mode 100644
index d7bc0b901..000000000
--- a/packages/autofocus/test/index.test.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import { describe, test, expect, vi, beforeEach, afterAll, beforeAll } from "vitest";
-import { createRoot, createSignal, flush } from "solid-js";
-import { autofocus, createAutofocus } from "../src/index.js";
-
-let focused: HTMLElement | null = null;
-
-const original_focus = HTMLElement.prototype.focus;
-HTMLElement.prototype.focus = function (this) {
- focused = this;
-};
-
-beforeAll(() => {
- vi.useFakeTimers();
-});
-
-beforeEach(() => {
- vi.clearAllTimers();
- focused = null;
-});
-
-afterAll(() => {
- vi.useRealTimers();
- HTMLElement.prototype.focus = original_focus;
-});
-
-describe("autofocus", () => {
- test("focuses the element with autofocus attribute", () => {
- const el = document.createElement("button");
- el.setAttribute("autofocus", "");
-
- const dispose = createRoot(dispose => {
- // Phase 1: factory registers onSettled
- const ref = autofocus();
- // Phase 2: ref callback receives the element
- ref(el);
- return dispose;
- });
-
- flush();
- expect(focused).toBe(null);
- vi.runAllTimers();
- expect(focused).toBe(el);
-
- dispose();
- });
-
- test("doesn't focus when autofocus HTML attribute is absent", () => {
- const el = document.createElement("button");
-
- const dispose = createRoot(dispose => {
- const ref = autofocus();
- ref(el);
- return dispose;
- });
-
- flush();
- expect(focused).toBe(null);
- vi.runAllTimers();
- expect(focused).toBe(null);
-
- dispose();
- });
-
-});
-
-describe("createAutofocus", () => {
- const el = document.createElement("button"),
- el2 = document.createElement("button");
-
- test("createAutofocus focuses the element", () => {
- const dispose = createRoot(dispose => {
- createAutofocus(() => el);
- return dispose;
- });
-
- flush();
- expect(focused).toBe(null);
- vi.runAllTimers();
- expect(focused).toBe(el);
-
- dispose();
- });
-
- test("createAutofocus works with signal", () => {
- const [ref, setRef] = createSignal();
-
- const dispose = createRoot(dispose => {
- createAutofocus(ref);
- return dispose;
- });
-
- flush();
- expect(focused).toBe(null);
- vi.runAllTimers();
- expect(focused).toBe(null);
-
- setRef(el);
- flush();
- expect(focused).toBe(null);
- vi.runAllTimers();
- expect(focused).toBe(el);
-
- setRef(el2);
- flush();
- expect(focused).toBe(el);
- vi.runAllTimers();
- expect(focused).toBe(el2);
-
- dispose();
-
- setRef(el);
- expect(focused).toBe(el2);
- vi.runAllTimers();
- expect(focused).toBe(el2);
- });
-});
diff --git a/packages/event-listener/src/types.ts b/packages/event-listener/src/types.ts
index 3394558df..46794c15d 100644
--- a/packages/event-listener/src/types.ts
+++ b/packages/event-listener/src/types.ts
@@ -1,4 +1,4 @@
-import type { JSX } from "solid-js";
+import type { JSX } from "@solidjs/web";
export type EventListenerOptions = boolean | AddEventListenerOptions;
diff --git a/packages/autofocus/CHANGELOG.md b/packages/focus/CHANGELOG.md
similarity index 98%
rename from packages/autofocus/CHANGELOG.md
rename to packages/focus/CHANGELOG.md
index 976ecae9d..ac009a623 100644
--- a/packages/autofocus/CHANGELOG.md
+++ b/packages/focus/CHANGELOG.md
@@ -1,4 +1,4 @@
-# @solid-primitives/autofocus
+# @solid-primitives/focus
## 0.1.4
diff --git a/packages/autofocus/LICENSE b/packages/focus/LICENSE
similarity index 84%
rename from packages/autofocus/LICENSE
rename to packages/focus/LICENSE
index 38b41d975..7a35c2e14 100644
--- a/packages/autofocus/LICENSE
+++ b/packages/focus/LICENSE
@@ -2,6 +2,10 @@ MIT License
Copyright (c) 2021 Solid Primitives Working Group
+The `createFocusTrap` primitive is ported from solid-focus-trap:
+ Copyright (c) 2023 Jasmin Noetzli (GiyoMoon)
+ https://github.com/corvudev/corvu/tree/main/packages/solid-focus-trap
+
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@@ -18,4 +22,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+SOFTWARE.
diff --git a/packages/focus/README.md b/packages/focus/README.md
new file mode 100644
index 000000000..92772a3e6
--- /dev/null
+++ b/packages/focus/README.md
@@ -0,0 +1,165 @@
+
+
+
+
+# @solid-primitives/focus
+
+[](https://bundlephobia.com/package/@solid-primitives/focus)
+[](https://www.npmjs.com/package/@solid-primitives/focus)
+[](https://github.com/solidjs-community/solid-primitives#contribution-process)
+
+Primitives for autofocusing HTML elements and trapping focus within a container.
+
+The native `autofocus` attribute only works on page load, which makes it incompatible with SolidJS. These primitives run on render, allowing autofocus on initial render as well as dynamically added components.
+
+- [`autofocus`](#autofocus) - Ref callback factory to autofocus an element on render.
+- [`createAutofocus`](#createautofocus) - Reactive primitive to autofocus an element on render.
+- [`createFocusTrap`](#createfocustrap) - Traps focus inside a given DOM element.
+
+## Installation
+
+```bash
+npm install @solid-primitives/focus
+# or
+yarn add @solid-primitives/focus
+# or
+pnpm add @solid-primitives/focus
+```
+
+## `autofocus`
+
+### How to use it
+
+`autofocus` is a ref callback factory. It uses the native `autofocus` attribute to determine whether to focus the element.
+
+```tsx
+import { autofocus } from "@solid-primitives/focus";
+
+;
+```
+
+To conditionally enable autofocus, control the `autofocus` attribute directly — the `autofocus()` ref only focuses when the attribute is present, so removing it is sufficient to opt out:
+
+```tsx
+// Conditionally autofocus by toggling the attribute
+;
+```
+
+> **Note:** The `enabled` parameter was removed because it was redundant — the same effect is achieved by omitting the `autofocus` attribute. Previously, Solid directives always received an accessor argument whether you used it or not, which gave the impression an explicit toggle was necessary.
+
+## `createAutofocus`
+
+`createAutofocus` reactively autofocuses an element passed in as a signal.
+
+```tsx
+import { createAutofocus } from "@solid-primitives/focus";
+
+// Using ref
+let ref!: HTMLButtonElement;
+createAutofocus(() => ref);
+
+;
+
+// Using ref signal
+const [ref, setRef] = createSignal();
+createAutofocus(ref);
+
+;
+```
+
+## `createFocusTrap`
+
+`createFocusTrap` traps keyboard focus inside a given DOM element, cycling through focusable children on Tab / Shift+Tab. It uses a `MutationObserver` to stay up to date with DOM changes and restores focus to the previously focused element when deactivated.
+
+> Ported from [solid-focus-trap](https://github.com/corvudev/corvu/tree/main/packages/solid-focus-trap) by [Jasmin Noetzli (GiyoMoon)](https://github.com/GiyoMoon), adapted for Solid.js 2.0.
+
+### How to use it
+
+```tsx
+import { createFocusTrap } from "@solid-primitives/focus";
+
+const DialogContent: Component<{ open: boolean }> = props => {
+ const [contentRef, setContentRef] = createSignal(null);
+
+ createFocusTrap({
+ element: contentRef,
+ enabled: () => props.open,
+ });
+
+ return (
+
+
+
+
+
+
+ );
+};
+```
+
+### Props
+
+| Prop | Type | Default | Description |
+| -------------------- | --------------------------------- | -------------------------------- | --------------------------------------------------------------------------------- |
+| `element` | `MaybeAccessor` | — | Element to trap focus within. |
+| `enabled` | `MaybeAccessor` | `true` | Whether the trap is active. |
+| `observeChanges` | `MaybeAccessor` | `true` | Watch for DOM mutations inside the container and refresh focusable elements. |
+| `initialFocusElement`| `MaybeAccessor` | First focusable element | Element to focus when the trap activates. |
+| `restoreFocus` | `MaybeAccessor` | `true` | Restore focus to the previously focused element when the trap deactivates. |
+| `finalFocusElement` | `MaybeAccessor` | Previously focused element | Element to focus when the trap deactivates. |
+| `onInitialFocus` | `(event: Event) => void` | — | Callback when focus moves into the trap. Call `event.preventDefault()` to cancel.|
+| `onFinalFocus` | `(event: Event) => void` | — | Callback when focus restores. Call `event.preventDefault()` to cancel. |
+
+### Custom initial focus
+
+```tsx
+const [contentRef, setContentRef] = createSignal(null);
+const [inputRef, setInputRef] = createSignal(null);
+
+createFocusTrap({
+ element: contentRef,
+ enabled: () => props.open,
+ initialFocusElement: inputRef,
+});
+
+return (
+
+
+
+
+
+
+);
+```
+
+### Preventing focus moves
+
+```tsx
+createFocusTrap({
+ element: contentRef,
+ onInitialFocus: event => {
+ event.preventDefault(); // focus won't move on activation
+ },
+ onFinalFocus: event => {
+ event.preventDefault(); // focus won't restore on deactivation
+ },
+});
+```
+
+## Demo
+
+You may see the working example here: https://primitives.solidjs.community/playground/focus/
+
+Source code: https://github.com/solidjs-community/solid-primitives/blob/main/packages/focus/dev/index.tsx
+
+## Credits
+
+`createFocusTrap` is ported from [solid-focus-trap](https://github.com/corvudev/corvu/tree/main/packages/solid-focus-trap), part of the [corvu](https://corvu.dev) UI toolkit by [Jasmin Noetzli (GiyoMoon)](https://github.com/GiyoMoon). Licensed under the MIT License.
+
+## Changelog
+
+See [CHANGELOG.md](./CHANGELOG.md)
diff --git a/packages/autofocus/dev/index.tsx b/packages/focus/dev/index.tsx
similarity index 100%
rename from packages/autofocus/dev/index.tsx
rename to packages/focus/dev/index.tsx
diff --git a/packages/autofocus/package.json b/packages/focus/package.json
similarity index 68%
rename from packages/autofocus/package.json
rename to packages/focus/package.json
index c0e0ca5af..c848eaa19 100644
--- a/packages/autofocus/package.json
+++ b/packages/focus/package.json
@@ -1,11 +1,20 @@
{
- "name": "@solid-primitives/autofocus",
+ "name": "@solid-primitives/focus",
"version": "0.2.0",
- "description": "Primitives for autofocusing HTML elements",
+ "description": "Primitives for autofocusing HTML elements and trapping focus within a container",
"author": "jer3m01 ",
- "contributors": [],
+ "contributors": [
+ {
+ "name": "Jasmin Noetzli",
+ "url": "https://github.com/GiyoMoon"
+ },
+ {
+ "name": "David Di Biase",
+ "url": "https://github.com/davedbase"
+ }
+ ],
"license": "MIT",
- "homepage": "https://primitives.solidjs.community/package/autofocus",
+ "homepage": "https://primitives.solidjs.community/package/focus",
"repository": {
"type": "git",
"url": "git+https://github.com/solidjs-community/solid-primitives.git"
@@ -14,11 +23,14 @@
"url": "https://github.com/solidjs-community/solid-primitives/issues"
},
"primitive": {
- "name": "autofocus",
+ "name": "focus",
"stage": 1,
"list": [
"autofocus",
- "createAutofocus"
+ "createAutofocus",
+ "createFocusTrap",
+ "makeFocusListener",
+ "createFocusSignal"
],
"category": "Inputs"
},
@@ -26,7 +38,11 @@
"solid",
"primitives",
"focus",
- "autofocus"
+ "autofocus",
+ "focus-trap",
+ "trap",
+ "accessibility",
+ "a11y"
],
"private": false,
"sideEffects": false,
@@ -56,6 +72,7 @@
"solid-js": "^2.0.0-beta.12"
},
"dependencies": {
+ "@solid-primitives/event-listener": "workspace:^",
"@solid-primitives/utils": "workspace:^"
},
"typesVersions": {},
diff --git a/packages/autofocus/src/index.ts b/packages/focus/src/autofocus.ts
similarity index 100%
rename from packages/autofocus/src/index.ts
rename to packages/focus/src/autofocus.ts
diff --git a/packages/focus/src/focusSignal.ts b/packages/focus/src/focusSignal.ts
new file mode 100644
index 000000000..c24e09e6c
--- /dev/null
+++ b/packages/focus/src/focusSignal.ts
@@ -0,0 +1,52 @@
+import { type Accessor } from "solid-js";
+import { isServer } from "@solidjs/web";
+import { type MaybeAccessor, createHydratableSignal } from "@solid-primitives/utils";
+import { makeEventListener, createEventListener } from "@solid-primitives/event-listener";
+
+/**
+ * Attaches "blur" and "focus" event listeners to the element.
+ * @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/focus#makeFocusListener
+ * @param target element
+ * @param callback handle focus change
+ * @param useCapture activates capturing, which allows to listen on events at the root that don't support bubbling.
+ * @returns function for clearing event listeners
+ * @example
+ * const [isFocused, setIsFocused] = createSignal(false)
+ * const clear = makeFocusListener(el, focused => setIsFocused(focused));
+ * // remove listeners (happens also on cleanup)
+ * clear();
+ */
+export function makeFocusListener(
+ target: Element,
+ callback: (isActive: boolean) => void,
+ useCapture = true,
+): VoidFunction {
+ if (isServer) {
+ return () => {};
+ }
+ const clear1 = makeEventListener(target, "blur", callback.bind(undefined, false), useCapture);
+ const clear2 = makeEventListener(target, "focus", callback.bind(undefined, true), useCapture);
+ return () => (clear1(), clear2());
+}
+
+/**
+ * Provides a signal representing element's focus state.
+ * @param target element or a reactive function returning one
+ * @returns boolean signal representing element's focus state
+ * @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/focus#createFocusSignal
+ * @example
+ * const isFocused = createFocusSignal(() => el)
+ * isFocused() // T: boolean
+ */
+export function createFocusSignal(target: MaybeAccessor): Accessor {
+ if (isServer) {
+ return () => false;
+ }
+ const [isActive, setIsActive] = createHydratableSignal(
+ false,
+ () => document.activeElement === target,
+ );
+ createEventListener(target, "blur", () => setIsActive(false), true);
+ createEventListener(target, "focus", () => setIsActive(true), true);
+ return isActive;
+}
diff --git a/packages/focus/src/focusTrap.ts b/packages/focus/src/focusTrap.ts
new file mode 100644
index 000000000..06391279e
--- /dev/null
+++ b/packages/focus/src/focusTrap.ts
@@ -0,0 +1,214 @@
+/*
+ * Ported from solid-focus-trap by Jasmin Noetzli (GiyoMoon)
+ * MIT License — https://github.com/corvudev/corvu/tree/main/packages/solid-focus-trap
+ * Adapted for Solid.js 2.0 and @solid-primitives/focus by the Solid Primitives Working Group.
+ */
+
+import { access, afterPaint, INTERNAL_OPTIONS, type MaybeAccessor } from "@solid-primitives/utils";
+import { createEffect, createMemo, createSignal } from "solid-js";
+
+const FOCUSABLE_SELECTOR =
+ 'a[href]:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), textarea:not([tabindex="-1"]), select:not([tabindex="-1"]), details:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])';
+
+const EVENT_INITIAL_FOCUS = "focusTrap.initialFocus";
+const EVENT_FINAL_FOCUS = "focusTrap.finalFocus";
+const EVENT_OPTIONS = { bubbles: false, cancelable: true } as const;
+
+export type CreateFocusTrapProps = {
+ /** Element to trap focus within. */
+ element: MaybeAccessor;
+ /** Whether the focus trap is active. Default: `true` */
+ enabled?: MaybeAccessor;
+ /**
+ * Watch for DOM mutations inside the container and reload the list of
+ * focusable elements accordingly. Default: `true`
+ */
+ observeChanges?: MaybeAccessor;
+ /**
+ * Element to focus when the trap activates.
+ * Default: the first focusable element inside `element`.
+ */
+ initialFocusElement?: MaybeAccessor;
+ /**
+ * Restore focus to the element that was focused before the trap activated
+ * when the trap is deactivated. Default: `true`
+ */
+ restoreFocus?: MaybeAccessor;
+ /**
+ * Element to focus when the trap deactivates.
+ * Default: the element that was focused before the trap activated.
+ */
+ finalFocusElement?: MaybeAccessor;
+ /**
+ * Callback fired when focus moves into the trap.
+ * Call `event.preventDefault()` to suppress the focus move.
+ */
+ onInitialFocus?: (event: Event) => void;
+ /**
+ * Callback fired when focus is restored after deactivation.
+ * Call `event.preventDefault()` to suppress the focus move.
+ */
+ onFinalFocus?: (event: Event) => void;
+};
+
+/**
+ * Traps focus inside the given element. Aware of DOM changes inside the trap
+ * via a MutationObserver. Properly restores focus when deactivated.
+ *
+ * Ported from [solid-focus-trap](https://github.com/corvudev/corvu/tree/main/packages/solid-focus-trap)
+ * by Jasmin Noetzli (GiyoMoon), adapted for Solid.js 2.0.
+ *
+ * @example
+ * ```tsx
+ * const [ref, setRef] = createSignal(null);
+ * createFocusTrap({ element: ref, enabled: () => isOpen() });
+ *