@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
76 lines (75 loc) • 2.66 kB
JavaScript
import { useSyncExternalStore } from "react";
//#region src/base-ui/FocusScope/store.ts
let state = {
activeScopeId: null,
knownScopes: /* @__PURE__ */ new Map(),
lastFocusedItem: /* @__PURE__ */ new Map()
};
const listeners = /* @__PURE__ */ new Set();
const setState = (patch) => {
state = {
...state,
...patch
};
listeners.forEach((listener) => listener());
};
const subscribe = (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
};
const getActiveScopeId = () => state.activeScopeId;
const setActiveScope = (id) => {
if (id !== null && !state.knownScopes.has(id)) return;
if (state.activeScopeId === id) return;
setState({ activeScopeId: id });
};
const getLastFocusedItem = (scopeId) => state.lastFocusedItem.get(scopeId) ?? null;
const setLastFocusedItem = (scopeId, itemId) => {
if ((state.lastFocusedItem.get(scopeId) ?? null) === itemId) return;
const next = new Map(state.lastFocusedItem);
if (itemId === null) next.delete(scopeId);
else next.set(scopeId, itemId);
setState({ lastFocusedItem: next });
};
const findScopeId = (event) => {
return (event.target instanceof Element ? event.target : null)?.closest("[data-focus-scope]")?.dataset.focusScope ?? null;
};
const onGlobalInteract = (event) => {
const id = findScopeId(event);
if (id !== null) setActiveScope(id);
};
let listening = false;
const ensureDocumentListeners = () => {
if (listening || typeof document === "undefined") return;
listening = true;
document.addEventListener("pointerdown", onGlobalInteract, true);
document.addEventListener("focusin", onGlobalInteract, true);
};
const registerScope = (id) => {
ensureDocumentListeners();
const next = new Map(state.knownScopes);
next.set(id, (next.get(id) ?? 0) + 1);
setState({ knownScopes: next });
return () => {
const count = state.knownScopes.get(id) ?? 0;
const scopes = new Map(state.knownScopes);
if (count > 1) {
scopes.set(id, count - 1);
setState({ knownScopes: scopes });
return;
}
scopes.delete(id);
const lastFocused = new Map(state.lastFocusedItem);
lastFocused.delete(id);
setState({
activeScopeId: state.activeScopeId === id ? null : state.activeScopeId,
knownScopes: scopes,
lastFocusedItem: lastFocused
});
};
};
const useActiveScopeId = () => useSyncExternalStore(subscribe, getActiveScopeId, () => null);
const useFocusScopeActive = (id) => useSyncExternalStore(subscribe, () => state.activeScopeId === id, () => false);
//#endregion
export { getActiveScopeId, getLastFocusedItem, registerScope, setActiveScope, setLastFocusedItem, subscribe, useActiveScopeId, useFocusScopeActive };
//# sourceMappingURL=store.mjs.map