merge: stack widget modernization on dnd kit
This commit is contained in:
940 files changed
+104475
-33649
No files matched your search
@@ -0,0 +1,3 @@
|
||||
"use client";
|
||||
|
||||
export { Spotlight } from "./components/spotlight";
|
||||
@@ -11,18 +11,18 @@ import { hotkeys } from "@homarr/definitions";
|
||||
import type { TranslationObject } from "@homarr/translation";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { OpenMediaRequestSearchOptions } from "../index";
|
||||
import {
|
||||
consumePendingMediaRequestSearch,
|
||||
consumePendingSpotlightOpen,
|
||||
mediaRequestSearchEvent,
|
||||
spotlightOpenEvent,
|
||||
} from "../open";
|
||||
import type { OpenMediaRequestSearchOptions } from "../open";
|
||||
import type { inferSearchInteractionOptions } from "../lib/interaction";
|
||||
import type { SearchMode } from "../lib/mode";
|
||||
import { searchModes } from "../modes";
|
||||
import { useHomeEmptyGroupsWithPreferences } from "../modes/help/home-empty-groups";
|
||||
import {
|
||||
mediaRequestSearchEvent,
|
||||
mediaRequestSearchScopeAtom,
|
||||
selectAction,
|
||||
spotlightActions,
|
||||
spotlightStore,
|
||||
} from "../spotlight-store";
|
||||
import { mediaRequestSearchScopeAtom, selectAction, spotlightActions, spotlightStore } from "../spotlight-store";
|
||||
import { SpotlightChildrenActions } from "./actions/children-actions";
|
||||
import { SpotlightActionGroups } from "./actions/groups/action-group";
|
||||
|
||||
@@ -64,6 +64,17 @@ const SpotlightWithActiveMode = ({ modeState, queryState, activeMode }: Spotligh
|
||||
const homeEmptyGroups = useHomeEmptyGroupsWithPreferences();
|
||||
const hasModeCharacter = activeMode.modeKey !== defaultMode && activeMode.character !== undefined;
|
||||
|
||||
useEffect(() => {
|
||||
const handleOpen = () => {
|
||||
consumePendingSpotlightOpen();
|
||||
spotlightActions.open();
|
||||
};
|
||||
|
||||
window.addEventListener(spotlightOpenEvent, handleOpen);
|
||||
if (consumePendingSpotlightOpen()) spotlightActions.open();
|
||||
return () => window.removeEventListener(spotlightOpenEvent, handleOpen);
|
||||
}, []);
|
||||
|
||||
const clearChildrenStack = useCallback(() => {
|
||||
setChildrenStack([]);
|
||||
}, []);
|
||||
@@ -84,17 +95,24 @@ const SpotlightWithActiveMode = ({ modeState, queryState, activeMode }: Spotligh
|
||||
}, [setQuery]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleMediaRequestSearch = (event: Event) => {
|
||||
const { integrationIds, query } = (event as CustomEvent<OpenMediaRequestSearchOptions>).detail ?? {};
|
||||
const applyMediaRequestSearch = ({ integrationIds, query: searchQuery }: OpenMediaRequestSearchOptions) => {
|
||||
setMediaRequestSearchScope({ integrationIds });
|
||||
setMode("media");
|
||||
clearChildrenStack();
|
||||
setQuery(query ?? "");
|
||||
setQuery(searchQuery ?? "");
|
||||
spotlightActions.open();
|
||||
setTimeout(() => selectAction(0, spotlightStore));
|
||||
};
|
||||
|
||||
const handleMediaRequestSearch = (event: Event) => {
|
||||
consumePendingMediaRequestSearch();
|
||||
const { integrationIds, query: searchQuery } = (event as CustomEvent<OpenMediaRequestSearchOptions>).detail ?? {};
|
||||
applyMediaRequestSearch({ integrationIds, query: searchQuery });
|
||||
};
|
||||
|
||||
window.addEventListener(mediaRequestSearchEvent, handleMediaRequestSearch);
|
||||
const pendingSearch = consumePendingMediaRequestSearch();
|
||||
if (pendingSearch) applyMediaRequestSearch(pendingSearch);
|
||||
return () => {
|
||||
window.removeEventListener(mediaRequestSearchEvent, handleMediaRequestSearch);
|
||||
};
|
||||
@@ -134,6 +152,7 @@ const SpotlightWithActiveMode = ({ modeState, queryState, activeMode }: Spotligh
|
||||
store={spotlightStore}
|
||||
>
|
||||
<MantineSpotlight.Search
|
||||
data-homarr-dev-benchmark-spotlight
|
||||
placeholder={`${t("search.placeholder")}...`}
|
||||
ref={inputRef}
|
||||
leftSectionWidth={hasModeCharacter ? 80 : 48}
|
||||
|
||||
@@ -1,28 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { mediaRequestSearchEvent, spotlightActions } from "./spotlight-store";
|
||||
|
||||
export { Spotlight } from "./components/spotlight";
|
||||
export { openSpotlight, openMediaRequestSearch };
|
||||
export { openSpotlight, openMediaRequestSearch, mediaRequestSearchEvent } from "./open";
|
||||
export {
|
||||
SpotlightProvider,
|
||||
useRegisterSpotlightContextResults,
|
||||
useRegisterSpotlightContextActions,
|
||||
} from "./modes/home/context";
|
||||
|
||||
const openSpotlight = spotlightActions.open;
|
||||
|
||||
export interface OpenMediaRequestSearchOptions {
|
||||
integrationIds?: string[];
|
||||
query?: string;
|
||||
}
|
||||
|
||||
const openMediaRequestSearch = (options: OpenMediaRequestSearchOptions = {}) => {
|
||||
if (typeof window !== "undefined") {
|
||||
window.dispatchEvent(new CustomEvent<OpenMediaRequestSearchOptions>(mediaRequestSearchEvent, { detail: options }));
|
||||
}
|
||||
|
||||
spotlightActions.open();
|
||||
};
|
||||
|
||||
export { mediaRequestSearchEvent };
|
||||
@@ -0,0 +1,54 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import {
|
||||
consumePendingMediaRequestSearch,
|
||||
consumePendingSpotlightOpen,
|
||||
hasPendingSpotlightOpen,
|
||||
mediaRequestSearchEvent,
|
||||
openMediaRequestSearch,
|
||||
openSpotlight,
|
||||
spotlightOpenEvent,
|
||||
} from "./open";
|
||||
|
||||
describe("lazy Spotlight open bridge", () => {
|
||||
beforeEach(() => {
|
||||
consumePendingSpotlightOpen();
|
||||
consumePendingMediaRequestSearch();
|
||||
});
|
||||
|
||||
it("keeps a pre-mount open request until the lazy component consumes it", () => {
|
||||
const listener = vi.fn();
|
||||
window.addEventListener(spotlightOpenEvent, listener);
|
||||
|
||||
openSpotlight();
|
||||
|
||||
expect(listener).toHaveBeenCalledOnce();
|
||||
expect(consumePendingSpotlightOpen()).toBe(true);
|
||||
expect(consumePendingSpotlightOpen()).toBe(false);
|
||||
window.removeEventListener(spotlightOpenEvent, listener);
|
||||
});
|
||||
|
||||
it("lets a listener installed after the request discover the pending open", () => {
|
||||
openSpotlight();
|
||||
|
||||
expect(hasPendingSpotlightOpen()).toBe(true);
|
||||
expect(consumePendingSpotlightOpen()).toBe(true);
|
||||
expect(hasPendingSpotlightOpen()).toBe(false);
|
||||
});
|
||||
|
||||
it("retains media options while dispatching both mount and media events", () => {
|
||||
const openListener = vi.fn();
|
||||
const mediaListener = vi.fn();
|
||||
window.addEventListener(spotlightOpenEvent, openListener);
|
||||
window.addEventListener(mediaRequestSearchEvent, mediaListener);
|
||||
|
||||
openMediaRequestSearch({ integrationIds: ["integration-a"], query: "movie" });
|
||||
|
||||
expect(openListener).toHaveBeenCalledOnce();
|
||||
expect(mediaListener).toHaveBeenCalledOnce();
|
||||
expect(consumePendingSpotlightOpen()).toBe(true);
|
||||
expect(consumePendingMediaRequestSearch()).toEqual({ integrationIds: ["integration-a"], query: "movie" });
|
||||
window.removeEventListener(spotlightOpenEvent, openListener);
|
||||
window.removeEventListener(mediaRequestSearchEvent, mediaListener);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
export const spotlightOpenEvent = "homarr:spotlight:open";
|
||||
export const mediaRequestSearchEvent = "homarr:spotlight:media-request-search";
|
||||
|
||||
export interface OpenMediaRequestSearchOptions {
|
||||
integrationIds?: string[];
|
||||
query?: string;
|
||||
}
|
||||
|
||||
let pendingMediaRequestSearch: OpenMediaRequestSearchOptions | null = null;
|
||||
let pendingSpotlightOpen = false;
|
||||
|
||||
const requestSpotlightMount = () => {
|
||||
if (typeof window !== "undefined") window.dispatchEvent(new Event(spotlightOpenEvent));
|
||||
};
|
||||
|
||||
export const openSpotlight = () => {
|
||||
pendingSpotlightOpen = true;
|
||||
requestSpotlightMount();
|
||||
};
|
||||
|
||||
export const consumePendingSpotlightOpen = () => {
|
||||
const pending = pendingSpotlightOpen;
|
||||
pendingSpotlightOpen = false;
|
||||
return pending;
|
||||
};
|
||||
|
||||
export const hasPendingSpotlightOpen = () => pendingSpotlightOpen;
|
||||
|
||||
export const consumePendingMediaRequestSearch = () => {
|
||||
const pending = pendingMediaRequestSearch;
|
||||
pendingMediaRequestSearch = null;
|
||||
return pending;
|
||||
};
|
||||
|
||||
export const openMediaRequestSearch = (options: OpenMediaRequestSearchOptions = {}) => {
|
||||
pendingMediaRequestSearch = options;
|
||||
openSpotlight();
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.dispatchEvent(new CustomEvent<OpenMediaRequestSearchOptions>(mediaRequestSearchEvent, { detail: options }));
|
||||
}
|
||||
};
|
||||
@@ -12,7 +12,6 @@ export interface MediaRequestSearchScope {
|
||||
}
|
||||
|
||||
export const mediaRequestSearchScopeAtom = atom<MediaRequestSearchScope>({});
|
||||
export const mediaRequestSearchEvent = "homarr:spotlight:media-request-search";
|
||||
|
||||
export const setSelectedAction = (index: number, store: SpotlightStore) => {
|
||||
store.updateState((state) => ({ ...state, selected: index }));
|
||||
|
||||
Reference in new issue
Block a user