@ahrowe/ui 0.37.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cjs-types/common/commandPalette/commandPalette.d.ts +4 -0
- package/dist/cjs-types/common/commandPalette/commandPalette.types.d.ts +42 -0
- package/dist/cjs-types/common/commandPalette/filterCommands.d.ts +15 -0
- package/dist/cjs-types/common/commandPalette/index.d.ts +2 -0
- package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +6 -0
- package/dist/cjs-types/common/copyButton/copyButton.d.ts +4 -0
- package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +36 -0
- package/dist/cjs-types/common/copyButton/index.d.ts +2 -0
- package/dist/cjs-types/common/errorBoundary/errorBoundary.d.ts +0 -3
- package/dist/cjs-types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
- package/dist/cjs-types/common/hooks/index.d.ts +2 -0
- package/dist/cjs-types/common/hooks/useClipboard.d.ts +16 -0
- package/dist/cjs-types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
- package/dist/cjs-types/common/menu/menu.types.d.ts +2 -0
- package/dist/cjs-types/common/themeProvider/themeProvider.d.ts +8 -7
- package/dist/cjs-types/common/weatherIcon/index.d.ts +3 -0
- package/dist/cjs-types/common/weatherIcon/parts.d.ts +26 -0
- package/dist/cjs-types/common/weatherIcon/weatherIcon.d.ts +4 -0
- package/dist/cjs-types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
- package/dist/cjs-types/index.d.ts +6 -0
- package/dist/esm/common/buttonGroup/buttonGroup.module.mjs.map +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs +2 -0
- package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -0
- package/dist/esm/common/commandPalette/commandPalette.module.mjs +2 -0
- package/dist/esm/common/commandPalette/commandPalette.module.mjs.map +1 -0
- package/dist/esm/common/commandPalette/filterCommands.mjs +2 -0
- package/dist/esm/common/commandPalette/filterCommands.mjs.map +1 -0
- package/dist/esm/common/copyButton/copyButton.mjs +2 -0
- package/dist/esm/common/copyButton/copyButton.mjs.map +1 -0
- package/dist/esm/common/copyButton/copyButton.module.mjs +2 -0
- package/dist/esm/common/copyButton/copyButton.module.mjs.map +1 -0
- package/dist/esm/common/dropdown/dropdown.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
- package/dist/esm/common/errorBoundary/errorBoundary.mjs.map +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.module.mjs +1 -1
- package/dist/esm/common/errorBoundary/errorBoundary.module.mjs.map +1 -1
- package/dist/esm/common/hooks/useClipboard.mjs +2 -0
- package/dist/esm/common/hooks/useClipboard.mjs.map +1 -0
- package/dist/esm/common/inputDropdown/inputDropdown.mjs +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.mjs.map +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.module.mjs +1 -1
- package/dist/esm/common/inputDropdown/inputDropdown.module.mjs.map +1 -1
- package/dist/esm/common/menu/menu.mjs +1 -1
- package/dist/esm/common/menu/menu.mjs.map +1 -1
- package/dist/esm/common/menu/menu.types.mjs.map +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
- package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
- package/dist/esm/common/timer/timer.mjs +1 -1
- package/dist/esm/common/timer/timer.mjs.map +1 -1
- package/dist/esm/common/weatherIcon/parts.mjs +2 -0
- package/dist/esm/common/weatherIcon/parts.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.module.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.module.mjs.map +1 -0
- package/dist/esm/common/weatherIcon/weatherIcon.types.mjs +2 -0
- package/dist/esm/common/weatherIcon/weatherIcon.types.mjs.map +1 -0
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/common/commandPalette/commandPalette.d.ts +4 -0
- package/dist/types/common/commandPalette/commandPalette.types.d.ts +42 -0
- package/dist/types/common/commandPalette/filterCommands.d.ts +15 -0
- package/dist/types/common/commandPalette/index.d.ts +2 -0
- package/dist/types/common/configProvider/configProvider.types.d.ts +6 -0
- package/dist/types/common/copyButton/copyButton.d.ts +4 -0
- package/dist/types/common/copyButton/copyButton.types.d.ts +36 -0
- package/dist/types/common/copyButton/index.d.ts +2 -0
- package/dist/types/common/errorBoundary/errorBoundary.d.ts +0 -3
- package/dist/types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
- package/dist/types/common/hooks/index.d.ts +2 -0
- package/dist/types/common/hooks/useClipboard.d.ts +16 -0
- package/dist/types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
- package/dist/types/common/menu/menu.types.d.ts +2 -0
- package/dist/types/common/themeProvider/themeProvider.d.ts +8 -7
- package/dist/types/common/weatherIcon/index.d.ts +3 -0
- package/dist/types/common/weatherIcon/parts.d.ts +26 -0
- package/dist/types/common/weatherIcon/weatherIcon.d.ts +4 -0
- package/dist/types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
- package/dist/types/index.d.ts +6 -0
- package/docs/ButtonGroup.md +2 -0
- package/docs/CLAUDE.md +4 -1
- package/docs/CommandPalette.md +84 -0
- package/docs/ConfigProvider.md +2 -2
- package/docs/CopyButton.md +45 -0
- package/docs/ErrorBoundary.md +4 -4
- package/docs/Fab.md +1 -2
- package/docs/Hooks.md +39 -3
- package/docs/InputDropdown.md +9 -4
- package/docs/Menu.md +5 -3
- package/docs/WeatherIcon.md +49 -0
- package/package.json +1 -2
package/docs/Hooks.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Hooks
|
|
2
2
|
|
|
3
|
-
**When to use:** Standalone React hooks the library uses internally and publishes for the same jobs in your own code: keyboard shortcuts, media queries, the mobile keyboard's viewport, scroll-edge detection, swipe-to-dismiss, and the Escape / focus-trap / scroll-lock behaviour behind a custom overlay.
|
|
3
|
+
**When to use:** Standalone React hooks the library uses internally and publishes for the same jobs in your own code: keyboard shortcuts, media queries, the mobile keyboard's viewport, scroll-edge detection, swipe-to-dismiss, copying to the clipboard, and the Escape / focus-trap / scroll-lock behaviour behind a custom overlay.
|
|
4
4
|
|
|
5
5
|
**Keywords:** hotkey, keybinding, keyboard shortcut, cmd+k, command palette, breakpoint, responsive, touch device, on-screen keyboard, focus trap, scroll lock, swipe
|
|
6
6
|
|
|
7
|
-
**Import:** `import { useHotkeys, formatHotkey, useMediaQuery, useCoarsePointer, useVisualViewportSize, useScrollEdges, useSwipeDismiss, useOverlay, tabbablesIn } from '@ahrowe/ui'`
|
|
8
|
-
**Types:** `import type { HotkeyMap, HotkeyHandler, UseHotkeysOptions, VisualViewportSize, ScrollEdges, ScrollEdgesOptions, UseSwipeDismissOptions, UseSwipeDismissResult, UseOverlayArgs } from '@ahrowe/ui'`
|
|
7
|
+
**Import:** `import { useHotkeys, formatHotkey, useMediaQuery, useCoarsePointer, useVisualViewportSize, useScrollEdges, useSwipeDismiss, useOverlay, useClipboard, tabbablesIn } from '@ahrowe/ui'`
|
|
8
|
+
**Types:** `import type { HotkeyMap, HotkeyHandler, UseHotkeysOptions, VisualViewportSize, ScrollEdges, ScrollEdgesOptions, UseSwipeDismissOptions, UseSwipeDismissResult, UseOverlayArgs, UseClipboardOptions, UseClipboardResult } from '@ahrowe/ui'`
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -211,6 +211,42 @@ function CustomOverlay({ isOpen, onClose, children }) {
|
|
|
211
211
|
|
|
212
212
|
Reach for [Modal.md](Modal.md) or [Drawer.md](Drawer.md) first; this is for an overlay they do not cover.
|
|
213
213
|
|
|
214
|
+
## useClipboard
|
|
215
|
+
|
|
216
|
+
Writes text to the clipboard and reports how it went. For the usual icon-only copy control, use
|
|
217
|
+
[CopyButton.md](CopyButton.md), which is this hook plus the feedback and the screen-reader
|
|
218
|
+
announcement. Reach for the hook when the trigger is something else, such as a labelled `Button`:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
const { copy, copied } = useClipboard();
|
|
222
|
+
|
|
223
|
+
<Button icon={copied ? faCheck : faCopy} onClick={() => void copy(shareUrl)}>
|
|
224
|
+
{copied ? 'Copied' : 'Copy link'}
|
|
225
|
+
</Button>;
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`copy(text)` resolves `true` on success and `false` on failure, so it never needs a `try`:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
const ok = await copy(invoice.number);
|
|
232
|
+
showToast(ok ? 'Copied' : 'Could not copy', { type: ok ? 'success' : 'error' });
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
| Option | Type | Default | Description |
|
|
236
|
+
| ------ | ---- | ------- | ----------- |
|
|
237
|
+
| `timeout` | `number` | `2000` | How long `copied` stays `true` after a copy, in ms |
|
|
238
|
+
|
|
239
|
+
| Returns | Type | Description |
|
|
240
|
+
| ------- | ---- | ----------- |
|
|
241
|
+
| `copy` | `(text: string) => Promise<boolean>` | Writes `text`; a second copy restarts the timeout |
|
|
242
|
+
| `copied` | `boolean` | `true` for `timeout` ms after a successful copy |
|
|
243
|
+
| `error` | `Error \| null` | Why the last copy failed, until the next attempt |
|
|
244
|
+
| `reset` | `() => void` | Clears `copied` and `error` early |
|
|
245
|
+
|
|
246
|
+
It uses `navigator.clipboard.writeText` only. That API needs a secure context (`https://` or
|
|
247
|
+
`localhost`) and may be blocked inside an iframe; there it fails through `error` rather than falling
|
|
248
|
+
back to the deprecated `document.execCommand('copy')`.
|
|
249
|
+
|
|
214
250
|
## tabbablesIn
|
|
215
251
|
|
|
216
252
|
The tabbable descendants of an element, in DOM order. An element taken out of the tab order with
|
package/docs/InputDropdown.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**Keywords:** autocomplete, typeahead
|
|
6
6
|
|
|
7
7
|
**Import:** `import { InputDropdown } from '@ahrowe/ui'`
|
|
8
|
-
**Types:** `import type { InputDropdownItem, InputDropdownProps } from '@ahrowe/ui'`
|
|
8
|
+
**Types:** `import type { InputDropdownItem, InputDropdownLabels, InputDropdownProps } from '@ahrowe/ui'`
|
|
9
9
|
|
|
10
10
|
```tsx
|
|
11
11
|
import { InputDropdown } from '@ahrowe/ui';
|
|
@@ -63,6 +63,7 @@ interface InputDropdownItem {
|
|
|
63
63
|
| `onChange` | `(value: unknown) => void` | Called on every text change |
|
|
64
64
|
| `placeholder` | `string` | |
|
|
65
65
|
| `readOnly` | `boolean` | Make the field read-only — typing is blocked, the list won't open, and keyboard navigation/selection is disabled. Default `false` |
|
|
66
|
+
| `labels` | `InputDropdownLabels` | Text the component renders of its own, see **Labels** below |
|
|
66
67
|
| `formValidator` | `FormValidator` | Connects to validation — shows the error state/message once the field has been touched (blurred), and the required mark (`*`) when the validator has a `required` validator |
|
|
67
68
|
| `error` | `string` | Manual error message (used when no `formValidator` is given) |
|
|
68
69
|
| `isRequired` | `boolean` | Shows the required mark (`*`) next to the label. Also inferred automatically when `formValidator` has a `required` validator. Default `false` |
|
|
@@ -78,8 +79,12 @@ Inside a `<form>`, `Enter` on a highlighted suggestion picks it without submitti
|
|
|
78
79
|
with nothing highlighted submits as any text field would. So cycling to your own text and pressing
|
|
79
80
|
`Enter` twice is the way to submit exactly what you typed. `Home` and `End` are left to the text field, so they move the caret.
|
|
80
81
|
|
|
81
|
-
**
|
|
82
|
+
**Nothing to show:** the list only opens when it has something in it. With `allowCustomValue` a search that matches nothing closes it, since the typed text is kept anyway, and it reopens as soon as something matches again. Without `allowCustomValue` the typed text is thrown away on blur, so a `No matches` row says so while the user is still typing, and a screen reader announces it. Nothing typed yet opens nothing in either mode, including a preset value that `items` does not contain.
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
**Labels:** `empty` (shown when nothing matches and `allowCustomValue` is off, default `No matches`): the text this component renders of its own. Pass `labels` to override any of them, on the component or app-wide through `ConfigProvider`; they merge per key. Type: `InputDropdownLabels`.
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
**Slots:** `root` `dropdown` `item` `empty`
|
|
87
|
+
|
|
88
|
+
Long lists are handled by capping how many matches are rendered (`maxItems`, default `100`) rather than by virtualizing: only about six rows fit in the scroll box, so anything past the cap is DOM the user never sees. Raise `maxItems` (or set it to `0`) if a list is genuinely meant to be scrolled end to end. For lists too large to pass as `items` at all, fetch on the server, keep the results in your own state and feed them in as `items`. Without `allowCustomValue`, say so while a fetch runs, or the field reports `No matches` for results that have not arrived yet: `labels={{ empty: isFetching ? 'Loading…' : 'No matches' }}`.
|
|
89
|
+
|
|
90
|
+
The list is rendered through a portal and tracks the trigger across scroll, so it always escapes clipping ancestors (cards, scroll containers, virtualized lists). The panel itself is a [Popover](Popover.md), so it flips and clamps the same way every other anchored surface in the library does. It is never a bottom sheet, whatever `presentation` a `ConfigProvider` sets: the field being typed into is the trigger, and a sheet would cover it with its backdrop and open the list away from it, below an on-screen keyboard. It flips to open upward automatically when there's no room below, and shifts horizontally to stay within the viewport when the trigger sits near the left or right edge of the screen. It hides (without closing) if the trigger itself scrolls behind a clipping ancestor, reappearing once it's back in view. It also re-measures when its own content changes size while open, so a list that shrinks (e.g. filtered down to fewer entries) stays anchored to the trigger instead of hanging above it.
|
package/docs/Menu.md
CHANGED
|
@@ -29,11 +29,13 @@ const items: MenuEntry[] = [
|
|
|
29
29
|
**Entries** are one of three shapes, and the list is read in the order you give it:
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
|
-
{ id: 'edit', label: 'Edit', icon, shortcut, disabled, danger, onClick } // an action
|
|
33
|
-
{ separator: true }
|
|
34
|
-
{ heading: 'This document' }
|
|
32
|
+
{ id: 'edit', label: 'Edit', icon, shortcut, keywords, disabled, danger, onClick } // an action
|
|
33
|
+
{ separator: true } // a line
|
|
34
|
+
{ heading: 'This document' } // a label over what follows
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
`keywords` are extra search terms for [CommandPalette](CommandPalette.md), which takes the same entries. The menu ignores them.
|
|
38
|
+
|
|
37
39
|
**Shortcuts are drawn, not bound.** A `shortcut` is a combo in [useHotkeys](Hooks.md) spelling, and the menu writes it on the trailing edge for the platform the reader is on: `mod+s` shows as `⌘S` on an Apple one and `Ctrl+S` everywhere else. So there is one spelling per shortcut and a hand-written `'⌘S'` can no longer be wrong on Windows.
|
|
38
40
|
|
|
39
41
|
Listening for it is yours, because only your app knows what else is listening: if it already binds `⌘S` and the menu bound it too, both would fire and the action would run twice. `menuHotkeys` turns the same entries into a map for `useHotkeys`, so the one spelling covers the hint and the binding:
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# WeatherIcon
|
|
2
|
+
|
|
3
|
+
**When to use:** An animated weather symbol for a forecast, a dashboard widget or a day selector, one per condition a forecast reports. For a static pictogram of anything else, use a FontAwesome icon.
|
|
4
|
+
|
|
5
|
+
**Keywords:** forecast, rain, snow, sun, cloud, storm, climate, meteo
|
|
6
|
+
|
|
7
|
+
**Import:** `import { WeatherIcon, WeatherType } from '@ahrowe/ui'`
|
|
8
|
+
**Types:** `import type { WeatherIconProps, WeatherIconLabels } from '@ahrowe/ui'`
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { WeatherIcon, WeatherType } from '@ahrowe/ui';
|
|
12
|
+
|
|
13
|
+
<WeatherIcon type={WeatherType.Rainy} />
|
|
14
|
+
|
|
15
|
+
// Any CSS size; a number is pixels
|
|
16
|
+
<WeatherIcon type={WeatherType.Snowy} size='3em' />
|
|
17
|
+
|
|
18
|
+
// A still frame, for small or repeated icons such as a row of days
|
|
19
|
+
<WeatherIcon type={WeatherType.PartlyCloudy} size={32} animated={false} />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Enums:** `WeatherType`: `Sunny` · `ClearNight` · `Cloudy` · `PartlyCloudy` · `PartlyCloudyNight` · `RainyLight` · `Rainy` · `RainyHeavy` · `Snowy` · `Hail` · `Foggy` · `Thunderstorm`
|
|
23
|
+
|
|
24
|
+
**Your API's own enum works as it is.** `type` takes the enum or its string value (`'rainy'`, `'partlyCloudy'`), so a backend enum with the same values passes without a mapping, even though TypeScript treats two enums as different types.
|
|
25
|
+
|
|
26
|
+
**Colour** is the text colour it sits in (`currentColor`), like any icon. Set `color` on it or on a parent:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
<WeatherIcon type={WeatherType.Sunny} style={{ color: 'var(--warn-color)' }} />
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Motion.** Clouds drift, rain and snow fall, hail bounces, lightning flashes twice every four seconds. Under `prefers-reduced-motion` it draws the same still frame as `animated={false}`: each drop, flake and stone frozen at a different point of its fall rather than all bunched under the cloud. `animated={false}` is also worth setting on a long row of small icons, where the motion is too small to see but costs the same. Set it app-wide through `ConfigProvider`.
|
|
33
|
+
|
|
34
|
+
**Accessibility.** The svg is `role="img"` and named after the weather ("Heavy rain"). Where the text next to it already says the same, pass `aria-hidden`.
|
|
35
|
+
|
|
36
|
+
**Key props:**
|
|
37
|
+
|
|
38
|
+
| Prop | Type | Description |
|
|
39
|
+
|------|------|-------------|
|
|
40
|
+
| `type` | `WeatherType \| string value` | The weather to draw (required) |
|
|
41
|
+
| `size` | `number \| string` | Width and height. A number is pixels (default `64`) |
|
|
42
|
+
| `animated` | `boolean` | `false` draws a still frame (default `true`). Always still under reduced motion |
|
|
43
|
+
| `labels` | `WeatherIconLabels` | The accessible name of each weather |
|
|
44
|
+
|
|
45
|
+
The native svg attributes (`className`, `style`, `aria-*`, `data-*`, handlers) reach the `<svg>`.
|
|
46
|
+
|
|
47
|
+
**Global defaults:** adopts `ConfigProvider`, e.g. `defaultProps={{ WeatherIcon: { animated: false } }}`. See [ConfigProvider.md](ConfigProvider.md).
|
|
48
|
+
|
|
49
|
+
**Labels:** one key per `WeatherType` value, `sunny` to `thunderstorm`: the accessible name of each weather. Pass `labels` to override any of them, on the component or app-wide through `ConfigProvider`; they merge per key. Type: `WeatherIconLabels`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahrowe/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "A React UI component library with theming, CSS Modules, and TypeScript support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -128,7 +128,6 @@
|
|
|
128
128
|
"publint": "^0.3.24",
|
|
129
129
|
"react": "^19.3.0",
|
|
130
130
|
"react-dom": "^19.3.0",
|
|
131
|
-
"react-element-to-jsx-string": "^17.0.1",
|
|
132
131
|
"react-markdown": "^10.1.0",
|
|
133
132
|
"react-syntax-highlighter": "^16.1.1",
|
|
134
133
|
"remark-gfm": "^4.0.1",
|