npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shak-hooks/angular

v0.0.2

Published

Angular signals/services for Shak Hooks

Readme

@shak-hooks/angular

Angular Signals utilities for Shak Hooks.

Install

npm i @shak-hooks/angular

Requirements

  • Angular @angular/core >=16

Usage

Most hooks return Angular Signal / WritableSignal values (read with signal() and update with .set() / .update()).

import { Component, signal } from "@angular/core";
import { useCounter } from "@shak-hooks/angular";

@Component({
  selector: "app-counter",
  standalone: true,
  template: `
    <button (click)="dec()">-</button>
    <span>{{ count() }}</span>
    <button (click)="inc()">+</button>
  `,
})
export class CounterComponent {
  readonly countHook = useCounter(0);
  readonly count = this.countHook[0];
  private readonly actions = this.countHook[1];

  inc = () => this.actions.inc();
  dec = () => this.actions.dec();
}

Testing

Run only the Angular hook tests from the repo root:

pnpm test:angular

Injection Context & Cleanup

Some hooks use Angular DI (inject(DestroyRef) / inject(PLATFORM_ID)) for automatic cleanup. Use them inside an injection context (e.g. a component constructor, runInInjectionContext, or pass an Injector parameter when the hook supports it).

Hooks

  • useBattery
  • useClickAway
  • useContinuousRetry
  • useCopyToClipboard
  • useCountdown
  • useCounter
  • useDebounce
  • useDefault
  • useDocumentTitle
  • useEventListener
  • useFavicon
  • useFetch
  • useGeolocation
  • useHistoryState
  • useHover
  • useIdle
  • useIntersectionObserver
  • useInterval
  • useIntervalWhen
  • useIsClient
  • useKeyPress
  • useList
  • useLocalStorage
  • useLockBodyScroll
  • useLogger
  • useLongPress
  • useMap
  • useMeasure
  • useMediaQuery
  • useMouse
  • useNetworkState
  • useObjectState
  • useOrientation
  • usePageLeave
  • usePreferredLanguage
  • usePrevious
  • useQueue
  • useRandomInterval
  • useScript
  • useSessionStorage
  • useSet
  • useThrottle
  • useTimeout
  • useToggle
  • useVisibilityChange
  • useWindowScroll
  • useWindowSize
  • useIsFirstRender
  • useRenderCount
  • useRenderInfo

API

useBattery

Tracks the Battery Status API (when supported).

const { level, charging } = useBattery();
console.log(level(), charging());

useClickAway

Calls a handler when a click happens outside the target element.

const target = signal<HTMLElement | null>(null);
useClickAway(target, () => console.log("outside"));

useContinuousRetry

Retries an async/sync callback at a fixed interval until it returns true or hits maxRetries.

const done = useContinuousRetry(() => Boolean(localStorage.getItem("ready")), 200, { maxRetries: 10 });

useCopyToClipboard

Copies text to the clipboard.

const { value, copy, error } = useCopyToClipboard();
await copy("hello");
console.log(value(), error());

useCountdown

Countdown timer with controls (requires an injection context for cleanup; also supports passing an Injector).

const { count, start, stop, reset } = useCountdown(10);
console.log(count());

useCounter

Counter with optional min/max and helpers.

const [count, { inc, dec, set, reset }] = useCounter(0, { min: 0, max: 10 });

useDebounce

Debounces a Signal<T> and returns a Signal<T>.

const search = signal("");
const debounced = useDebounce(search, 250);

useDefault

Returns a WritableSignal plus a computed/defaulted Signal value.

const [state, setState, value] = useDefault<string | null>(null, "fallback");

useDocumentTitle

Sets document.title (with optional restore on destroy).

useDocumentTitle("Dashboard", { restoreOnDestroy: true });

useEventListener

Adds an event listener to window (default) or a target/Signal target.

useEventListener("click", () => console.log("clicked"));

useFavicon

Sets the favicon URL (supports passing an Injector).

const href = signal("/favicon.ico");
useFavicon(href, injector);

useFetch

Reactive fetch helper with data, error, loading, execute, abort.

const url = signal("/api/health");
const { data, loading, error } = useFetch<{ ok: boolean }>(url);

useGeolocation

Tracks the Geolocation API (when supported).

const { coords, locatedAt, error } = useGeolocation();

useHistoryState

Stores state in history.state under a key.

