@jini-ai/ui 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @jini-ai/ui
|
|
2
2
|
|
|
3
|
+
## 0.4.1 — 2026-10-04
|
|
4
|
+
|
|
5
|
+
- `useFocusTrap` returns focus to the element that held it when the trap activated (the dialog's opener) once the trap closes or deactivates, if that element is still in the document.
|
|
6
|
+
|
|
3
7
|
## 0.4.0 — 2026-10-02
|
|
4
8
|
|
|
5
9
|
### BREAKING
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type RefObject } from "react";
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Trap Tab/Shift+Tab in the most recently activated container; outer traps resume on cleanup, and
|
|
4
|
+
* focus goes back to whatever held it when the trap activated (the dialog's opener).
|
|
5
|
+
*/
|
|
3
6
|
export declare function useFocusTrap({ containerRef }: {
|
|
4
7
|
containerRef: RefObject<HTMLElement | null>;
|
|
5
8
|
}, { active }?: {
|
|
@@ -38,11 +38,19 @@ function redirectTarget(container, shiftKey) {
|
|
|
38
38
|
return first;
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Trap Tab/Shift+Tab in the most recently activated container; outer traps resume on cleanup, and
|
|
43
|
+
* focus goes back to whatever held it when the trap activated (the dialog's opener).
|
|
44
|
+
*/
|
|
42
45
|
export function useFocusTrap({ containerRef }, { active = true } = {}) {
|
|
43
46
|
useEffect(() => {
|
|
44
47
|
if (!active)
|
|
45
48
|
return;
|
|
49
|
+
// Captured before anything inside the dialog takes focus, so keyboard users land back on the
|
|
50
|
+
// control they opened it from instead of at the top of the page. An element already inside the
|
|
51
|
+
// container (an autoFocus child committed first) is not the opener, so nothing is restored then.
|
|
52
|
+
const previous = document.activeElement;
|
|
53
|
+
const opener = previous instanceof HTMLElement && !containerRef.current?.contains(previous) ? previous : null;
|
|
46
54
|
activeTraps.push(containerRef);
|
|
47
55
|
function onKeyDown(event) {
|
|
48
56
|
if (event.key !== "Tab" || activeTraps[activeTraps.length - 1] !== containerRef)
|
|
@@ -60,6 +68,8 @@ export function useFocusTrap({ containerRef }, { active = true } = {}) {
|
|
|
60
68
|
return () => {
|
|
61
69
|
document.removeEventListener("keydown", onKeyDown);
|
|
62
70
|
activeTraps.splice(activeTraps.lastIndexOf(containerRef), 1);
|
|
71
|
+
if (opener?.isConnected)
|
|
72
|
+
opener.focus();
|
|
63
73
|
};
|
|
64
74
|
}, [containerRef, active]);
|
|
65
75
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Generic, product-neutral React UI primitives and feature domains. Framework-free types, decision rules, catalogs, and host port contracts live behind ./core (no React, no DOM). The Excalidraw and Lexical editors live behind ./sketch-editor and ./lexical-rich-text-editor; the GrapesJS-backed inline HTML editor lives behind ./html-editor.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -162,8 +162,8 @@
|
|
|
162
162
|
"shiki": "^1.24.4",
|
|
163
163
|
"tailwind-merge": "^2.6.0",
|
|
164
164
|
"zod": "^3.25.76",
|
|
165
|
-
"@jini-ai/
|
|
166
|
-
"@jini-ai/
|
|
165
|
+
"@jini-ai/agentic": "0.4.0",
|
|
166
|
+
"@jini-ai/core": "0.4.0"
|
|
167
167
|
},
|
|
168
168
|
"devDependencies": {
|
|
169
169
|
"@excalidraw/excalidraw": "0.18.1",
|