Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
18 changes: 18 additions & 0 deletions .changeset/active-element-solid2-migration.md
Original file line number Diff line number Diff line change
@@ -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)
25 changes: 0 additions & 25 deletions .changeset/autofocus-solid2-migration.md

This file was deleted.

40 changes: 40 additions & 0 deletions .changeset/focus-solid2-migration.md
Original file line number Diff line number Diff line change
@@ -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
<button use:autofocus autofocus>...</button>
// After
<button ref={autofocus()} autofocus>...</button>

// Before
<button use:autofocus={false} autofocus>...</button>
// After
<button ref={autofocus(false)} autofocus>...</button>
```
- `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
```
2 changes: 1 addition & 1 deletion .changeset/pre.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
60 changes: 0 additions & 60 deletions packages/active-element/dev/index.tsx

This file was deleted.

7 changes: 0 additions & 7 deletions packages/active-element/dev/utils.ts

This file was deleted.

10 changes: 5 additions & 5 deletions packages/active-element/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
"name": "active-element",
"stage": 3,
"list": [
"createActiveElement",
"createFocusSignal"
"createActiveElement"
],
"category": "Inputs",
"gzip": 942
Expand All @@ -39,7 +38,6 @@
}
},
"scripts": {
"dev": "node --import=@nothing-but/node-resolve-ts --experimental-transform-types ../../scripts/dev.ts",
"build": "node --import=@nothing-but/node-resolve-ts --experimental-transform-types ../../scripts/build.ts",
"vitest": "vitest -c ../../configs/vitest.config.ts",
"test": "pnpm run vitest",
Expand All @@ -56,10 +54,12 @@
"@solid-primitives/utils": "workspace:^"
},
"peerDependencies": {
"solid-js": "^1.6.12"
"@solidjs/web": "^2.0.0-beta.14",
"solid-js": "^2.0.0-beta.14"
},
"typesVersions": {},
"devDependencies": {
"solid-js": "^1.9.7"
"@solidjs/web": "2.0.0-beta.14",
"solid-js": "2.0.0-beta.14"
}
}
96 changes: 20 additions & 76 deletions packages/active-element/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
import { type Accessor, type JSX } from "solid-js";
import { isServer } from "solid-js/web";
import {
type MaybeAccessor,
type Directive,
createHydratableSignal,
} from "@solid-primitives/utils";
import { makeEventListener, createEventListener } from "@solid-primitives/event-listener";

declare module "solid-js" {
namespace JSX {
interface Directives {
focus: (isActive: boolean) => void;
}
}
}
// This ensures the `JSX` import won't fall victim to tree shaking
export type E = JSX.Element;
import { type Accessor, onCleanup } from "solid-js";
import { isServer } from "@solidjs/web";
import { createHydratableSignal } from "@solid-primitives/utils";
import { makeEventListener } from "@solid-primitives/event-listener";

const getActiveElement = () =>
document.activeElement === document.body ? null : document.activeElement;
Expand Down Expand Up @@ -61,67 +47,25 @@ export function createActiveElement(): Accessor<Element | null> {
}

/**
* Attaches "blur" and "focus" event listeners to the element.
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/active-element#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(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 () => void 0;
}
const clear1 = makeEventListener(target, "blur", callback.bind(void 0, false), useCapture);
const clear2 = makeEventListener(target, "focus", callback.bind(void 0, 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/active-element#createFocusSignal
* @example
* const isFocused = createFocusSignal(() => el)
* isFocused() // T: boolean
*/
export function createFocusSignal(target: MaybeAccessor<Element>): Accessor<boolean> {
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;
}

/**
* A directive that notifies you when the element becomes active or inactive.
* A ref factory that notifies you when the element becomes active or inactive.
*
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/active-element#focus
*
* @example
* const [active, setActive] = createSignal(false)
* <input use:focus={setActive} />
* <input ref={focus(setActive)} />
*/
export const focus: Directive<(isActive: boolean) => void> = (target, props) => {
if (isServer) {
return;
}
const callback = props();
callback(document.activeElement === target);
makeFocusListener(target, callback);
};
export function focus(callback: (isActive: boolean) => void): (target: Element) => void {
if (isServer) return () => {};
let cleanup: (() => void) | null = null;
onCleanup(() => cleanup?.());
return (target: Element) => {
callback(document.activeElement === target);
const c1 = makeEventListener(target, "blur", () => callback(false), true);
const c2 = makeEventListener(target, "focus", () => callback(true), true);
cleanup = () => {
c1();
c2();
};
};
}
Loading