const { state, setHistoryState } = useHistoryState({ tab: "home" }, "page-state");

useHover

Tracks hover state for an element target.

const hovered = useHover(signal<HTMLElement | null>(null));

useIdle

Becomes true after ms of no activity; resets on user activity (supports passing an Injector).

const idle = useIdle(60_000);
console.log(idle());

useIntersectionObserver

IntersectionObserver wrapper (returns a Signal<IntersectionObserverEntry | null>).

const target = signal<Element | null>(null);
const entry = useIntersectionObserver(target, { threshold: 0.1 });

useInterval

Interval runner with delay: number | null (pass null to pause).

useInterval(() => console.log("tick"), 1000);

useIntervalWhen

Interval runner gated by a boolean condition.

useIntervalWhen(() => console.log("tick"), 1000, true, true);

useIsClient

Checks the Angular PLATFORM_ID (true when PLATFORM_ID === "browser").

const isClient = useIsClient();

useKeyPress

Tracks whether a specific key is currently pressed.

const pressed = useKeyPress("Escape");

useList

List state with helper actions.

const [list, actions] = useList<string>(["a"]);
actions.push("b");

useLocalStorage

LocalStorage-backed WritableSignal<T>.

const value = useLocalStorage("theme", "light");

useLockBodyScroll

Locks body scroll (sets body.style.overflow = "hidden") and restores on destroy (supports passing an Injector).

const locked = signal(true);
useLockBodyScroll(locked, injector);

useLogger

Console logging helper (logs mount; logs unmount when used inside an injection context).

useLogger("MyComponent", { debug: true });

useLongPress

Long-press detection for mouse/touch with callbacks.

const handlers = useLongPress(() => console.log("long press"), { delay: 500 });

useMap

Map state with helper actions.

const [map, actions] = useMap<string, number>([["a", 1]]);
actions.set("b", 2);

useMeasure

Element measurement via ResizeObserver.

const [ref, rect] = useMeasure();

useMediaQuery

Tracks a media query match.

const matches = useMediaQuery("(min-width: 768px)");

useMouse

Tracks mouse position.

const { x, y } = useMouse();

useNetworkState

Tracks network status (online/offline + connection info where available).

const state = useNetworkState();

useObjectState

Object state with merge update.

const { state, update } = useObjectState({ a: 1, b: 2 });
update({ b: 3 });

useOrientation

Tracks device orientation events (where available).

const state = useOrientation();

usePageLeave

Calls a callback when the mouse leaves the page viewport (supports passing an Injector).

usePageLeave(() => console.log("left page"));

usePreferredLanguage

Returns the user’s preferred language (when available).

const lang = usePreferredLanguage();

usePrevious

Returns the previous value of a Signal<T>.

const prev = usePrevious(currentSignal);

useQueue

Queue state with helper actions.

const [queue, actions] = useQueue<number>([1, 2]);
actions.add(3);

useRandomInterval

Runs a callback at random intervals between minDelay and maxDelay.

const cleanup = useRandomInterval(() => console.log("tick"), 500, 1500);
cleanup();

useIsFirstRender

Signal<boolean> that flips to false after the first render.

const isFirst = useIsFirstRender(injector);
console.log(isFirst());

useRenderCount

Signal<number> that increments after each render.

const renderCount = useRenderCount(injector);
console.log(renderCount());

useRenderInfo

Logs basic render timing information after each render.

useRenderInfo("MyComponent", injector);

useScript

Loads an external script and returns its status signal.

const status = useScript("https://example.com/sdk.js");
console.log(status());

useSessionStorage

SessionStorage-backed WritableSignal<T>.

const value = useSessionStorage("draft", "");

useSet

Set state with helper actions.

const [set, actions] = useSet<string>(["a"]);
actions.add("b");

useThrottle

Throttles a rapidly changing value/signal.

const value = signal("a");
const throttled = useThrottle(value, 250);

useTimeout

Runs a callback after delay (pass null to disable).

useTimeout(() => console.log("done"), 1000);

useToggle

Boolean toggle with helper actions.

const [on, actions] = useToggle(false);
actions.toggle();

useVisibilityChange

Tracks whether the document is visible.

const visible = useVisibilityChange();

useWindowScroll

Tracks window scroll position.

const { x, y } = useWindowScroll();

useWindowSize

Tracks window size.

const { width, height } = useWindowSize();