@ahrowe/ui 0.51.0 → 0.52.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/CHANGELOG.md +13 -0
- package/README.md +1 -0
- package/dist/cjs-types/common/actionIcon/actionIcon.types.d.ts +10 -6
- package/dist/cjs-types/common/ahroweProvider/ahroweProvider.d.ts +1 -1
- package/dist/cjs-types/common/button/button.types.d.ts +25 -8
- package/dist/cjs-types/common/configProvider/configProvider.d.ts +2 -1
- package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +8 -0
- package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +4 -1
- package/dist/cjs-types/common/listItem/index.d.ts +2 -0
- package/dist/cjs-types/common/listItem/listItem.d.ts +4 -0
- package/dist/cjs-types/common/listItem/listItem.types.d.ts +33 -0
- package/dist/cjs-types/common/menu/menu.types.d.ts +2 -2
- package/dist/cjs-types/common/menu/menuItem.d.ts +5 -2
- package/dist/cjs-types/common/themeProvider/theme.types.d.ts +4 -0
- package/dist/cjs-types/common/utils/navigation.d.ts +12 -0
- package/dist/cjs-types/index.d.ts +2 -0
- package/dist/esm/common/ahroweProvider/ahroweProvider.mjs +1 -1
- package/dist/esm/common/ahroweProvider/ahroweProvider.mjs.map +1 -1
- package/dist/esm/common/breadcrumb/breadcrumb.mjs +1 -1
- package/dist/esm/common/breadcrumb/breadcrumb.mjs.map +1 -1
- package/dist/esm/common/button/button.mjs +1 -1
- package/dist/esm/common/button/button.mjs.map +1 -1
- package/dist/esm/common/button/button.module.mjs +1 -1
- package/dist/esm/common/button/button.module.mjs.map +1 -1
- package/dist/esm/common/button/button.types.mjs.map +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs +1 -1
- package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -1
- package/dist/esm/common/configProvider/configProvider.mjs +1 -1
- package/dist/esm/common/configProvider/configProvider.mjs.map +1 -1
- package/dist/esm/common/listItem/listItem.mjs +2 -0
- package/dist/esm/common/listItem/listItem.mjs.map +1 -0
- package/dist/esm/common/listItem/listItem.module.mjs +2 -0
- package/dist/esm/common/listItem/listItem.module.mjs.map +1 -0
- 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/menu/menuItem.mjs +1 -1
- package/dist/esm/common/menu/menuItem.mjs.map +1 -1
- package/dist/esm/common/utils/navigation.mjs +2 -0
- package/dist/esm/common/utils/navigation.mjs.map +1 -0
- package/dist/esm/index.mjs +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/common/actionIcon/actionIcon.types.d.ts +10 -6
- package/dist/types/common/ahroweProvider/ahroweProvider.d.ts +1 -1
- package/dist/types/common/button/button.types.d.ts +25 -8
- package/dist/types/common/configProvider/configProvider.d.ts +2 -1
- package/dist/types/common/configProvider/configProvider.types.d.ts +8 -0
- package/dist/types/common/copyButton/copyButton.types.d.ts +4 -1
- package/dist/types/common/listItem/index.d.ts +2 -0
- package/dist/types/common/listItem/listItem.d.ts +4 -0
- package/dist/types/common/listItem/listItem.types.d.ts +33 -0
- package/dist/types/common/menu/menu.types.d.ts +2 -2
- package/dist/types/common/menu/menuItem.d.ts +5 -2
- package/dist/types/common/themeProvider/theme.types.d.ts +4 -0
- package/dist/types/common/utils/navigation.d.ts +12 -0
- package/dist/types/index.d.ts +2 -0
- package/docs/ActionIcon.md +7 -3
- package/docs/AhroweProvider.md +2 -1
- package/docs/Breadcrumb.md +4 -24
- package/docs/Button.md +44 -9
- package/docs/CLAUDE.md +4 -0
- package/docs/CommandPalette.md +1 -1
- package/docs/ConfigProvider.md +34 -1
- package/docs/ListItem.md +97 -0
- package/docs/Menu.md +2 -2
- package/docs/ScrollbarProvider.md +1 -1
- package/package.json +1 -1
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { IconContent } from '../types/icon.types.js';
|
|
2
2
|
import { SlotClassNames, SlotStyles } from '../types/slots.types.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ButtonElementProps, ButtonLinkProps } from '../button/button.types.js';
|
|
4
4
|
export type ActionIconSlots = 'root' | 'icon';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* that describe a label or a size.
|
|
8
|
-
*/
|
|
9
|
-
export interface ActionIconProps extends Omit<ButtonProps, 'icon' | 'size' | 'children' | 'isLoading' | 'shortcut' | 'classNames' | 'styles'> {
|
|
5
|
+
type LabelAndSizeProps = 'icon' | 'size' | 'children' | 'isLoading' | 'shortcut' | 'classNames' | 'styles';
|
|
6
|
+
interface ActionIconOwnProps {
|
|
10
7
|
icon: IconContent;
|
|
11
8
|
classNames?: SlotClassNames<ActionIconSlots>;
|
|
12
9
|
styles?: SlotStyles<ActionIconSlots>;
|
|
13
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* A Button with only an icon, sized by that icon, so every Button prop applies except the ones
|
|
13
|
+
* that describe a label or a size. With `href` it is a link, as a Button is. Two branches rather
|
|
14
|
+
* than one `Omit<ButtonProps>`, which would merge them and lose `target` and the typed `onClick`.
|
|
15
|
+
*/
|
|
16
|
+
export type ActionIconProps = (Omit<ButtonElementProps, LabelAndSizeProps> & ActionIconOwnProps) | (Omit<ButtonLinkProps, LabelAndSizeProps> & ActionIconOwnProps);
|
|
17
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
2
|
import { AhroweProviderProps } from './ahroweProvider.types.js';
|
|
3
|
-
declare function AhroweProvider({ children, themes, currentThemeId, defaultProps, presentation, toast, scrollbars, }: AhroweProviderProps): ReactElement;
|
|
3
|
+
declare function AhroweProvider({ children, themes, currentThemeId, defaultProps, presentation, navigate, toast, scrollbars, }: AhroweProviderProps): ReactElement;
|
|
4
4
|
export default AhroweProvider;
|
|
@@ -27,20 +27,14 @@ export declare enum ButtonType {
|
|
|
27
27
|
Submit = "submit"
|
|
28
28
|
}
|
|
29
29
|
export type ButtonSlots = 'background' | 'content' | 'loadingContainer' | 'label' | 'shortcut';
|
|
30
|
-
|
|
31
|
-
* `DataProps` supplies the `data-*` index signature, which `ButtonHTMLAttributes` doesn't cover.
|
|
32
|
-
* `type`, `color` and `onClick` below deliberately narrow their inherited counterparts.
|
|
33
|
-
*/
|
|
34
|
-
export interface ButtonProps extends DataProps, React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
30
|
+
interface ButtonOwnProps extends DataProps {
|
|
35
31
|
className?: string;
|
|
36
32
|
style?: CSSProperties;
|
|
37
|
-
type?: ButtonType;
|
|
38
33
|
/** Emphasis: `Filled`, `Light`, `Outlined` (default) or `Plain`. */
|
|
39
34
|
styleType?: ButtonStyleType;
|
|
40
35
|
/** A `ThemeColor` (default `Primary`) or any CSS colour, such as `'var(--brand)'`. */
|
|
41
36
|
color?: ColorValue;
|
|
42
37
|
size?: ButtonSize;
|
|
43
|
-
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;
|
|
44
38
|
children?: React.ReactNode;
|
|
45
39
|
icon?: IconContent;
|
|
46
40
|
/**
|
|
@@ -51,7 +45,30 @@ export interface ButtonProps extends DataProps, React.ButtonHTMLAttributes<HTMLB
|
|
|
51
45
|
isLoading?: boolean;
|
|
52
46
|
disabled?: boolean;
|
|
53
47
|
title?: string;
|
|
54
|
-
ref?: React.Ref<HTMLButtonElement>;
|
|
55
48
|
classNames?: SlotClassNames<ButtonSlots>;
|
|
56
49
|
styles?: SlotStyles<ButtonSlots>;
|
|
57
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* A Button without `href`, a `<button>`. `DataProps` supplies the `data-*` index signature, which
|
|
53
|
+
* `ButtonHTMLAttributes` doesn't cover. `type` and `onClick` narrow their native counterparts.
|
|
54
|
+
*/
|
|
55
|
+
export interface ButtonElementProps extends ButtonOwnProps, Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof ButtonOwnProps | 'type' | 'onClick'> {
|
|
56
|
+
href?: undefined;
|
|
57
|
+
type?: ButtonType;
|
|
58
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;
|
|
59
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
60
|
+
}
|
|
61
|
+
/** A Button with `href`, an `<a>`: `target`, `rel` and `download` are the native ones. */
|
|
62
|
+
export interface ButtonLinkProps extends ButtonOwnProps, Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof ButtonOwnProps | 'type' | 'onClick' | 'href'> {
|
|
63
|
+
/**
|
|
64
|
+
* Renders the button as a link, so a long press, Ctrl/⌘-click and a middle click work as on any
|
|
65
|
+
* other: `tel:`, `mailto:`, a maps URL, a page of the app. A plain click on a page of the app goes
|
|
66
|
+
* through `ConfigProvider`'s `navigate` when it is set.
|
|
67
|
+
*/
|
|
68
|
+
href: string;
|
|
69
|
+
type?: undefined;
|
|
70
|
+
onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => Promise<void> | void;
|
|
71
|
+
ref?: React.Ref<HTMLAnchorElement>;
|
|
72
|
+
}
|
|
73
|
+
export type ButtonProps = ButtonElementProps | ButtonLinkProps;
|
|
74
|
+
export {};
|
|
@@ -9,5 +9,6 @@ export declare const ConfigContext: React.Context<ComponentDefaults | null>;
|
|
|
9
9
|
* consumer who missed one would get sheets in half their app.
|
|
10
10
|
*/
|
|
11
11
|
export declare const PresentationContext: React.Context<Presentation | undefined>;
|
|
12
|
-
declare
|
|
12
|
+
export declare const NavigateContext: React.Context<((path: string) => void) | undefined>;
|
|
13
|
+
declare function ConfigProvider({ children, defaultProps, presentation, navigate }: ConfigProviderProps): React.JSX.Element;
|
|
13
14
|
export default ConfigProvider;
|
|
@@ -151,4 +151,12 @@ export interface ConfigProviderProps {
|
|
|
151
151
|
* component-keyed entry in `defaultProps` overrides it, and an explicit prop overrides both.
|
|
152
152
|
*/
|
|
153
153
|
presentation?: Presentation;
|
|
154
|
+
/**
|
|
155
|
+
* The app's router, for the links components render: Button and ListItem with an `href`, and
|
|
156
|
+
* Breadcrumb, Menu and CommandPalette entries. A plain click on a link within the app calls it
|
|
157
|
+
* with the path (`/clients/7?tab=projects`) instead of loading the page. Ctrl/⌘-click, a middle
|
|
158
|
+
* click, `target`, `download` and links to anywhere else stay with the browser. Unset, every
|
|
159
|
+
* link loads its page. A nested provider that does not set it keeps the outer one's.
|
|
160
|
+
*/
|
|
161
|
+
navigate?: (path: string) => void;
|
|
154
162
|
}
|
|
@@ -11,7 +11,10 @@ export interface CopyButtonLabels {
|
|
|
11
11
|
/** Announced when the copy fails. Default: "Could not copy". */
|
|
12
12
|
failed?: string;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
/** The `<button>` branch of ActionIcon: a copy button is never a link. */
|
|
15
|
+
export interface CopyButtonProps extends Omit<Extract<ActionIconProps, {
|
|
16
|
+
href?: undefined;
|
|
17
|
+
}>, 'href' | 'icon' | 'onCopy' | 'onError' | 'onClick' | 'ref' | 'classNames' | 'styles'> {
|
|
15
18
|
/** Text written to the clipboard on click. */
|
|
16
19
|
value: string;
|
|
17
20
|
/** How long the copied state shows, in ms. Default: 2000. */
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { ListItemProps } from './listItem.types.js';
|
|
3
|
+
declare function ListItem({ icon, title, subtitle, endContent, href, target, rel, download, onClick, selected, chevron, className, style, classNames, styles: slotStyles, ...rest }: ListItemProps): ReactElement;
|
|
4
|
+
export default ListItem;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AnchorHTMLAttributes, CSSProperties, MouseEventHandler, ReactNode } from 'react';
|
|
2
|
+
import { IconContent } from '../types/icon.types.js';
|
|
3
|
+
import { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types.js';
|
|
4
|
+
export type ListItemSlots = 'root' | 'icon' | 'labels' | 'trigger' | 'title' | 'subtitle' | 'endContent' | 'chevron';
|
|
5
|
+
export interface ListItemProps extends Omit<HtmlProps<HTMLDivElement>, 'title' | 'onClick'>, Pick<AnchorHTMLAttributes<HTMLAnchorElement>, 'target' | 'rel' | 'download'> {
|
|
6
|
+
/** Before the title: a FontAwesome icon or any element, such as an `Avatar`. */
|
|
7
|
+
icon?: IconContent;
|
|
8
|
+
/** The main line. Cut off with an ellipsis where it does not fit. */
|
|
9
|
+
title: ReactNode;
|
|
10
|
+
/** A smaller line under the title, cut off after two lines. */
|
|
11
|
+
subtitle?: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* The trailing edge: a status `Chip`, a date, an amount, and controls of its own such as an
|
|
14
|
+
* edit button or a `Switch`, which keep working in a row that opens something.
|
|
15
|
+
*/
|
|
16
|
+
endContent?: ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Opens something from the whole row, as a link: Ctrl/⌘-click and a middle click open a new tab.
|
|
19
|
+
* A plain click on a page of the app goes through `ConfigProvider`'s `navigate` when it is set.
|
|
20
|
+
* `target`, `rel` and `download` apply to it.
|
|
21
|
+
*/
|
|
22
|
+
href?: string;
|
|
23
|
+
/** Opens something from the whole row, as a button. With `href` as well, runs before the link is followed. */
|
|
24
|
+
onClick?: MouseEventHandler<HTMLElement>;
|
|
25
|
+
/** The row whose record is open: tinted, and the current page to a screen reader. */
|
|
26
|
+
selected?: boolean;
|
|
27
|
+
/** The chevron at the end. Shown for a row with an `href`, which takes the user somewhere. */
|
|
28
|
+
chevron?: boolean;
|
|
29
|
+
className?: string;
|
|
30
|
+
style?: CSSProperties;
|
|
31
|
+
classNames?: SlotClassNames<ListItemSlots>;
|
|
32
|
+
styles?: SlotStyles<ListItemSlots>;
|
|
33
|
+
}
|
|
@@ -26,8 +26,8 @@ export interface MenuAction {
|
|
|
26
26
|
/** Renders in the destructive colour. For deleting, discarding, revoking. */
|
|
27
27
|
danger?: boolean;
|
|
28
28
|
/**
|
|
29
|
-
* Followed after `onClick` and `onAction
|
|
30
|
-
*
|
|
29
|
+
* Followed after `onClick` and `onAction`, through `ConfigProvider`'s `navigate` when it is set.
|
|
30
|
+
* In `Menu` a real `<a>`, so Ctrl/⌘-click opens a new tab.
|
|
31
31
|
*/
|
|
32
32
|
href?: string;
|
|
33
33
|
onClick?: () => void;
|
|
@@ -6,5 +6,8 @@ export declare function renderItemIcon(icon: MenuAction['icon'], reserveCell: bo
|
|
|
6
6
|
className: string;
|
|
7
7
|
style?: CSSProperties;
|
|
8
8
|
}): ReactNode;
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Runs an action the way clicking it does: its own `onClick`, then `onAction`, then its `href`.
|
|
11
|
+
* `follow` is `useFollowHref()` where the caller can reach the router; `menuHotkeys` cannot.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runMenuAction(action: MenuAction, onAction?: (id: string) => void, follow?: (href: string) => void): void;
|
|
@@ -217,6 +217,10 @@ export interface ThemeVariables {
|
|
|
217
217
|
'--multi-select-min-height'?: string;
|
|
218
218
|
/** Maximum height of the open option panel. Falls back to 260px. */
|
|
219
219
|
'--multi-select-panel-height'?: string;
|
|
220
|
+
/** Minimum height of a row. Falls back to 48px, the smallest comfortable touch target. */
|
|
221
|
+
'--list-item-min-height'?: string;
|
|
222
|
+
/** Background of a `selected` row. Falls back to a 12% tint of --primary-color. */
|
|
223
|
+
'--list-item-selected-background'?: string;
|
|
220
224
|
/** Width a column collapses to once it has no cards (see `collapseEmptyColumns`). Falls back to 140px. */
|
|
221
225
|
'--kanban-empty-column-width'?: string;
|
|
222
226
|
'--option-picker-padding'?: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { MouseEvent } from 'react';
|
|
2
|
+
type Navigate = (path: string) => void;
|
|
3
|
+
/**
|
|
4
|
+
* The click handler for an `<a>` a component renders: a plain click on a link within the app goes
|
|
5
|
+
* to `ConfigProvider`'s `navigate`, everything else to the browser. Call it after the consumer's own
|
|
6
|
+
* `onClick`, so a `preventDefault()` there still wins.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useLinkClick(): (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
9
|
+
/** Follows a link from code, where no anchor was clicked: through the router where it can. */
|
|
10
|
+
export declare function followHref(href: string, navigate?: Navigate): void;
|
|
11
|
+
export declare function useFollowHref(): (href: string) => void;
|
|
12
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -82,6 +82,8 @@ export { default as KanbanBoard } from './common/kanbanBoard/index.js';
|
|
|
82
82
|
export * from './common/kanbanBoard/index.js';
|
|
83
83
|
export { default as KlipyPicker } from './common/klipyPicker/index.js';
|
|
84
84
|
export * from './common/klipyPicker/index.js';
|
|
85
|
+
export { default as ListItem } from './common/listItem/index.js';
|
|
86
|
+
export * from './common/listItem/index.js';
|
|
85
87
|
export { default as Loader } from './common/loader/index.js';
|
|
86
88
|
export * from './common/loader/index.js';
|
|
87
89
|
export { default as LoadingOverlay } from './common/loadingOverlay/index.js';
|
package/docs/ActionIcon.md
CHANGED
|
@@ -9,18 +9,21 @@
|
|
|
9
9
|
**Requires:** `@fortawesome/free-solid-svg-icons` peer dependency.
|
|
10
10
|
|
|
11
11
|
An ActionIcon is a [Button](Button.md) holding only an icon and sized by it: a real `<button>`
|
|
12
|
-
with Button's hover, focus ring and ripple. It takes Button's `styleType` and `color`, plain and
|
|
12
|
+
with Button's hover, focus ring and ripple, or with an `href` a link, as a Button is. It takes Button's `styleType` and `color`, plain and
|
|
13
13
|
in the surrounding text colour by default, so it fits whatever it sits on.
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
16
|
import { ActionIcon, ButtonStyleType, ThemeColor } from '@ahrowe/ui';
|
|
17
|
-
import { faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
17
|
+
import { faPhone, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
18
18
|
|
|
19
19
|
<ActionIcon icon={faXmark} aria-label="Close" onClick={onClose} />
|
|
20
20
|
|
|
21
21
|
// A destructive row action
|
|
22
22
|
<ActionIcon icon={faTrash} color={ThemeColor.Error} title="Delete item" onClick={handleDelete} />
|
|
23
23
|
|
|
24
|
+
// A link: a long press copies the number
|
|
25
|
+
<ActionIcon icon={faPhone} href="tel:+431234567" aria-label="Call" />
|
|
26
|
+
|
|
24
27
|
// Filled, for an action that should stand out
|
|
25
28
|
<ActionIcon icon={faTrash} styleType={ButtonStyleType.Filled} color={ThemeColor.Error} aria-label="Delete" />
|
|
26
29
|
```
|
|
@@ -35,7 +38,8 @@ import { faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
|
35
38
|
| `title` | `string` | Tooltip. Give an icon-only button an `aria-label` as well |
|
|
36
39
|
| `onClick` | `(event: MouseEvent<HTMLButtonElement>) => void \| Promise<void>` | Click handler |
|
|
37
40
|
| `disabled` | `boolean` | Fades it and blocks clicks |
|
|
38
|
-
| `
|
|
41
|
+
| `href` | `string` | Renders it as an `<a>`, with `target`, `rel` and `download`. See [Button.md](Button.md) |
|
|
42
|
+
| `ref` | `Ref<HTMLButtonElement>` | Ref to the button, or `Ref<HTMLAnchorElement>` with an `href` |
|
|
39
43
|
|
|
40
44
|
Every other Button prop and native `<button>` attribute passes through, except `size`,
|
|
41
45
|
`children`, `isLoading` and `shortcut`. The box is the icon plus `4px` on each side; set `width`
|
package/docs/AhroweProvider.md
CHANGED
|
@@ -40,6 +40,7 @@ Every prop is optional. `<AhroweProvider><App /></AhroweProvider>` applies `defa
|
|
|
40
40
|
| `currentThemeId` | `string` | the first theme without a `baseThemeId` | The `id` of the theme to apply |
|
|
41
41
|
| `defaultProps` | `ComponentDefaults` | | Default props per component. See [ConfigProvider.md](ConfigProvider.md) |
|
|
42
42
|
| `presentation` | `Presentation` | | How every sheet-capable component presents. See [ConfigProvider.md](ConfigProvider.md) |
|
|
43
|
+
| `navigate` | `(path: string) => void` | | The app's router, for the links components render. Usually set on a `ConfigProvider` inside the router instead, see [ConfigProvider.md](ConfigProvider.md) |
|
|
43
44
|
| `toast` | `ToastContextConfig` | | Settings for every toast `showToast` shows. See [Toast.md](Toast.md) |
|
|
44
45
|
| `scrollbars` | `boolean` | `true` | Styles every scrollbar inside the app in the theme's colours. See **Scrollbars** |
|
|
45
46
|
|
|
@@ -51,7 +52,7 @@ On the server, and while hydrating, nothing is portalled: the server has nowhere
|
|
|
51
52
|
|
|
52
53
|
## Nesting
|
|
53
54
|
|
|
54
|
-
A nested `AhroweProvider` applies its `themes`, `currentThemeId`, `defaultProps` and `
|
|
55
|
+
A nested `AhroweProvider` applies its `themes`, `currentThemeId`, `defaultProps`, `presentation` and `navigate` to its section. Its `defaultProps` merge with the outer ones per component, the same as nested `ConfigProvider`s. Toasts, scrollbars and the portal element stay with the outermost provider, so a nested `toast` or `scrollbars` is ignored, with a warning.
|
|
55
56
|
|
|
56
57
|
Only a provider inside another `AhroweProvider` counts as nested. Put the outer one at the root first: one placed around a section of an app that has none renders the portal element inside that section, where an `overflow: hidden` or a `transform` around it clips every panel.
|
|
57
58
|
|
package/docs/Breadcrumb.md
CHANGED
|
@@ -30,28 +30,8 @@ import { faHouse, faFolder } from '@fortawesome/free-solid-svg-icons';
|
|
|
30
30
|
]}
|
|
31
31
|
/>
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
import { useNavigate } from 'react-router-dom';
|
|
36
|
-
|
|
37
|
-
function Example() {
|
|
38
|
-
const navigate = useNavigate();
|
|
39
|
-
return (
|
|
40
|
-
<Breadcrumb
|
|
41
|
-
items={[
|
|
42
|
-
{
|
|
43
|
-
label: 'Home',
|
|
44
|
-
href: '/',
|
|
45
|
-
onClick: (event) => {
|
|
46
|
-
event.preventDefault();
|
|
47
|
-
navigate('/');
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
{ label: 'Current page' },
|
|
51
|
-
]}
|
|
52
|
-
/>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
33
|
+
// With a client-side router, set `navigate` once on ConfigProvider (ConfigProvider.md):
|
|
34
|
+
// a plain click on a crumb then goes through the router, a middle click still opens a new tab.
|
|
55
35
|
|
|
56
36
|
// A disabled crumb: dimmed, not clickable, regardless of href/onClick
|
|
57
37
|
<Breadcrumb
|
|
@@ -86,9 +66,9 @@ function Example() {
|
|
|
86
66
|
|-------|------|-------------|
|
|
87
67
|
| `id` | `string` | Optional stable key; falls back to the item's index |
|
|
88
68
|
| `label` | `ReactNode` | The crumb's text |
|
|
89
|
-
| `href` | `string` | Renders the crumb as a real `<a
|
|
69
|
+
| `href` | `string` | Renders the crumb as a real `<a>`, followed through `ConfigProvider`'s `navigate` when it is set. Omit to render a plain clickable element (with `onClick`) or static text |
|
|
90
70
|
| `icon` | `IconDefinition \| ReactElement` | Optional leading icon — FontAwesome icon or any React element |
|
|
91
|
-
| `onClick` | `(event: MouseEvent) => void` | Click handler.
|
|
71
|
+
| `onClick` | `(event: MouseEvent) => void` | Click handler. Alongside `href` it runs first, and `event.preventDefault()` stops the link from being followed. Also works on its own, with no `href` |
|
|
92
72
|
| `disabled` | `boolean` | Dims the crumb and makes it non-interactive, regardless of `href`/`onClick` |
|
|
93
73
|
|
|
94
74
|
**Key props:**
|
package/docs/Button.md
CHANGED
|
@@ -12,7 +12,7 @@ works with any colour. The colours are the library-wide `ThemeColor`s or any CSS
|
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
14
|
import { Button, ButtonStyleType, ButtonSize, ButtonType, ThemeColor, useHotkeys } from '@ahrowe/ui';
|
|
15
|
-
import { faPlus, faSave, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
15
|
+
import { faLocationArrow, faPhone, faPlus, faSave, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
16
16
|
|
|
17
17
|
// The main action
|
|
18
18
|
<Button styleType={ButtonStyleType.Filled} onClick={handleSave}>Save</Button>
|
|
@@ -67,6 +67,10 @@ useHotkeys({ 'mod+s': handleSave });
|
|
|
67
67
|
|
|
68
68
|
// Submit button in a form
|
|
69
69
|
<Button type={ButtonType.Submit} styleType={ButtonStyleType.Filled}>Submit</Button>
|
|
70
|
+
|
|
71
|
+
// A link: a long press copies the number, a middle click opens a new tab
|
|
72
|
+
<Button href="tel:+431234567" icon={faPhone}>Call</Button>
|
|
73
|
+
<Button href={mapsUrl} target="_blank" icon={faLocationArrow}>Directions</Button>
|
|
70
74
|
```
|
|
71
75
|
|
|
72
76
|
**Enums:**
|
|
@@ -83,19 +87,37 @@ useHotkeys({ 'mod+s': handleSave });
|
|
|
83
87
|
| `styleType` | `ButtonStyleType` | `Filled`: solid fill, for the one main action. `Light`: a tint of the colour behind text in the colour. `Outlined` (default): border and text in the colour. `Plain`: text in the colour, no fill or border, for toolbar, icon and close buttons |
|
|
84
88
|
| `color` | `ColorValue` | A `ThemeColor` (default `Primary`) or any CSS colour. `Neutral` takes the surrounding text colour, with a `--border-color` outline; `Error` is for destructive actions. See [ThemeColor.md](ThemeColor.md) |
|
|
85
89
|
| `size` | `ButtonSize` | Size variant. `Icon` strips all padding, inherits the surrounding text size, and centres a lone icon — set the width/height yourself |
|
|
86
|
-
| `type` | `ButtonType` | HTML button type |
|
|
90
|
+
| `type` | `ButtonType` | HTML button type. Not on a link |
|
|
91
|
+
| `href` | `string` | Renders an `<a>` instead of a `<button>`, see **As a link** |
|
|
87
92
|
| `icon` | `IconDefinition \| ReactElement` | FontAwesome icon or any React element, shown before label |
|
|
88
93
|
| `shortcut` | `string` | A combo in [useHotkeys](Hooks.md) spelling, e.g. `'mod+s'`. Shown after the label, written for the platform. Binding it is yours, see below |
|
|
89
|
-
| `isLoading` | `boolean` | Replaces content with a [Loader](Loader.md) in the button's text colour. Blocks clicks like `disabled
|
|
94
|
+
| `isLoading` | `boolean` | Replaces content with a [Loader](Loader.md) in the button's text colour. Blocks clicks and a form submit like `disabled`, but keeps full colour and the keyboard focus. |
|
|
90
95
|
| `disabled` | `boolean` | Fades the button to half opacity |
|
|
91
96
|
| `onClick` | `(e: MouseEvent) => void \| Promise<void>` | |
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
Without an `href`, every native `<button>` attribute is accepted and forwarded — pointer handlers
|
|
99
|
+
(`onPointerDown` and friends, for press-and-hold controls that act on press rather than release),
|
|
100
|
+
`onContextMenu`, `tabIndex`, `name`, `value`, `form`, `autoFocus`, and the rest. `type` and
|
|
101
|
+
`onClick` are narrowed: `type` takes the `ButtonType` enum, and `onClick` may return a promise.
|
|
102
|
+
`color` is Button's own prop, not the obsolete HTML attribute. With an `href`, the native `<a>`
|
|
103
|
+
attributes take their place, see below.
|
|
104
|
+
|
|
105
|
+
## As a link
|
|
106
|
+
|
|
107
|
+
With an `href` a Button is an `<a>` that looks like a button, so it behaves like a link: a long
|
|
108
|
+
press offers to copy the address, and Ctrl/⌘-click and a middle click open a new tab. Use it for
|
|
109
|
+
anything that goes somewhere: `tel:`, `mailto:`, a maps URL, a page of your app. A button whose
|
|
110
|
+
`onClick` sets `window.location` gets none of that.
|
|
111
|
+
|
|
112
|
+
- **Types.** `ButtonProps` is two shapes, told apart by `href`. The link takes the native `<a>`
|
|
113
|
+
attributes, `target`, `rel` and `download` among them, types `onClick` and `ref` for the `<a>`,
|
|
114
|
+
and has no `type`. An `href` that may be `undefined` renders a `<button>` while it is, and an
|
|
115
|
+
`onClick` beside it needs its event typed as `MouseEvent<HTMLElement>`.
|
|
116
|
+
- **Pages of your app** load the whole page unless `ConfigProvider` has a `navigate`, which then
|
|
117
|
+
takes a plain click, see [ConfigProvider.md](ConfigProvider.md).
|
|
118
|
+
- **`disabled`** drops the `href`: an `<a>` cannot be disabled, so it leaves the tab order and is
|
|
119
|
+
announced as a dimmed link. **`isLoading`** keeps it, and with it the focus, but is not followed.
|
|
120
|
+
- **`target="_blank"`** needs no `rel="noopener"`: every browser the library supports implies it.
|
|
99
121
|
|
|
100
122
|
**Shortcuts are drawn, not bound.** `shortcut` writes the combo in a pill after the label,
|
|
101
123
|
`⌘S` on an Apple platform and `Ctrl+S` elsewhere. It also sets `aria-keyshortcuts`, so a screen
|
|
@@ -139,6 +161,19 @@ A Button is sized by its font. `--default-font-size` is the base, `Medium` is th
|
|
|
139
161
|
|
|
140
162
|
`ButtonSize.Icon` is the exception: it inherits the surrounding text size and has no padding at all, because the caller sets the box.
|
|
141
163
|
|
|
164
|
+
## Migration from the `ButtonProps` interface
|
|
165
|
+
|
|
166
|
+
Before 0.51, `ButtonProps` and `ActionIconProps` were interfaces for a `<button>`. Each is now a
|
|
167
|
+
union of a button and a link shape, told apart by `href`. Code that renders a Button compiles
|
|
168
|
+
unchanged; a type built on one may not, because an interface cannot extend a union and `Omit` on
|
|
169
|
+
one keeps only what both shapes share:
|
|
170
|
+
|
|
171
|
+
| Before | After |
|
|
172
|
+
|--------|-------|
|
|
173
|
+
| `interface SaveButtonProps extends ButtonProps { … }` | `type SaveButtonProps = ButtonProps & { … }`, which keeps both shapes, so the wrapper takes an `href` too |
|
|
174
|
+
| `interface MyIconProps extends ActionIconProps { … }` | `type MyIconProps = ActionIconProps & { … }` |
|
|
175
|
+
| `Omit<ButtonProps, 'styleType'>` | `Omit<ButtonElementProps, 'styleType'>` for a wrapper that is only ever a `<button>` |
|
|
176
|
+
|
|
142
177
|
## Migration from `ButtonSize.Default`
|
|
143
178
|
|
|
144
179
|
Before 0.45, `ButtonSize.Default` was a second name for `Medium`, which is now the default. Replace
|
package/docs/CLAUDE.md
CHANGED
|
@@ -40,6 +40,9 @@ enums and slot names. Do not infer props from a component's name.
|
|
|
40
40
|
emphasis, the same way everywhere: `Filled` for the one main action of a region (at most one),
|
|
41
41
|
`Light` for status and selection, `Outlined` for secondary actions, `Plain` for tertiary ones.
|
|
42
42
|
- **`icon` props** take a FontAwesome `IconDefinition` or any React element.
|
|
43
|
+
- **Links.** An `href` (Button, ActionIcon, ListItem, Breadcrumb, Menu, CommandPalette) renders a
|
|
44
|
+
real `<a>`. With a client-side router, set `navigate` on a `ConfigProvider` inside the router
|
|
45
|
+
once, or every plain click loads the page (ConfigProvider.md).
|
|
43
46
|
- **`ref`** is a plain prop, there is no `forwardRef`. It lands on the element you would reach for:
|
|
44
47
|
Input, Textarea, NumberInput and SearchInput hand out their inner `<input>`, ChatComposer its
|
|
45
48
|
`<textarea>`. Timer, Flip, Tilt, Ripple, VirtualList, ChatMessageList, RoomDrawer and RoomViewer
|
|
@@ -106,6 +109,7 @@ enums and slot names. Do not infer props from a component's name.
|
|
|
106
109
|
| InteractableDiv.md | InteractableDiv | Accessible `<div>` that behaves like a button — click and keyboard (Enter/Space) events, with proper ARIA semantics |
|
|
107
110
|
| KanbanBoard.md | KanbanBoard, KanbanColumn | Drag-and-drop board with columns — task management, workflow stages, status boards (e.g. Todo / In Progress / Done) |
|
|
108
111
|
| KlipyPicker.md | KlipyPicker | GIF picker backed by the Klipy API — chat reactions, message composers, content authoring tools |
|
|
112
|
+
| ListItem.md | ListItem | One row of a list that stands for a record: a customer, an invoice, a delivery note |
|
|
109
113
|
| Loader.md | Loader, LoaderType, ThemeColor | A loading indicator for something in progress with no known end: a fetch, a save, a connection |
|
|
110
114
|
| LoadingOverlay.md | LoadingOverlay, Loader, LoaderType | Covering content while it reloads: a table refreshing, a form saving, a panel fetching its next page |
|
|
111
115
|
| Menu.md | Menu, menuHotkeys | A list of actions hanging off a trigger — a row's "more" button, a toolbar overflow, a context menu on a card |
|
package/docs/CommandPalette.md
CHANGED
|
@@ -73,7 +73,7 @@ On a small touch screen it can present as a bottom sheet like every `Modal`: pas
|
|
|
73
73
|
| `isLoading` | `boolean` | Shows the `loading` label instead of `empty` while nothing matches |
|
|
74
74
|
| `presentation` | `Presentation` | Centred dialog (default), bottom sheet, or a sheet only on a small touch screen |
|
|
75
75
|
|
|
76
|
-
A command is a `MenuAction`: `id`, `label`, `icon`, `shortcut`, `keywords`, `disabled`, `danger`, `href` and `onClick`. A command with an `href` goes there after running. See [Menu.md](Menu.md).
|
|
76
|
+
A command is a `MenuAction`: `id`, `label`, `icon`, `shortcut`, `keywords`, `disabled`, `danger`, `href` and `onClick`. A command with an `href` goes there after running, through `ConfigProvider`'s `navigate` when it is set. See [Menu.md](Menu.md).
|
|
77
77
|
|
|
78
78
|
**Global defaults:** adopts `ConfigProvider`, e.g. `defaultProps={{ CommandPalette: { presentation: Presentation.Auto } }}`. See [ConfigProvider.md](ConfigProvider.md).
|
|
79
79
|
|
package/docs/ConfigProvider.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
**Import:** `import { ConfigProvider } from '@ahrowe/ui'`
|
|
8
8
|
**Types:** `import type { ComponentDefaults, ConfigProviderProps } from '@ahrowe/ui'`
|
|
9
9
|
|
|
10
|
-
At the app root, pass `defaultProps` and `
|
|
10
|
+
At the app root, pass `defaultProps`, `presentation` and `navigate` to [AhroweProvider](AhroweProvider.md), which includes a ConfigProvider. Use ConfigProvider on its own for a section with defaults of its own:
|
|
11
11
|
|
|
12
12
|
```tsx
|
|
13
13
|
import { AhroweProvider, ConfigProvider } from '@ahrowe/ui';
|
|
@@ -58,6 +58,7 @@ So a global default only fills in props a given instance left out — any instan
|
|
|
58
58
|
|------|------|-------------|
|
|
59
59
|
| `defaultProps` | `ComponentDefaults` | Default props keyed by component name, e.g. `{ Input: { alwaysFloatLabel: true } }` |
|
|
60
60
|
| `presentation` | `Presentation` | How every sheet-capable component presents by default — one switch for all of them (see below) |
|
|
61
|
+
| `navigate` | `(path: string) => void` | The app's router, for every link a component renders (see below) |
|
|
61
62
|
| `children` | `ReactNode` | Your app content |
|
|
62
63
|
|
|
63
64
|
## App-wide `presentation`
|
|
@@ -90,6 +91,38 @@ It resolves *under* the component-keyed defaults, so the full order for a `prese
|
|
|
90
91
|
|
|
91
92
|
A nested provider inherits it unless it sets its own, the same way `defaultProps` compose. Everything built on those four components follows along: `SplitButton`, `ColorPicker`, `Breadcrumb`, `VirtualList`, and `DatePicker`/`TimeInput`/`KlipyPicker` in either their menu or their `isModal` mode. `InputDropdown` and `Tooltip` are deliberately never sheets and ignore it.
|
|
92
93
|
|
|
94
|
+
## Client-side routing (`navigate`)
|
|
95
|
+
|
|
96
|
+
Button, ActionIcon and ListItem with an `href`, and Breadcrumb, Menu and CommandPalette entries with one, are real links: a long press, Ctrl/⌘-click and a middle click work on them as on any `<a>`. Without `navigate` a plain click loads the page. With it, a plain click on a page of the app calls `navigate` with the path instead:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { Outlet, useNavigate } from 'react-router-dom';
|
|
100
|
+
import { ConfigProvider } from '@ahrowe/ui';
|
|
101
|
+
|
|
102
|
+
// The root route's element. useNavigate needs the router, and AhroweProvider is usually outside it.
|
|
103
|
+
function Root() {
|
|
104
|
+
const navigate = useNavigate();
|
|
105
|
+
return (
|
|
106
|
+
<ConfigProvider navigate={navigate}>
|
|
107
|
+
<Outlet />
|
|
108
|
+
</ConfigProvider>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
It is called with the path, search and hash (`/clients/7?tab=projects`) for a plain left click on an `http:` or `https:` link to the page's own origin. The browser keeps everything else: a click with Ctrl, ⌘, Shift or Alt, a middle click, a link with a `target` or `download`, another site, `mailto:`, `tel:`, a `blob:` URL, and a jump to an anchor on the current page. An `onClick` of your own runs first, and a `preventDefault()` in it keeps `navigate` out.
|
|
114
|
+
|
|
115
|
+
- **Write absolute paths.** An `href` is resolved against the page's URL, like any link, because that is what a middle click does; `navigate` gets the resolved path. A router resolves a relative path against the route instead, so `href="projects/1"` on `/clients/7` is `/clients/projects/1`, not where `navigate('projects/1')` would go.
|
|
116
|
+
- **`navigate` decides.** It gets every path of the page's own origin, also one the router does not own: a server route such as `/logout`, a file under `/api`, a page outside a `basename`. Hand those back to the browser with `window.location.assign(path)`. A `basename` is part of the `href` but not of the path the router's `navigate` takes, so strip it:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
navigate={(path) => {
|
|
120
|
+
if (path === BASENAME || path.startsWith(`${BASENAME}/`)) navigate(path.slice(BASENAME.length) || '/');
|
|
121
|
+
else window.location.assign(path);
|
|
122
|
+
}}
|
|
123
|
+
```
|
|
124
|
+
- **Two places it cannot reach.** Toasts render in `AhroweProvider`'s own host, outside a `ConfigProvider` placed inside the router. `menuHotkeys` runs outside React, so an entry with an `href` run from its shortcut loads the page.
|
|
125
|
+
|
|
93
126
|
## Supported components
|
|
94
127
|
|
|
95
128
|
Each entry is a `Partial<...Props>`, so any of that component's props can be defaulted:
|
package/docs/ListItem.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# ListItem
|
|
2
|
+
|
|
3
|
+
**When to use:** One row of a list that stands for a record: a customer, an invoice, a delivery note. It shows a title, a second line and a status, can hold actions of its own, and can open the record from the whole row. For hundreds of rows use [VirtualList](VirtualList.md); for actions hanging off a trigger, [Menu](Menu.md).
|
|
4
|
+
|
|
5
|
+
**Keywords:** row, list row, cell, entry, record, item, drill-down, detail link, settings row
|
|
6
|
+
|
|
7
|
+
**Import:** `import { ListItem } from '@ahrowe/ui'`
|
|
8
|
+
**Types:** `import type { ListItemProps } from '@ahrowe/ui'`
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { ActionIcon, Chip, ListItem, ThemeColor } from '@ahrowe/ui';
|
|
12
|
+
import { faFileInvoice, faPen } from '@fortawesome/free-solid-svg-icons';
|
|
13
|
+
|
|
14
|
+
// Shows a record
|
|
15
|
+
<ListItem title="Created" endContent="12 Oct 2026" />
|
|
16
|
+
|
|
17
|
+
// Opens it: the whole row is the link, with a chevron
|
|
18
|
+
<ListItem
|
|
19
|
+
icon={faFileInvoice}
|
|
20
|
+
title="Invoice 2026-0042"
|
|
21
|
+
subtitle="Müller GmbH, due 1 Nov"
|
|
22
|
+
endContent={<Chip text="Open" color={ThemeColor.Warn} />}
|
|
23
|
+
href="/invoices/42"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
// Opens it and has an action: the edit button stays its own
|
|
27
|
+
<ListItem
|
|
28
|
+
title="Müller GmbH"
|
|
29
|
+
subtitle="3 projects"
|
|
30
|
+
href="/clients/7"
|
|
31
|
+
endContent={<ActionIcon icon={faPen} aria-label="Edit" onClick={editClient} />}
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
// Opens it from a button instead of a link: no chevron, which promises a page
|
|
35
|
+
<ListItem title="Add a contact" onClick={openContactModal} />
|
|
36
|
+
|
|
37
|
+
// The one that is open, beside its detail view
|
|
38
|
+
<ListItem title="Müller GmbH" href="/clients/7" selected={clientId === 7} />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Opening a record
|
|
42
|
+
|
|
43
|
+
With `href` the title is a link, and with `onClick` a button. Either one stretches over the whole
|
|
44
|
+
row, so a click anywhere on it opens the record, and with a link a middle click and a long press
|
|
45
|
+
too. Controls in the row stay their own: a button, a link, an input or anything with a `tabindex`
|
|
46
|
+
sits above the stretched area. Text around them still opens the row, so a click on a status `Chip`
|
|
47
|
+
opens the record.
|
|
48
|
+
|
|
49
|
+
A plain click on a page of the app goes through `ConfigProvider`'s `navigate` when it is set, see
|
|
50
|
+
[ConfigProvider.md](ConfigProvider.md). A screen reader hears the title as the link's name and the
|
|
51
|
+
subtitle, and a text `endContent`, as its description, rather than the whole row run together.
|
|
52
|
+
|
|
53
|
+
Because the link covers the row, its text cannot be selected with the mouse. A row that is only
|
|
54
|
+
read, with neither `href` nor `onClick`, selects as usual.
|
|
55
|
+
|
|
56
|
+
The rows sit in whatever holds them, a `Card` or a `<ul>` of your own. ListItem draws no dividers
|
|
57
|
+
and no list semantics of its own: it is a `<div>`, so in a `<ul>` give each one an `<li>`.
|
|
58
|
+
|
|
59
|
+
**Dividers.** Between groups of rows, a [Divider](Divider.md): it has no margin, so it adds the line
|
|
60
|
+
and nothing else. Between every row, a border from the container instead, because a screen reader
|
|
61
|
+
announces each `Divider` as a separator. The same rule works on `<div>`s and on `<li>`s:
|
|
62
|
+
|
|
63
|
+
```css
|
|
64
|
+
.clients > * + * {
|
|
65
|
+
border-top: 1px solid var(--border-color);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Key props:**
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Description |
|
|
72
|
+
|------|------|-------------|
|
|
73
|
+
| `title` | `ReactNode` | The main line, cut off with an ellipsis where it does not fit (required) |
|
|
74
|
+
| `subtitle` | `ReactNode` | A smaller line under it, cut off after two lines |
|
|
75
|
+
| `icon` | `IconDefinition \| ReactElement` | Before the title: a FontAwesome icon or any element, such as an `Avatar` |
|
|
76
|
+
| `endContent` | `ReactNode` | The trailing edge: a status, a date, an amount, and controls of its own |
|
|
77
|
+
| `href` | `string` | Opens the record from the whole row, as a link. `target`, `rel` and `download` apply to it |
|
|
78
|
+
| `onClick` | `MouseEventHandler<HTMLElement>` | Opens the record from the whole row, as a button. With `href` too, runs first, and `preventDefault()` keeps the link from being followed |
|
|
79
|
+
| `selected` | `boolean` | The row whose record is open: tinted in `--list-item-selected-background`, and `aria-current="page"` on its link |
|
|
80
|
+
| `chevron` | `boolean` | The chevron at the end. On by default for a row with an `href`, off for one with only `onClick`, which opens a dialog or runs an action rather than going somewhere |
|
|
81
|
+
|
|
82
|
+
Every native attribute passes through to the root `<div>`. A long title shrinks before
|
|
83
|
+
`endContent` and the chevron do, so they never leave the row. `endContent` itself never shrinks:
|
|
84
|
+
a value longer than a date or an amount belongs in the `subtitle`.
|
|
85
|
+
|
|
86
|
+
**Theming:**
|
|
87
|
+
|
|
88
|
+
| Variable | Default |
|
|
89
|
+
|----------|---------|
|
|
90
|
+
| `--list-item-min-height` | `48px`, the smallest comfortable touch target |
|
|
91
|
+
| `--list-item-selected-background` | a 12% tint of `--primary-color` |
|
|
92
|
+
|
|
93
|
+
Set it once in the theme rather than on each row: `'--list-item-min-height': '64px'`. Hover and
|
|
94
|
+
press tint the row in its own text colour, so they show on any surface; the keyboard focus ring
|
|
95
|
+
goes around the whole row, inside its edge, in `--focus-border-color`.
|
|
96
|
+
|
|
97
|
+
**Slots:** `root` `icon` `labels` `trigger` (the link or button, which draws the hover and focus ring in its `::after`) `title` `subtitle` `endContent` `chevron`
|
package/docs/Menu.md
CHANGED
|
@@ -54,7 +54,7 @@ useHotkeys(menuHotkeys(items, run));
|
|
|
54
54
|
useHotkeys(menuHotkeys(items, run), { enabled: isOpen });
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
It runs each entry the way clicking it does, its own `onClick` then `onAction` then its `href`, and leaves out a `disabled` entry and any entry with no `shortcut`. Bind them yourself instead if the actions already live in a keymap of your own; the menu does not mind either way.
|
|
57
|
+
It runs each entry the way clicking it does, its own `onClick` then `onAction` then its `href`, and leaves out a `disabled` entry and any entry with no `shortcut`. It runs outside React, so it cannot reach `ConfigProvider`'s `navigate`: an `href` run from a shortcut loads the page. Bind them yourself instead if the actions already live in a keymap of your own; the menu does not mind either way.
|
|
58
58
|
|
|
59
59
|
An action runs its own `onClick` first, then `onAction` on the menu, then the menu closes. Use whichever of the two fits: a handler per entry when they are unrelated, `onAction` when they funnel into one place.
|
|
60
60
|
|
|
@@ -120,7 +120,7 @@ Separators and headings are never focused. The trigger is given `aria-haspopup`,
|
|
|
120
120
|
| `shortcut` | `string` | A combo in `useHotkeys` spelling, e.g. `'mod+k'`. Shown on the trailing edge, written for the platform. Binding it is yours, see `menuHotkeys` |
|
|
121
121
|
| `disabled` | `boolean` | Not clickable, and skipped by the arrow keys |
|
|
122
122
|
| `danger` | `boolean` | Renders in the destructive colour |
|
|
123
|
-
| `href` | `string` | Makes the entry a real `<a>`, so Ctrl/⌘-click and a middle click open it in a new tab. A click runs `onClick` and `onAction` first, a middle click neither
|
|
123
|
+
| `href` | `string` | Makes the entry a real `<a>`, so Ctrl/⌘-click and a middle click open it in a new tab. A click runs `onClick` and `onAction` first, a middle click neither, then follows the link through `ConfigProvider`'s `navigate` when it is set, see [ConfigProvider.md](ConfigProvider.md). A `disabled` entry stays a button |
|
|
124
124
|
| `onClick` | `() => void` | Runs before `onAction` |
|
|
125
125
|
|
|
126
126
|
**Slots:** `root` `trigger` `menu` `item` `itemIcon` `itemLabel` `itemShortcut` `separator` `heading`
|