import { watch, type Ref, onUnmounted } from 'vue'; export function useGlobalCursor( isActive: Readonly>, cursorCssValue: string, documentAccessor: CursorStyleDomModifier = new GlobalDocumentCursorStyleDomModifier(), ) { const cursorStyle = createCursorStyle(cursorCssValue, documentAccessor); watch(isActive, (isCursorVisible) => { if (isCursorVisible) { documentAccessor.appendStyleToHead(cursorStyle); } else { documentAccessor.removeElement(cursorStyle); } }); onUnmounted(() => { documentAccessor.removeElement(cursorStyle); }); } function createCursorStyle( cursorCssValue: string, documentAccessor: CursorStyleDomModifier, ): HTMLStyleElement { // Using `document.body.style.cursor` does not override cursor when hovered on input boxes, // buttons etc. so we create a custom style that will do that const cursorStyle = documentAccessor.createStyleElement(); cursorStyle.innerHTML = `*{cursor: ${cursorCssValue}!important;}`; return cursorStyle; } export interface CursorStyleDomModifier { appendStyleToHead(element: HTMLStyleElement): void; removeElement(element: HTMLStyleElement): void; createStyleElement(): HTMLStyleElement; } class GlobalDocumentCursorStyleDomModifier implements CursorStyleDomModifier { public appendStyleToHead(element: HTMLStyleElement): void { document.head.appendChild(element); } public removeElement(element: HTMLStyleElement): void { element.remove(); } public createStyleElement(): HTMLStyleElement { return document.createElement('style'); } }