-
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
482 additions
and
212 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"runed": minor | ||
--- | ||
|
||
feat: Configurable globals (`window`, `document`, `navigator`, etc.) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// configurable-globals.ts | ||
import { BROWSER } from "esm-env"; | ||
|
||
export type ConfigurableWindow = { | ||
/** Provide a custom `window` object to use in place of the global `window` object. */ | ||
window?: typeof globalThis & Window; | ||
}; | ||
|
||
export type ConfigurableDocument = { | ||
/** Provide a custom `document` object to use in place of the global `document` object. */ | ||
document?: Document; | ||
}; | ||
|
||
export type ConfigurableDocumentOrShadowRoot = { | ||
/* | ||
* Specify a custom `document` instance or a shadow root, e.g. working with iframes or in testing environments. | ||
*/ | ||
document?: DocumentOrShadowRoot; | ||
}; | ||
|
||
export type ConfigurableNavigator = { | ||
/** Provide a custom `navigator` object to use in place of the global `navigator` object. */ | ||
navigator?: Navigator; | ||
}; | ||
|
||
export type ConfigurableLocation = { | ||
/** Provide a custom `location` object to use in place of the global `location` object. */ | ||
location?: Location; | ||
}; | ||
|
||
export const defaultWindow = /* #__PURE__ */ BROWSER ? window : undefined; | ||
export const defaultDocument = /* #__PURE__ */ BROWSER ? window.document : undefined; | ||
export const defaultNavigator = /* #__PURE__ */ BROWSER ? window.navigator : undefined; | ||
export const defaultLocation = /* #__PURE__ */ BROWSER ? window.location : undefined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
export type Getter<T> = () => T; | ||
export type MaybeGetter<T> = T | Getter<T>; | ||
export type MaybeElementGetter<T extends Element = HTMLElement> = MaybeGetter<T | null | undefined>; | ||
export type Setter<T> = (value: T) => void; | ||
|
||
export type Expand<T> = T extends infer U ? { [K in keyof U]: U[K] } : never; | ||
export type WritableProperties<T> = { | ||
-readonly [P in keyof T]: T[P]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* Handles getting the active element in a document or shadow root. | ||
* If the active element is within a shadow root, it will traverse the shadow root | ||
* to find the active element. | ||
* If not, it will return the active element in the document. | ||
* | ||
* @param document A document or shadow root to get the active element from. | ||
* @returns The active element in the document or shadow root. | ||
*/ | ||
export function getActiveElement(document: DocumentOrShadowRoot): HTMLElement | null { | ||
let activeElement = document.activeElement as HTMLElement | null; | ||
|
||
while (activeElement?.shadowRoot) { | ||
const node = activeElement.shadowRoot.activeElement as HTMLElement | null; | ||
if (node === activeElement) break; | ||
else activeElement = node; | ||
} | ||
|
||
return activeElement; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1 @@ | ||
export function noop(): void { | ||
// noop | ||
} | ||
export function noop(): void {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 14 additions & 6 deletions
20
packages/runed/src/lib/utilities/IsFocusWithin/IsFocusWithin.svelte.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,29 @@ | ||
import { extract } from "../extract/extract.svelte.js"; | ||
import { activeElement } from "../activeElement/activeElement.svelte.js"; | ||
import type { MaybeGetter } from "$lib/internal/types.js"; | ||
import type { MaybeElementGetter } from "$lib/internal/types.js"; | ||
import { | ||
type ConfigurableDocumentOrShadowRoot, | ||
type ConfigurableWindow, | ||
} from "$lib/internal/configurable-globals.js"; | ||
import { useActiveElement } from "../useActiveElement/useActiveElement.svelte.js"; | ||
|
||
type IsFocusWithinOptions = ConfigurableDocumentOrShadowRoot & ConfigurableWindow; | ||
|
||
/** | ||
* Tracks whether the focus is within a target element. | ||
* @see {@link https://runed.dev/docs/utilities/is-focus-within} | ||
*/ | ||
export class IsFocusWithin { | ||
#node: MaybeGetter<HTMLElement | undefined | null>; | ||
#node: MaybeElementGetter; | ||
#target = $derived.by(() => extract(this.#node)); | ||
#activeElement: ReturnType<typeof useActiveElement>; | ||
|
||
constructor(node: MaybeGetter<HTMLElement | undefined | null>) { | ||
constructor(node: MaybeElementGetter, options: IsFocusWithinOptions = {}) { | ||
this.#node = node; | ||
this.#activeElement = useActiveElement({ document: options.document, window: options.window }); | ||
} | ||
|
||
readonly current = $derived.by(() => { | ||
if (!this.#target || !activeElement.current) return false; | ||
return this.#target.contains(activeElement.current); | ||
if (!this.#target || !this.#activeElement.current) return false; | ||
return this.#target.contains(this.#activeElement.current); | ||
}); | ||
} |
Oops, something went wrong.