Skip to content
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
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The goal of Solid Primitives is to wrap client and server side functionality to
|----|----|----|----|----|----|
|<h4>*Inputs*</h4>|
|[active-element](https://github.com/solidjs-community/solid-primitives/tree/main/packages/active-element#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createActiveElement](https://github.com/solidjs-community/solid-primitives/tree/main/packages/active-element#createactiveelement)<br />[createFocusSignal](https://github.com/solidjs-community/solid-primitives/tree/main/packages/active-element#createfocussignal)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/active-element?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/active-element)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/active-element?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/active-element)||
|[autofocus](https://github.com/solidjs-community/solid-primitives/tree/main/packages/autofocus#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-1.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[autofocus](https://github.com/solidjs-community/solid-primitives/tree/main/packages/autofocus#autofocus)<br />[createAutofocus](https://github.com/solidjs-community/solid-primitives/tree/main/packages/autofocus#createautofocus)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/autofocus?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/autofocus)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/autofocus?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/autofocus)|✓|
|[focus](https://github.com/solidjs-community/solid-primitives/tree/main/packages/focus#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-1.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[autofocus](https://github.com/solidjs-community/solid-primitives/tree/main/packages/focus#autofocus)<br />[createAutofocus](https://github.com/solidjs-community/solid-primitives/tree/main/packages/focus#createautofocus)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/focus?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/focus)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/focus?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/focus)|✓|
|[input-mask](https://github.com/solidjs-community/solid-primitives/tree/main/packages/input-mask#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-1.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createInputMask](https://github.com/solidjs-community/solid-primitives/tree/main/packages/input-mask#createinputmask)<br />[createMaskPattern](https://github.com/solidjs-community/solid-primitives/tree/main/packages/input-mask#createmaskpattern)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/input-mask?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/input-mask)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/input-mask?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/input-mask)|✓|
|[keyboard](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-1.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[useKeyDownList](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#usekeydownlist)<br />[useCurrentlyHeldKey](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#usecurrentlyheldkey)<br />[useKeyDownSequence](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#usekeydownsequence)<br />[createKeyHold](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#createkeyhold)<br />[createShortcut](https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyboard#createshortcut)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/keyboard?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/keyboard)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/keyboard?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/keyboard)||
|[mouse](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mouse#readme)|[![STAGE](https://img.shields.io/endpoint?style=for-the-badge&label=&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-3.json)](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createMousePosition](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mouse#createmouseposition)<br />[createPositionToElement](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mouse#createpositiontoelement)|[![SIZE](https://img.shields.io/bundlephobia/minzip/@solid-primitives/mouse?style=for-the-badge&label=)](https://bundlephobia.com/package/@solid-primitives/mouse)|[![VERSION](https://img.shields.io/npm/v/@solid-primitives/mouse?style=for-the-badge&label=)](https://www.npmjs.com/package/@solid-primitives/mouse)||
Expand Down
9 changes: 5 additions & 4 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"
},
Expand Down Expand Up @@ -55,10 +54,12 @@
"@solid-primitives/utils": "workspace:^"
},
"peerDependencies": {
"solid-js": "^1.6.12"
"@solidjs/web": "^2.0.0-beta.12",
"solid-js": "^2.0.0-beta.12"
},
"typesVersions": {},
"devDependencies": {
"solid-js": "^1.9.7"
"@solidjs/web": "2.0.0-beta.12",
"solid-js": "2.0.0-beta.12"
}
}
63 changes: 6 additions & 57 deletions packages/active-element/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +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";
import { isServer } from "@solidjs/web";
import { type Directive, createHydratableSignal } from "@solid-primitives/utils";
import { makeEventListener } from "@solid-primitives/event-listener";

declare module "solid-js" {
namespace JSX {
Expand All @@ -15,7 +11,7 @@ declare module "solid-js" {
}
}
// This ensures the `JSX` import won't fall victim to tree shaking
export type E = JSX.Element;
export type E = JSX.Directives;

const getActiveElement = () =>
document.activeElement === document.body ? null : document.activeElement;
Expand Down Expand Up @@ -60,54 +56,6 @@ export function createActiveElement(): Accessor<Element | null> {
return active;
}

/**
* 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.
*
Expand All @@ -123,5 +71,6 @@ export const focus: Directive<(isActive: boolean) => void> = (target, props) =>
}
const callback = props();
callback(document.activeElement === target);
makeFocusListener(target, callback);
makeEventListener(target, "blur", callback.bind(void 0, false), true);
makeEventListener(target, "focus", callback.bind(void 0, true), true);
};
47 changes: 1 addition & 46 deletions packages/active-element/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { createRoot } from "solid-js";
import { describe, test, expect } from "vitest";
import {
makeActiveElementListener,
createActiveElement,
makeFocusListener,
createFocusSignal,
focus,
} from "../src/index.js";
import { makeActiveElementListener, createActiveElement, focus } from "../src/index.js";

const dispatchFocusEvent = (
target: Element | Window = window,
Expand Down Expand Up @@ -40,29 +34,6 @@ describe("makeActiveElementListener", () => {
}));
});

describe("makeFocusListener", () => {
test("works properly", () =>
createRoot(dispose => {
const el = document.createElement("div");
const captured: any[] = [];
const clear = makeFocusListener(el, e => captured.push(e));
expect(captured).toEqual([]);
dispatchFocusEvent(el, "focus");
expect(captured).toEqual([true]);
dispatchFocusEvent(el, "blur");
expect(captured).toEqual([true, false]);
clear();
dispatchFocusEvent(el, "focus");
expect(captured).toEqual([true, false]);
makeFocusListener(el, e => captured.push(e));
dispatchFocusEvent(el, "blur");
expect(captured).toEqual([true, false, false]);
dispose();
dispatchFocusEvent(el, "focus");
expect(captured).toEqual([true, false, false]);
}));
});

describe("createActiveElement", () => {
test("works properly", () =>
createRoot(dispose => {
Expand All @@ -72,22 +43,6 @@ describe("createActiveElement", () => {
}));
});

describe("createFocusSignal", () => {
test("works properly", () =>
createRoot(dispose => {
const el = document.createElement("div");
const activeEl = createFocusSignal(el);
expect(activeEl()).toBe(false);
dispatchFocusEvent(el, "focus");
expect(activeEl()).toBe(true);
dispatchFocusEvent(el, "blur");
expect(activeEl()).toBe(false);
dispose();
dispatchFocusEvent(el, "focus");
expect(activeEl()).toBe(false);
}));
});

describe("use:focus", () => {
test("works properly", () =>
createRoot(dispose => {
Expand Down
9 changes: 1 addition & 8 deletions packages/active-element/test/server.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { describe, test, expect, vi } from "vitest";
import { makeActiveElementListener, createActiveElement, createFocusSignal } from "../src/index.js";
import { makeActiveElementListener, createActiveElement } from "../src/index.js";

describe("API doesn't break in SSR", () => {
// check if the API doesn't throw when calling it in SSR
test("makeActiveElementListener() - SSR", () => {
const cb = vi.fn();
expect(() => makeActiveElementListener(cb)).not.toThrow();
Expand All @@ -12,10 +11,4 @@ describe("API doesn't break in SSR", () => {
test("createActiveElement() - SSR", () => {
expect(() => createActiveElement()).not.toThrow();
});

test("createFocusSignal() - SSR", () => {
const el = vi.fn();
expect(() => createFocusSignal(el)).not.toThrow();
expect(el).not.toBeCalled();
});
});
81 changes: 0 additions & 81 deletions packages/autofocus/README.md

This file was deleted.

Loading