Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# `useMobile()` — viewport-width breakpoint hook
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Returns `true` when the viewport width is at or below the supplied
|
||||
`breakpointPx` (defaulting to 768px, which mirrors Tailwind's `md`
|
||||
breakpoint). The hook subscribes to a `matchMedia` listener — NOT a
|
||||
polling timer — so layout changes propagate without forcing a re-render
|
||||
on every scroll/resize tick. SSR-safe.
|
||||
|
||||
## Features
|
||||
|
||||
- Subscribes via `matchMedia` so re-renders happen only on actual
|
||||
threshold crossings
|
||||
- Returns `false` server-side (no `window`) so SSR HTML matches the
|
||||
desktop layout by default
|
||||
- Configurable breakpoint per-call site
|
||||
- Cleans up the listener on unmount
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```tsx
|
||||
import { useMobile } from "@suaveplan/react-ui/hooks/use-mobile";
|
||||
|
||||
function Layout() {
|
||||
const isMobile = useMobile();
|
||||
return <div data-mobile={isMobile}>{isMobile ? <Drawer /> : <Sidebar />}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```tsx
|
||||
import { useMobile } from "@suaveplan/react-ui/hooks/use-mobile";
|
||||
|
||||
function ResponsiveTable() {
|
||||
const isCompact = useMobile(640);
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
{isCompact ? <th>Item</th> : <><th>SKU</th><th>Name</th><th>Price</th></>}
|
||||
</thead>
|
||||
…
|
||||
</table>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `useMobile(breakpointPx?: number = 768): boolean`
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The hook holds a `useState` initialised by reading
|
||||
`window.matchMedia(...).matches` synchronously inside the initialiser
|
||||
when `window` is available — this gets the post-mount state right
|
||||
without an extra render. The post-mount `useEffect` then calls the
|
||||
listener once to sync against any state drift between SSR and the
|
||||
client. The `change` listener tears down on unmount AND when
|
||||
`breakpointPx` changes so a parent that swaps the breakpoint mid-life
|
||||
gets a fresh subscription. The chassis defaults to 768px (Tailwind's
|
||||
`md` boundary) but consumers picking other breakpoints get the same
|
||||
zero-extra-render behaviour. Defensive guards: when `matchMedia`
|
||||
itself throws (some restricted-permissions browsers), the listener
|
||||
setup short-circuits and `isMobile` stays at its initial value.
|
||||
@@ -0,0 +1,91 @@
|
||||
# `useToast()` — global toast queue
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Global toast queue exposed as a single hook so any component anywhere
|
||||
in the React tree can enqueue a toast. The chassis treats toasts as a
|
||||
process-wide UX surface — not a per-tree concern — so the queue lives
|
||||
in module scope rather than under a Provider. A single root-level
|
||||
renderer (the `Sonner` component shipping in Wave 3g) reads the queue
|
||||
and renders the active items.
|
||||
|
||||
## Features
|
||||
|
||||
- `useToast()` returns the live queue, the `toast(...)` enqueue
|
||||
callback, and the `dismiss(id)` callback
|
||||
- Subscribed via `useSyncExternalStore` so re-renders happen only on
|
||||
actual queue changes
|
||||
- Branded `TranslationKey` typing on `titleKey` / `descriptionKey`
|
||||
prevents hardcoded strings from leaking through the toast surface
|
||||
- OTel counter `react_ui.toast.shown_total{toast.kind}` increments on
|
||||
every enqueue
|
||||
- `resetToastQueueForTests()` exposed so tests can isolate per-case
|
||||
state
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```tsx
|
||||
import { useToast } from "@suaveplan/react-ui/hooks/use-toast";
|
||||
|
||||
function SaveButton() {
|
||||
const { toast } = useToast();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
toast({
|
||||
titleKey: "react-ui.loading",
|
||||
kind: "success",
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```tsx
|
||||
import { useToast } from "@suaveplan/react-ui/hooks/use-toast";
|
||||
|
||||
function ToastList() {
|
||||
const { toasts, dismiss } = useToast();
|
||||
return (
|
||||
<ul>
|
||||
{toasts.map((t) => (
|
||||
<li
|
||||
key={t.id}
|
||||
data-kind={t.kind}
|
||||
onClick={() => dismiss(t.id)}
|
||||
>
|
||||
[{t.kind}] {t.titleKey}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `useToast(): { toasts, toast, dismiss }`
|
||||
- `toast(input: ToastInput): string` — returns the new toast `id`
|
||||
- `dismiss(id: string): void`
|
||||
- `interface ToastItem` — `{ id, kind, titleKey, descriptionKey?, createdAt }`
|
||||
- `type ToastKind = 'default' | 'success' | 'error' | 'info' | 'warning'`
|
||||
- `resetToastQueueForTests(): void` — test isolation only
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The queue is intentionally module-level state subscribed via
|
||||
`useSyncExternalStore` so the React 18+ concurrent-mode guarantees
|
||||
about subscription consistency apply. Tests that exercise the hook
|
||||
should call `resetToastQueueForTests()` in `afterEach` to clear the
|
||||
queue and the listener set; otherwise listener references from prior
|
||||
tests can leak across the file boundary in some runners. The OTel
|
||||
counter fires on enqueue; dismissal does not record telemetry by
|
||||
design (auto-dismiss timers in the renderer would otherwise emit
|
||||
duplicates).
|
||||
Reference in New Issue
Block a user