UNPKG

preact-missing-hooks

Version:

A lightweight, extendable collection of missing React-like hooks for Preact — plus fresh, powerful new ones designed specifically for modern Preact apps.

221 lines (178 loc) 10.3 kB
# Preact Missing Hooks — Demo & Usage This folder contains a **live demo** of all hooks from `preact-missing-hooks`. Each hook is shown with a short flow description, example code, and an interactive “Live” panel. ## Running the demo ### Option 1: Local build + static server (recommended) From the project root: ```bash npm run build npx serve -l 5000 ``` Then open **http://localhost:5000/docs/** in your browser. The demo will load the built package from `../dist/index.module.js`. ### Option 2: Demo script From the project root you can use the existing demo script: ```bash npm run demo ``` Then open **http://localhost:5000/docs/** (or the port shown). ### Option 3: Unpkg (no build) Open `docs/index.html` in a browser that supports ES modules and import maps. The demo will load Preact and `preact-missing-hooks` from unpkg. Some features (e.g. `useWasmCompute` with a local WASM file) may be limited when opened as a file. --- ## What the demo includes | Hook | What you can try | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **useTransition** | Deferred state update; button shows “Pending…” then count. | | **useMutationObserver** | Add a node in the observed area; see mutation count. | | **useEventBus** | Emit “greet” and see the message in another part of the UI. | | **useWrappedChildren** | Children buttons get injected styles. | | **usePreferredTheme** | Shows light / dark / no-preference from system. | | **useNetworkState** | Online/offline and connection type. | | **useDeviceData** | Live browser/OS name & version (Client Hints + UA parse), language, CPUs, memory, viewport, screen, touch, theme prefs, UA brands, optional battery. | | **usePrefetch** | Hover or click to prefetch a URL (document or fetch); see prefetched status. | | **usePoll** | Poll until done (3 ticks); see poll count and result. Stop button to cancel. | | **useClipboard** | Copy and paste; see “Copied!” and pasted text. | | **useRageClick** | Click the area 3+ times quickly; rage click count. | | **useThreadedWorker** | Run a task; see loading and result. | | **useIndexedDB** | Insert, bulk insert, query, update, delete, clear. | | **useWebRTCIP** | Detect IP via WebRTC (may take a few seconds). | | **useWasmCompute** | Run WASM in a worker (needs `add.wasm` in docs). | | **useWorkerNotifications** | Run/fail tasks and queue updates; toasts show events. | | **useRefPrint** | Bind a ref to a section and click “Print / Save as PDF”; only that section is printed via `@media print`. | | **useRBAC** | Login as Admin / Editor / Viewer (localStorage or sessionStorage); see roles and capabilities; conditional UI by `can(...)`. | | **useLLMMetadata** | Change “route” with buttons; see injected script info in the Live panel and `<script data-llm="true">` in the document head. Safe with `null`/`undefined` config (minimal payload with `route: "/"`). | --- ## Demo usage patterns ### Minimal Preact example ```js import { h, render } from "preact"; import { useState } from "preact/hooks"; import { useClipboard, usePreferredTheme } from "preact-missing-hooks"; function App() { const { copy, copied } = useClipboard(); const theme = usePreferredTheme(); return h( "div", {}, h("button", { onClick: () => copy("Hi") }, copied ? "Copied!" : "Copy"), h("span", {}, " Theme: " + theme) ); } render(h(App), document.getElementById("root")); ``` ### With React ```jsx import { useTransition, useLLMMetadata } from "preact-missing-hooks/react"; import { useLocation } from "react-router-dom"; function App() { const { pathname } = useLocation(); useLLMMetadata({ route: pathname, mode: "auto-extract", tags: ["my-app"] }); const [startTransition, isPending] = useTransition(); // ... } ``` ### useRefPrint example Print only a specific section via the native print dialog (user can save as PDF): ```js import { h, render } from "preact"; import { useRef } from "preact/hooks"; import { useRefPrint } from "preact-missing-hooks"; function Report() { const printRef = useRef(null); const { print } = useRefPrint(printRef, { documentTitle: "My Report", downloadAsPdf: true, }); return h( "div", {}, h( "div", { ref: printRef, style: { padding: "1rem", background: "#f5f5f5" } }, "Only this section is printed when you click the button." ), h("button", { onClick: print }, "Print / Save as PDF") ); } render(h(Report), document.getElementById("root")); ``` ### useRBAC example Frontend-only role-based access: define roles with conditions, assign capabilities per role, and read the current user from localStorage (or sessionStorage / API): ```js import { h, render } from "preact"; import { useRBAC } from "preact-missing-hooks"; const roleDefinitions = [ { role: "admin", condition: (u) => u && u.role === "admin" }, { role: "editor", condition: (u) => u && (u.role === "editor" || u.role === "admin"), }, { role: "viewer", condition: (u) => u && u.id != null }, ]; const roleCapabilities = { admin: ["*"], editor: ["posts:edit", "posts:create", "posts:read"], viewer: ["posts:read"], }; function App() { const { user, roles, can, setUserInStorage } = useRBAC({ userSource: { type: "localStorage", key: "app-user" }, roleDefinitions, roleCapabilities, }); if (!user) { return h( "div", {}, h( "button", { onClick: () => setUserInStorage( { id: 1, role: "admin" }, "localStorage", "app-user" ), }, "Login as Admin" ), h( "button", { onClick: () => setUserInStorage( { id: 2, role: "viewer" }, "localStorage", "app-user" ), }, "Login as Viewer" ) ); } return h( "div", {}, h("p", {}, "Roles: " + roles.join(", ")), can("posts:edit") && h("button", {}, "Edit post"), can("*") && h("button", {}, "Admin panel"), h( "button", { onClick: () => setUserInStorage(null, "localStorage", "app-user") }, "Logout" ) ); } render(h(App), document.getElementById("root")); ``` ### useLLMMetadata in the demo The **useLLMMetadata** Live panel simulates route changes. Click “Route: /”, “Route: /blog”, or “Route: /docs”. Each change: 1. Removes any existing `<script data-llm="true">`. 2. Injects a new script with `type="application/llm+json"` containing `route`, `title`, `description`, `tags`, and `generatedAt`. Inspect the page’s `<head>` in DevTools to see the script. The Live panel shows a short summary of the injected payload. The hook never throws: passing `null` or `undefined` as config is safe and yields a minimal payload with `route: "/"` and `generatedAt`. --- ## File layout - **index.html** — Page shell, styles, and import map; mounts the app into `#root`. - **main.js** — Imports the package and Preact, defines demo components and the `HOOKS` list, renders the app. To add a new hook to the demo: 1. Import it in `main.js` from the package. 2. Implement a `DemoXxx` component that uses the hook. 3. Add an entry to the `HOOKS` array with `name`, `flow`, `summary`, `code`, and `Live: DemoXxx`.