diff --git a/.changeset/tricky-suns-occur.md b/.changeset/tricky-suns-occur.md new file mode 100644 index 00000000..5e13aa41 --- /dev/null +++ b/.changeset/tricky-suns-occur.md @@ -0,0 +1,5 @@ +--- +"runed": minor +--- + +feat: `useSupported` diff --git a/packages/runed/src/lib/functions/index.ts b/packages/runed/src/lib/functions/index.ts index b9550f29..79a35ccb 100644 --- a/packages/runed/src/lib/functions/index.ts +++ b/packages/runed/src/lib/functions/index.ts @@ -4,3 +4,4 @@ export * from "./useDebounce/index.js"; export * from "./useElementSize/index.js"; export * from "./useEventListener/index.js"; export * from "./useMounted/index.js"; +export * from "./useSupported/index.js"; diff --git a/packages/runed/src/lib/functions/useActiveElement/useActiveElement.svelte.ts b/packages/runed/src/lib/functions/useActiveElement/useActiveElement.svelte.ts index 703cf39d..600b86d6 100644 --- a/packages/runed/src/lib/functions/useActiveElement/useActiveElement.svelte.ts +++ b/packages/runed/src/lib/functions/useActiveElement/useActiveElement.svelte.ts @@ -1,14 +1,15 @@ +import { type ReadableBox, box } from "../box/box.svelte.js"; import { isBrowser } from "$lib/internal/utils/browser.js"; /** * Returns a reactive value that is equal to `document.activeElement`. * It automatically listens for changes, keeping the reference up to date. * - * @returns an object with a reactive value `value` that is equal to `document.activeElement`, or `null` - * if there's no active element. + * @returns an object with a reactive value `value` that is equal to `document.activeElement`, + * or `null` if there's no active element. */ -export function useActiveElement(): { value: Readonly } { - const activeElement = $state({ value: isBrowser() ? document.activeElement : null }); +export function useActiveElement(): ReadableBox { + const activeElement = box(isBrowser() ? document.activeElement : null); function onFocusChange() { activeElement.value = document.activeElement; @@ -24,5 +25,5 @@ export function useActiveElement(): { value: Readonly } { }; }); - return activeElement; + return box.readonly(activeElement); } diff --git a/packages/runed/src/lib/functions/useElementSize/useElementSize.svelte.ts b/packages/runed/src/lib/functions/useElementSize/useElementSize.svelte.ts index 30d97567..16c293ce 100644 --- a/packages/runed/src/lib/functions/useElementSize/useElementSize.svelte.ts +++ b/packages/runed/src/lib/functions/useElementSize/useElementSize.svelte.ts @@ -1,7 +1,7 @@ import { box } from "../box/box.svelte.js"; import type { MaybeBoxOrGetter } from "$lib/internal/types.js"; -type Options = { +export type UseElementSizeOptions = { initialSize?: { width: number; height: number; @@ -20,7 +20,7 @@ type Options = { */ export function useElementSize( _node: MaybeBoxOrGetter, - options: Options = { + options: UseElementSizeOptions = { box: "border-box", } ): { width: number; height: number } { diff --git a/packages/runed/src/lib/functions/useMounted/useMounted.svelte.ts b/packages/runed/src/lib/functions/useMounted/useMounted.svelte.ts index 6d7d7f54..f090ee16 100644 --- a/packages/runed/src/lib/functions/useMounted/useMounted.svelte.ts +++ b/packages/runed/src/lib/functions/useMounted/useMounted.svelte.ts @@ -10,6 +10,10 @@ export function useMounted(): ReadableBox { $effect(() => { untrack(() => (isMounted.value = true)); + + return () => { + isMounted.value = false; + }; }); return box.readonly(isMounted); diff --git a/packages/runed/src/lib/functions/useSupported/index.ts b/packages/runed/src/lib/functions/useSupported/index.ts new file mode 100644 index 00000000..c3421e3f --- /dev/null +++ b/packages/runed/src/lib/functions/useSupported/index.ts @@ -0,0 +1 @@ +export * from "./useSupported.svelte.js"; diff --git a/packages/runed/src/lib/functions/useSupported/useSupported.svelte.ts b/packages/runed/src/lib/functions/useSupported/useSupported.svelte.ts new file mode 100644 index 00000000..019688e2 --- /dev/null +++ b/packages/runed/src/lib/functions/useSupported/useSupported.svelte.ts @@ -0,0 +1,25 @@ +import { type ReadableBox, box } from "../box/box.svelte.js"; + +/** + * A hook that takes a predicate determine if a browser API is supported. + * + * Useful for checking if a browser API is supported before attempting to use it. + * + * @example + * ```ts + * const isSupported = useSupported(() => navigator); + * + * if (isSupported) { + * // do something with navigator + * } + * ``` + */ +export function useSupported(predicate: () => unknown): ReadableBox { + const isSupported = box(false); + + $effect(() => { + isSupported.value = Boolean(predicate()); + }); + + return box.readonly(isSupported); +} diff --git a/sites/docs/content/functions/use-supported.md b/sites/docs/content/functions/use-supported.md new file mode 100644 index 00000000..a12c2eba --- /dev/null +++ b/sites/docs/content/functions/use-supported.md @@ -0,0 +1,18 @@ +--- +title: useSupported +description: Determine if a feature is supported by the environment before using it. +--- + +## Usage + +```svelte + +``` diff --git a/sites/docs/mdsx.config.js b/sites/docs/mdsx.config.js index 49c45cac..742e2679 100644 --- a/sites/docs/mdsx.config.js +++ b/sites/docs/mdsx.config.js @@ -64,7 +64,7 @@ const prettyCodeOptions = { export const mdsxConfig = defineConfig({ extensions: [".md"], - remarkPlugins: [remarkGfm, remarkRemovePrettierIgnore], + remarkPlugins: [remarkGfm, remarkRemovePrettierIgnore, remarkTabsToSpaces], rehypePlugins: [[rehypePrettyCode, prettyCodeOptions], rehypeHandleMetadata, rehypeSlug], blueprints: { default: { @@ -100,6 +100,22 @@ function remarkRemovePrettierIgnore() { }; } +/** + * Converts tabs to spaces in code blocks to fit more into the viewport. + * + * @returns {MdastTransformer} - A unified transformer + * + */ +function remarkTabsToSpaces() { + return async (tree) => { + visit(tree, "code", (node) => { + node.value = node.value + // @ts-expect-error - not dealing with this rn + .replaceAll("\t", " "); + }); + }; +} + /** * Adds `data-metadata` to `
` elements that contain a `
`. * We use this to style elements within the `
` differently if a `
` diff --git a/sites/docs/src/lib/config/navigation.ts b/sites/docs/src/lib/config/navigation.ts index 73f3fc4e..4a94194b 100644 --- a/sites/docs/src/lib/config/navigation.ts +++ b/sites/docs/src/lib/config/navigation.ts @@ -91,6 +91,11 @@ export const navigation: Navigation = { href: "/docs/functions/use-mounted", items: [], }, + { + title: "useSupported", + href: "/docs/functions/use-supported", + items: [], + }, ], }, ],