@allxsmith/bestax-bulma 5.29.0 → 5.31.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.
Files changed (67) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/esm/components/ContextMenu.js +244 -0
  4. package/dist/esm/components/ContextMenu.js.map +1 -0
  5. package/dist/esm/components/Dropdown.js +37 -85
  6. package/dist/esm/components/Dropdown.js.map +1 -1
  7. package/dist/esm/components/HoverCard.js +3 -10
  8. package/dist/esm/components/HoverCard.js.map +1 -1
  9. package/dist/esm/components/Tooltip.js +88 -63
  10. package/dist/esm/components/Tooltip.js.map +1 -1
  11. package/dist/esm/elements/Button.js.map +1 -1
  12. package/dist/esm/elements/Buttons.js +2 -0
  13. package/dist/esm/elements/Buttons.js.map +1 -1
  14. package/dist/esm/elements/ToggleButton.js +68 -0
  15. package/dist/esm/elements/ToggleButton.js.map +1 -0
  16. package/dist/esm/form/DateTimeInputBase.js.map +1 -1
  17. package/dist/esm/form/TimeInputBase.js.map +1 -1
  18. package/dist/esm/form/_pickerInternals/pickerTypes.js +14 -6
  19. package/dist/esm/form/_pickerInternals/pickerTypes.js.map +1 -1
  20. package/dist/esm/helpers/useContextMenuTrigger.js +166 -0
  21. package/dist/esm/helpers/useContextMenuTrigger.js.map +1 -0
  22. package/dist/esm/helpers/useHoverOpen.js +40 -6
  23. package/dist/esm/helpers/useHoverOpen.js.map +1 -1
  24. package/dist/esm/helpers/useMenuKeys.js +182 -0
  25. package/dist/esm/helpers/useMenuKeys.js.map +1 -0
  26. package/dist/extras.css +1 -1
  27. package/dist/extras.css.map +1 -1
  28. package/dist/index.cjs +1896 -1261
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.esm.js +2 -0
  31. package/dist/index.esm.js.map +1 -1
  32. package/dist/types/components/ContextMenu.d.ts +66 -0
  33. package/dist/types/components/Tooltip.d.ts +63 -9
  34. package/dist/types/elements/Button.d.ts +1 -1
  35. package/dist/types/elements/Buttons.d.ts +1 -0
  36. package/dist/types/elements/ToggleButton.d.ts +100 -0
  37. package/dist/types/form/DateTimeInputBase.d.ts +4 -1
  38. package/dist/types/form/TimeInputBase.d.ts +5 -2
  39. package/dist/types/form/_pickerInternals/pickerTypes.d.ts +16 -1
  40. package/dist/types/helpers/useContextMenuTrigger.d.ts +67 -0
  41. package/dist/types/helpers/useHoverOpen.d.ts +19 -3
  42. package/dist/types/helpers/useMenuKeys.d.ts +57 -0
  43. package/dist/types/index.d.ts +2 -0
  44. package/dist/types-cjs/components/ContextMenu.d.ts +66 -0
  45. package/dist/types-cjs/components/Tooltip.d.ts +63 -9
  46. package/dist/types-cjs/elements/Button.d.ts +1 -1
  47. package/dist/types-cjs/elements/Buttons.d.ts +1 -0
  48. package/dist/types-cjs/elements/ToggleButton.d.ts +100 -0
  49. package/dist/types-cjs/form/DateTimeInputBase.d.ts +4 -1
  50. package/dist/types-cjs/form/TimeInputBase.d.ts +5 -2
  51. package/dist/types-cjs/form/_pickerInternals/pickerTypes.d.ts +16 -1
  52. package/dist/types-cjs/helpers/useContextMenuTrigger.d.ts +67 -0
  53. package/dist/types-cjs/helpers/useHoverOpen.d.ts +19 -3
  54. package/dist/types-cjs/helpers/useMenuKeys.d.ts +57 -0
  55. package/dist/types-cjs/index.d.ts +2 -0
  56. package/dist/versions/bestax-no-dark-mode.css +1 -1
  57. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  58. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  59. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  60. package/dist/versions/bestax-no-helpers.css +1 -1
  61. package/dist/versions/bestax-no-helpers.css.map +1 -1
  62. package/dist/versions/bestax-prefixed.css +1 -1
  63. package/dist/versions/bestax-prefixed.css.map +1 -1
  64. package/package.json +1 -1
  65. package/src/scss/components/_contextmenu.scss +50 -0
  66. package/src/scss/components/_index.scss +1 -0
  67. package/src/scss/components/_tooltip.scss +76 -1
package/dist/index.esm.js CHANGED
@@ -20,6 +20,7 @@ export { Collapses } from './esm/components/Collapses.js';
20
20
  export { Tooltip } from './esm/components/Tooltip.js';
21
21
  export { Popover, PopoverBody, PopoverClose, PopoverFooter, PopoverHeader } from './esm/components/Popover.js';
22
22
  export { HoverCard } from './esm/components/HoverCard.js';
23
+ export { ContextMenu } from './esm/components/ContextMenu.js';
23
24
  export { Step, Steps } from './esm/components/Steps.js';
24
25
  export { Sidebar } from './esm/components/Sidebar.js';
25
26
  export { Toast, ToastContainer, toast } from './esm/components/Toast.js';
@@ -31,6 +32,7 @@ export { Box } from './esm/elements/Box.js';
31
32
  export { Button } from './esm/elements/Button.js';
32
33
  export { Buttons } from './esm/elements/Buttons.js';
33
34
  export { LinkButton } from './esm/elements/LinkButton.js';
35
+ export { ToggleButton } from './esm/elements/ToggleButton.js';
34
36
  export { Code } from './esm/elements/Code.js';
35
37
  export { Content } from './esm/elements/Content.js';
36
38
  export { Delete } from './esm/elements/Delete.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { BulmaClassesProps } from '../helpers/useBulmaClasses.js';
3
+ /**
4
+ * Props for the ContextMenu component. `className`, `id`, `aria-label`, the
5
+ * other HTML attributes and the helper props land on the menu
6
+ * (`role="menu"`). The trigger keeps its own.
7
+ * @extraProp {string} [aria-label] - The menu's accessible name, such as "Actions for report.pdf". There's no button to borrow a name from, so pass this or `aria-labelledby`, and a development build warns when the menu opens with neither.
8
+ * @extraProp {string} [className] - Additional classes for the menu (`role="menu"`). The trigger keeps its own.
9
+ */
10
+ export interface ContextMenuProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'children'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
11
+ /**
12
+ * The element a right-click, Shift+F10, the context menu key or a long
13
+ * press opens the menu from: a table row, a card, an image. It's cloned,
14
+ * not wrapped, so a row stays a row, which means it has to be a single
15
+ * element that passes `className` and the `onContextMenu`,
16
+ * `onPointerDown`, `onPointerMove`, `onPointerUp`, `onPointerCancel` and
17
+ * `onTouchEnd` handlers to its DOM node. Its own handlers for those run
18
+ * first, and one that calls `preventDefault()` on `contextmenu` keeps the
19
+ * menu closed. A `ContextMenu` inside it opens instead of this one.
20
+ *
21
+ * It gets no ARIA and no tab stop, since it can be any element and a
22
+ * right-click isn't an activation. Keyboard users reach it through
23
+ * something focusable inside it, such as the row's own `Dropdown` button,
24
+ * or a `tabIndex={0}` you give it, and press Shift+F10 or the context menu
25
+ * key there. It gets `-webkit-touch-callout: none`, so iOS doesn't put its
26
+ * own callout for a link or an image over the menu. A long press still
27
+ * selects text the way it does anywhere, so give a region where it
28
+ * shouldn't `interaction="unselectable"`.
29
+ */
30
+ trigger: React.ReactElement;
31
+ /**
32
+ * The menu's items: `ContextMenu.Item` and `ContextMenu.Divider`, the same
33
+ * components as `Dropdown.Item` and `Dropdown.Divider`. Every action in it
34
+ * needs a visible way in as well, such as a `Dropdown` on the same row:
35
+ * nothing tells a screen reader user the menu is there, and on macOS only
36
+ * VoiceOver opens it from the keyboard.
37
+ */
38
+ children: React.ReactNode;
39
+ /**
40
+ * Called with `true` when the menu opens and `false` when it closes,
41
+ * whatever closed it, such as for highlighting the row whose menu is open.
42
+ * Nothing fires after ContextMenu unmounts. There's no `open` prop to go
43
+ * with it: a menu opened from code has no point to open at.
44
+ */
45
+ onOpenChange?: (open: boolean) => void;
46
+ /**
47
+ * Close the menu when an item is clicked, or activated with Enter or
48
+ * Space. A click on a divider or between items leaves it open either way.
49
+ */
50
+ closeOnClick?: boolean;
51
+ /**
52
+ * Leave the trigger to the browser: a right-click shows the browser's own
53
+ * menu, a long press does what it does anywhere, and an open menu closes.
54
+ */
55
+ disabled?: boolean;
56
+ }
57
+ /**
58
+ * ContextMenu with Item and Divider sub-components, which are `Dropdown.Item`
59
+ * and `Dropdown.Divider`, so items written for a Dropdown work here
60
+ * unchanged.
61
+ */
62
+ export declare const ContextMenu: React.FC<ContextMenuProps> & {
63
+ Item: import("../index.js").ConstrainedPolymorphicComponentWithoutRef<import("./Dropdown.js").DropdownItemOwnProps, import("./Dropdown.js").DropdownItemElement, "a", never>;
64
+ Divider: React.FC<{}>;
65
+ };
66
+ export default ContextMenu;
@@ -8,13 +8,29 @@ export type TooltipColor = 'primary' | 'link' | 'info' | 'success' | 'warning' |
8
8
  export type TooltipSize = 'small' | 'medium' | 'large';
9
9
  /**
10
10
  * Props for the Tooltip component.
11
- * @extraProp {React.ReactNode} [children] - The element that triggers the tooltip.
11
+ * @extraProp {React.ReactNode} [children] - The element that triggers the
12
+ * tooltip. Its `aria-describedby` points at the tooltip, after any ids it
13
+ * already had, so a screen reader reads the tooltip as its description when
14
+ * it takes focus, even while the tooltip is hidden. That takes a single
15
+ * element which passes `aria-describedby` on to its DOM node, as native
16
+ * elements and bestax components do, and takes focus itself, such as a
17
+ * button, a link or a `Span` with `tabIndex={0}`: something focusable inside
18
+ * it doesn't get the link, and text, a fragment or several elements get none.
19
+ * A development build warns when the first element Tab reaches in the
20
+ * trigger doesn't carry the link, or there isn't one.
12
21
  * @extraProp {string} [className] - Additional CSS classes.
13
22
  */
14
23
  export interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'color' | 'content'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
15
24
  /** The tooltip text content. */
16
25
  label?: string;
17
- /** Rich tooltip content (alternative to `label`). */
26
+ /**
27
+ * Rich tooltip content (alternative to `label`), to read rather than use.
28
+ * Nothing in it should take focus or a click: it isn't in the Tab order
29
+ * while hidden, focus there isn't counted as being in the tooltip, and
30
+ * Escape hiding it would drop that focus. For links or buttons, use
31
+ * `HoverCard` or `Popover`. A screen reader reads it as plain text, since
32
+ * it's the trigger's description.
33
+ */
18
34
  content?: React.ReactNode;
19
35
  /** Position of the tooltip. Default: 'top'. */
20
36
  position?: TooltipPosition;
@@ -22,7 +38,11 @@ export interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>
22
38
  color?: TooltipColor;
23
39
  /** Size of the tooltip. */
24
40
  size?: TooltipSize;
25
- /** Force tooltip to be always visible. */
41
+ /**
42
+ * Force tooltip to be always visible. Hover, focus and Escape leave it
43
+ * shown, so Escape goes on to whatever has focus, or to a `Modal` or
44
+ * `Popover` around it.
45
+ */
26
46
  active?: boolean;
27
47
  /** Allow tooltip to wrap to multiple lines. */
28
48
  multiline?: boolean;
@@ -32,16 +52,48 @@ export interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>
32
52
  square?: boolean;
33
53
  /** Show dashed underline on trigger. */
34
54
  dashed?: boolean;
35
- /** Delay before showing tooltip (ms). */
55
+ /**
56
+ * How long the pointer or keyboard focus waits on the trigger before the
57
+ * tooltip shows, in milliseconds.
58
+ */
36
59
  delay?: number;
37
- /** Delay in ms before hiding the tooltip. */
60
+ /**
61
+ * How long the tooltip stays after the pointer and focus have left the
62
+ * trigger and the tooltip, in milliseconds. The pointer can move from the
63
+ * trigger onto a shown tooltip without one, across the default gap. A
64
+ * theme that raises `--bulma-tooltip-arrow-size` or
65
+ * `--bulma-tooltip-arrow-margin` widens the gap past what the pointer can
66
+ * cross on its own, so give it a `closeDelay` long enough to cross the
67
+ * rest.
68
+ */
38
69
  closeDelay?: number;
70
+ /**
71
+ * Show the tooltip with a tap on a touch screen. Touch has no hover, so
72
+ * without this a phone or tablet never shows it. With it, a tap on the
73
+ * trigger shows it at once, and it stays until another tap on the trigger
74
+ * or the tooltip, a tap anywhere else, or Escape. The tap still does
75
+ * whatever the trigger does, so turn this on only for a trigger with no
76
+ * tap action of its own. Mouse and pen work the same either way.
77
+ * @defaultValue false
78
+ */
79
+ openOnTap?: boolean;
39
80
  /** Additional classes for the tooltip element. */
40
81
  tooltipClassName?: string;
41
82
  }
42
83
  /**
43
84
  * The `Tooltip` component displays helpful information when users hover over or focus on an element.
44
85
  *
86
+ * Hovering the trigger or giving it keyboard focus shows it. Touch doesn't,
87
+ * unless `openOnTap` is on, and neither does focus a click or a tap brings,
88
+ * so tapping the trigger just does what the trigger does. The pointer can
89
+ * move onto a shown tooltip. Escape hides it wherever focus is, without
90
+ * moving the pointer or focus, and it stays hidden until one of them leaves
91
+ * the trigger and comes back, or, with `openOnTap`, until the next tap on
92
+ * the trigger. The key always goes on too, to whatever has focus or a
93
+ * `Modal` or `Popover` around it, since a tooltip only adds to what's
94
+ * there. The tooltip is its trigger's description, so a screen reader reads
95
+ * it as the trigger takes focus, whether it's showing yet or not.
96
+ *
45
97
  * @function
46
98
  * @param {TooltipProps} props - Props for the Tooltip component.
47
99
  * @returns {JSX.Element} The rendered tooltip wrapper.
@@ -53,15 +105,17 @@ export interface TooltipProps extends Omit<React.HTMLAttributes<HTMLSpanElement>
53
105
  * </Tooltip>
54
106
  *
55
107
  * @example
56
- * // Positioned tooltip with color
57
- * <Tooltip label="Delete item" position="right" color="danger">
58
- * <Icon icon="fas fa-trash" />
108
+ * // Positioned tooltip with color, on an icon-only button with a name of its own
109
+ * <Tooltip label="Moves it to the trash" position="right" color="danger">
110
+ * <Button aria-label="Delete">
111
+ * <Icon name="fas fa-trash" />
112
+ * </Button>
59
113
  * </Tooltip>
60
114
  *
61
115
  * @example
62
116
  * // Multiline tooltip
63
117
  * <Tooltip label="This is a longer tooltip that wraps to multiple lines" multiline>
64
- * <span>?</span>
118
+ * <button>More info</button>
65
119
  * </Tooltip>
66
120
  */
67
121
  export declare const Tooltip: React.FC<TooltipProps>;
@@ -39,7 +39,7 @@ export interface ButtonOwnProps extends Omit<BulmaClassesProps, 'color' | 'backg
39
39
  isInverted?: boolean;
40
40
  /** Applies focused styling (visual only). */
41
41
  isFocused?: boolean;
42
- /** Applies active styling (visual only). */
42
+ /** Applies active styling (visual only), with no `aria-pressed`. For a button that stays pressed, use `ToggleButton`. */
43
43
  isActive?: boolean;
44
44
  /** Applies hovered styling (visual only). */
45
45
  isHovered?: boolean;
@@ -36,5 +36,6 @@ interface ButtonsProps extends React.HTMLAttributes<HTMLDivElement>, Omit<BulmaC
36
36
  export declare const Buttons: React.FC<ButtonsProps> & {
37
37
  Button: import("../index.js").PolymorphicComponent<import("./Button.js").ButtonOwnProps, "button", never>;
38
38
  LinkButton: import("../index.js").PolymorphicComponent<import("./LinkButton.js").LinkButtonOwnProps, "button", never>;
39
+ ToggleButton: React.ForwardRefExoticComponent<import("./ToggleButton.js").ToggleButtonProps & React.RefAttributes<HTMLButtonElement>>;
39
40
  };
40
41
  export {};
@@ -0,0 +1,100 @@
1
+ import React from 'react';
2
+ import { type ButtonOwnProps } from './Button.js';
3
+ /**
4
+ * The fills a pressed `ToggleButton` takes: `Button`'s colors, minus `text`
5
+ * and `ghost`, which don't fill, and `white`, `light`, `dark` and `black`.
6
+ * Bulma's color modifier fills the button and drops its border, and those four
7
+ * fill it with about the lightness a plain button already has in one theme or
8
+ * the other, so pressed would look like off without its border.
9
+ */
10
+ export type ToggleButtonColor = 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';
11
+ /**
12
+ * Props for the ToggleButton component. The helper props, the other button
13
+ * attributes and the `ref` all land on the `<button>`.
14
+ *
15
+ * Some of `Button`'s props are left out on purpose. `as`, since only a button
16
+ * can be pressed, and `type`, which is always `button`. `isActive`, which is
17
+ * `pressed` here, and `isStatic`, a toggle you couldn't press. And the props
18
+ * that change the fill (`isLight`, `isOutlined`, `isInverted`, `textColor`,
19
+ * `bgColor` and their shades), since each would color the button in both
20
+ * states or turn "on" into a faint tint. `color` takes only the fills that
21
+ * stand out from a plain button: see `ToggleButtonColor`.
22
+ *
23
+ * @extraProp {React.Ref<HTMLButtonElement>} [ref] - Ref forwarded to the `<button>`.
24
+ */
25
+ export interface ToggleButtonProps extends Omit<ButtonOwnProps, 'color' | 'isActive' | 'isStatic' | 'isLight' | 'isOutlined' | 'isInverted' | 'textColor' | 'bgColor' | 'colorShade' | 'backgroundColorShade'>, Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof ButtonOwnProps | 'type' | 'aria-pressed'> {
26
+ /**
27
+ * Whether it's pressed, when you hold the state. Setting it makes the toggle
28
+ * controlled: a click only calls `onPressedChange`, and nothing changes until
29
+ * you pass the new value. Pick this or `defaultPressed` for the button's
30
+ * lifetime.
31
+ */
32
+ pressed?: boolean;
33
+ /** Whether it starts pressed, when it holds its own state. */
34
+ defaultPressed?: boolean;
35
+ /**
36
+ * Called with the new state each time it's toggled, whether or not
37
+ * `pressed` is set. An `onClick` that calls `preventDefault()` stops the
38
+ * toggle, and this doesn't fire.
39
+ */
40
+ onPressedChange?: (pressed: boolean) => void;
41
+ /**
42
+ * The fill while it's pressed. Not pressed, it's a plain button, so the fill
43
+ * is the difference people see. `white`, `light`, `dark` and `black` aren't
44
+ * offered, since in one theme or the other each fills about as light or dark
45
+ * as a plain button already is. Of the rest, a pale one such as `warning`
46
+ * changes less than the default `link`.
47
+ */
48
+ color?: ToggleButtonColor;
49
+ /**
50
+ * The label, which stays the same in both states: a screen reader announces
51
+ * whether it's pressed, so "Mute" stays "Mute". An icon can swap, but an
52
+ * icon-only toggle needs an `aria-label` that doesn't. A label that changes
53
+ * ("Play", then "Pause") belongs on a plain `Button`.
54
+ */
55
+ children?: React.ReactNode;
56
+ /**
57
+ * Shows Bulma's spinner in place of the label and holds the toggle where it
58
+ * is: a click, Enter or Space does nothing, so neither `onClick` nor
59
+ * `onPressedChange` fires, until it's cleared. Set it while a change is
60
+ * saving. The button carries `aria-busy` and `aria-disabled` meanwhile, and
61
+ * stays focusable, so focus isn't lost mid-save. Under
62
+ * `prefers-reduced-motion: reduce` the spinner stops and stays drawn (with
63
+ * bestax's CSS loaded).
64
+ */
65
+ isLoading?: boolean;
66
+ /**
67
+ * Not accepted: `aria-pressed` follows the state. Declared because JSX lets a
68
+ * hyphenated attribute through when the props don't name it, so leaving it
69
+ * out of the type alone would not reject `aria-pressed="mixed"`.
70
+ * @internal
71
+ */
72
+ 'aria-pressed'?: never;
73
+ }
74
+ /**
75
+ * The `ToggleButton` component renders a button that stays pressed until it's pressed again, and says whether it's on through `aria-pressed`.
76
+ *
77
+ * It's always a `<button type="button">`, so it never submits a form and
78
+ * carries no form value: a setting saved with a form is a `Checkbox` or a
79
+ * `Switch`. A set of toggles that each turn on and off on their own goes in
80
+ * `Buttons hasAddons` with `role="group"` and an `aria-label`. A set where only
81
+ * one can be on is a radio group rather than a row of these: use `Radios`.
82
+ *
83
+ * @function
84
+ * @param {ToggleButtonProps} props - Props for the ToggleButton component.
85
+ * @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref to the `<button>`.
86
+ * @returns {JSX.Element} The rendered toggle button.
87
+ * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/button/ | APG button pattern}
88
+ *
89
+ * @example
90
+ * <ToggleButton onPressedChange={setMuted}>Mute</ToggleButton>
91
+ *
92
+ * @example
93
+ * <Buttons hasAddons role="group" aria-label="Text style">
94
+ * <ToggleButton aria-label="Bold" pressed={bold} onPressedChange={setBold}>
95
+ * <Icon name="bold" />
96
+ * </ToggleButton>
97
+ * </Buttons>
98
+ */
99
+ export declare const ToggleButton: React.ForwardRefExoticComponent<ToggleButtonProps & React.RefAttributes<HTMLButtonElement>>;
100
+ export default ToggleButton;
@@ -131,7 +131,10 @@ export interface DateTimeInputBaseProps extends Omit<React.InputHTMLAttributes<H
131
131
  audioTick?: boolean;
132
132
  /** Auto-route tactile feedback per wheel tick (vibrate on Android, audio thunk on iOS). Default `false`. */
133
133
  haptics?: boolean;
134
- /** Optional translatable string overrides. */
134
+ /**
135
+ * Optional translatable string overrides. In a 12-hour picker, `ampm`
136
+ * names the AM/PM column, `'AM/PM'` by default.
137
+ */
135
138
  labels?: PickerLabels;
136
139
  }
137
140
  /**
@@ -82,7 +82,7 @@ export interface TimeInputBaseProps extends Omit<React.InputHTMLAttributes<HTMLI
82
82
  size?: 'small' | 'medium' | 'large';
83
83
  /** Render the input with rounded corners. */
84
84
  isRounded?: boolean;
85
- /** Hour format. `'12'` shows an AM/PM toggle. */
85
+ /** Hour format. `'12'` adds an AM/PM column to the wheels. */
86
86
  hourFormat?: HourFormat;
87
87
  /** Show a seconds column. Note: iOS Safari's native time picker UI does not include a seconds wheel; pass `mobileNative={false}` if you need one on iOS. */
88
88
  enableSeconds?: boolean;
@@ -105,7 +105,10 @@ export interface TimeInputBaseProps extends Omit<React.InputHTMLAttributes<HTMLI
105
105
  triggerIcon?: boolean;
106
106
  /** Glyph for the right launcher button. */
107
107
  triggerIconName?: string;
108
- /** Optional translatable string overrides. */
108
+ /**
109
+ * Optional translatable string overrides. In a 12-hour picker, `ampm`
110
+ * names the AM/PM column, `'AM/PM'` by default.
111
+ */
109
112
  labels?: PickerLabels;
110
113
  /** Play a short audible click on each wheel-item crossing. Substitute for haptic feedback on iOS Safari (which has no web haptic API as of May 2026); on Android, `navigator.vibrate(5)` fires automatically regardless. */
111
114
  audioTick?: boolean;
@@ -49,13 +49,24 @@ export interface PickerLabels {
49
49
  hours?: string;
50
50
  minutes?: string;
51
51
  seconds?: string;
52
+ /**
53
+ * Time wheels: names the AM/PM column a 12-hour picker shows.
54
+ * @defaultValue 'AM/PM'
55
+ */
52
56
  ampm?: string;
57
+ /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */
53
58
  increaseHours?: string;
59
+ /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */
54
60
  decreaseHours?: string;
61
+ /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */
55
62
  increaseMinutes?: string;
63
+ /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */
56
64
  decreaseMinutes?: string;
65
+ /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */
57
66
  increaseSeconds?: string;
67
+ /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */
58
68
  decreaseSeconds?: string;
69
+ /** @deprecated Rendered nowhere: no picker reads it. It goes in the next major. */
59
70
  toggleAmPm?: string;
60
71
  chooseTime?: string;
61
72
  chooseDateTime?: string;
@@ -72,5 +83,9 @@ export interface PickerLabels {
72
83
  time?: string;
73
84
  }
74
85
  export declare const DEFAULT_PICKER_LABELS: Required<PickerLabels>;
75
- /** Merge user-supplied label overrides with the defaults. */
86
+ /**
87
+ * Merge user-supplied label overrides with the defaults. A key set to
88
+ * `undefined` keeps its default, so a locale table that is missing a string
89
+ * still names its control instead of leaving it with no accessible name.
90
+ */
76
91
  export declare const mergeLabels: (overrides?: PickerLabels) => Required<PickerLabels>;
@@ -0,0 +1,67 @@
1
+ import type { MouseEvent, PointerEvent, TouchEvent } from 'react';
2
+ /** A point in the viewport, in CSS pixels. */
3
+ export interface ContextMenuPoint {
4
+ x: number;
5
+ y: number;
6
+ }
7
+ /**
8
+ * Options for `useContextMenuTrigger`.
9
+ */
10
+ export interface UseContextMenuTriggerOptions {
11
+ /**
12
+ * Asks to open at `point`. `trigger` is the element the handlers are on,
13
+ * for its document.
14
+ */
15
+ onOpen: (point: ContextMenuPoint, trigger: HTMLElement) => void;
16
+ /** Leave the browser's own menu alone, and start no long press. */
17
+ disabled: boolean;
18
+ }
19
+ /** Handlers for the trigger element. */
20
+ export interface ContextMenuTriggerHandlers {
21
+ onContextMenu: (event: MouseEvent<HTMLElement>) => void;
22
+ onPointerDown: (event: PointerEvent<HTMLElement>) => void;
23
+ onPointerMove: (event: PointerEvent<HTMLElement>) => void;
24
+ onPointerUp: (event: PointerEvent<HTMLElement>) => void;
25
+ onPointerCancel: (event: PointerEvent<HTMLElement>) => void;
26
+ onTouchEnd: (event: TouchEvent<HTMLElement>) => void;
27
+ }
28
+ /** How long a touch or a pen has to stay down to open the menu, in milliseconds. */
29
+ export declare const LONG_PRESS_DELAY = 500;
30
+ /** How far a long press can move before it counts as something else, in CSS pixels. */
31
+ export declare const LONG_PRESS_TOLERANCE = 10;
32
+ /**
33
+ * Decides when a context menu asks to open, and where. It renders nothing,
34
+ * and knows nothing about ARIA or the menu.
35
+ *
36
+ * - **`contextmenu`:** a right-click, and Shift+F10 or the context menu key
37
+ * where the browser fires the event for them, asks to open at the point
38
+ * `contextMenuPoint` works out, and prevents the browser's own menu. Each
39
+ * one asks again, so a right-click elsewhere on the trigger moves it.
40
+ * - **Long press:** a touch or a pen held still on the trigger for
41
+ * `LONG_PRESS_DELAY` fires a `contextmenu` at what it landed on, so it
42
+ * takes the same path, handlers on the way included. Moving more than
43
+ * `LONG_PRESS_TOLERANCE`, lifting, another pointer coming down anywhere in
44
+ * the document, or `pointercancel`, which is the browser taking the
45
+ * gesture for a scroll or a zoom, cancels it, and a mouse never starts
46
+ * one. A pointer that comes down while another is held starts no long
47
+ * press of its own, on this trigger or another, so two fingers on two rows
48
+ * open nothing. While the finger is down, and only then, a listener on the
49
+ * document hears other pointers coming down. Where the
50
+ * browser fires `contextmenu` for a long press itself, the first of the two
51
+ * opens it, and the end of a touch that opened it is prevented, so lifting
52
+ * doesn't click what is under the finger.
53
+ * - **Who opens:** a handler that calls `preventDefault()` on the
54
+ * `contextmenu` before this one keeps it from asking, which is how a
55
+ * trigger's own handler opts out and how the innermost of nested triggers
56
+ * wins. A long press goes to the innermost trigger that takes it. Events
57
+ * from content portaled out of the trigger, which React brings here
58
+ * anyway, are ignored.
59
+ * - **`disabled`:** nothing asks, and the browser's menu shows.
60
+ *
61
+ * Nothing it scheduled runs after the component unmounts.
62
+ *
63
+ * @function useContextMenuTrigger
64
+ * @param options - How to ask to open, and whether to.
65
+ * @returns Handlers for the trigger element.
66
+ */
67
+ export declare function useContextMenuTrigger({ onOpen, disabled, }: UseContextMenuTriggerOptions): ContextMenuTriggerHandlers;
@@ -38,6 +38,14 @@ export interface UseHoverOpenOptions {
38
38
  * @defaultValue false
39
39
  */
40
40
  openOnTap?: boolean;
41
+ /**
42
+ * Whether an Escape that closes it stops there when focus is on the
43
+ * trigger, in the floating element or nowhere, so a `Popover` or `Modal`
44
+ * around it keeps its own. Off, Escape still closes it and always goes on
45
+ * to whatever has focus, as suits content that only adds to the trigger.
46
+ * @defaultValue true
47
+ */
48
+ consumeEscape?: boolean;
41
49
  }
42
50
  /**
43
51
  * Pointer and focus handlers for the element that holds the trigger. The
@@ -48,12 +56,18 @@ export interface UseHoverOpenOptions {
48
56
  */
49
57
  export interface HoverOpenHandlers {
50
58
  onPointerEnter: (event: PointerEvent<HTMLElement>) => void;
59
+ onPointerOver: (event: PointerEvent<HTMLElement>) => void;
51
60
  onPointerLeave: (event: PointerEvent<HTMLElement>) => void;
52
61
  onFocus: (event: FocusEvent<HTMLElement>) => void;
53
62
  onBlur: (event: FocusEvent<HTMLElement>) => void;
54
63
  onPointerDown: (event: PointerEvent<HTMLElement>) => void;
55
64
  onPointerUp: (event: PointerEvent<HTMLElement>) => void;
56
65
  }
66
+ /**
67
+ * Runs the caller's own handler on the element that holds the trigger, then
68
+ * the hook's, so neither replaces the other.
69
+ */
70
+ export declare function chain<E>(own: ((event: E) => void) | undefined, ours: (event: E) => void): (event: E) => void;
57
71
  /**
58
72
  * Decides when a floating element that opens on hover or keyboard focus asks
59
73
  * to open and to close. It renders nothing, and leaves roles, ARIA and
@@ -62,7 +76,8 @@ export interface HoverOpenHandlers {
62
76
  * - The pointer entering, or keyboard focus arriving on the trigger, asks to
63
77
  * open after `openDelay`. A touch pointer doesn't count. Neither does focus
64
78
  * that a press brings to the trigger: it counts when the browser shows it
65
- * as `:focus-visible`.
79
+ * as `:focus-visible`. The pointer arriving from outside a shadow root the
80
+ * trigger renders into counts too, though React sends no enter for it.
66
81
  * - Focus in the floating element holds it open however it got there,
67
82
  * including focus in content it portals elsewhere, such as a nested card.
68
83
  * Whether a click focuses what it lands on is up to the browser: Safari
@@ -73,8 +88,9 @@ export interface HoverOpenHandlers {
73
88
  * else closes it on a timer.
74
89
  * - Escape closes it at once, wherever focus is, and stops there when focus
75
90
  * is in it, on its trigger or nowhere, so a `Popover` or `Modal` around it
76
- * keeps its own. With focus somewhere else, the key goes on after closing
77
- * it. Inside another one, the inner one closes first. Escape before it
91
+ * keeps its own. With focus somewhere else, or with `consumeEscape` off,
92
+ * the key goes on after closing it. Inside another one, the inner one
93
+ * closes first. Escape before it
78
94
  * opens takes the opening back and goes on its way. One key listener per
79
95
  * document serves every hook on it, there only while one of them is open
80
96
  * or on its way to open.
@@ -0,0 +1,57 @@
1
+ import type { KeyboardEvent } from 'react';
2
+ /**
3
+ * Every menu-item role, so an item a caller gives `menuitemcheckbox` or
4
+ * `menuitemradio` (a filter or sort menu) stays reachable.
5
+ */
6
+ export declare const MENU_ITEM_SELECTOR = "[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]";
7
+ /**
8
+ * Whether a menu item can be reached and activated: it isn't disabled, as a
9
+ * native control or through `aria-disabled="true"`.
10
+ */
11
+ export declare function isEnabledMenuItem(item: Element): boolean;
12
+ /** The items in `menu` the keys reach, in document order. */
13
+ export declare function enabledMenuItems(menu: Element): HTMLElement[];
14
+ /** How long typed characters keep adding up to one search, in milliseconds. */
15
+ export declare const TYPEAHEAD_TIMEOUT = 500;
16
+ /** What `useMenuKeys` returns. */
17
+ export interface MenuKeys {
18
+ /** The `keydown` handler for the `role="menu"` element. */
19
+ onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
20
+ /**
21
+ * Forgets what has been typed, so the next character starts a new search.
22
+ * Call it when the menu opens, so a search typed just before it last
23
+ * closed doesn't carry over.
24
+ */
25
+ reset: () => void;
26
+ }
27
+ /**
28
+ * Moves through and activates the items of a `role="menu"` element from the
29
+ * keyboard. Put the `onKeyDown` it returns on that element. It reads the
30
+ * items from the element each time, so items that come and go are found,
31
+ * and it renders nothing.
32
+ *
33
+ * It acts only while focus is on the menu element itself or on one of its
34
+ * items, so anything else focusable inside the menu, such as a filter
35
+ * field, keeps its own keys.
36
+ *
37
+ * - Down and Up move to the next and previous enabled item, wrapping, and
38
+ * Home and End go to the first and last. From no item, Down goes to the
39
+ * first and Up to the last.
40
+ * - Enter and Space activate the focused item. A `<button>`, and a link with
41
+ * an `href` for Enter, get the browser's own click, and anything else gets
42
+ * one from here, so its `onClick` runs either way. A key held down
43
+ * activates it once. A key handler on the item that called
44
+ * `preventDefault()` has claimed the key, and nothing else runs it.
45
+ * - A printable character moves to the next enabled item whose text starts
46
+ * with what has been typed. Characters typed within `TYPEAHEAD_TIMEOUT` of
47
+ * each other add up to one search, until `reset` starts a new one.
48
+ *
49
+ * Items are `MENU_ITEM_SELECTOR` matches, and disabled ones are skipped by
50
+ * all of the above. Escape and Tab are left to the menu's owner, since what
51
+ * closing means is up to it, and so is any other key.
52
+ *
53
+ * @function useMenuKeys
54
+ * @returns The `keydown` handler for the menu element, and a way to forget
55
+ * what has been typed.
56
+ */
57
+ export declare function useMenuKeys(): MenuKeys;
@@ -19,6 +19,7 @@ export * from './components/Collapses.js';
19
19
  export * from './components/Tooltip.js';
20
20
  export * from './components/Popover.js';
21
21
  export * from './components/HoverCard.js';
22
+ export * from './components/ContextMenu.js';
22
23
  export * from './components/Steps.js';
23
24
  export * from './components/Sidebar.js';
24
25
  export * from './components/Toast.js';
@@ -30,6 +31,7 @@ export * from './elements/Box.js';
30
31
  export * from './elements/Button.js';
31
32
  export * from './elements/Buttons.js';
32
33
  export * from './elements/LinkButton.js';
34
+ export * from './elements/ToggleButton.js';
33
35
  export * from './elements/Code.js';
34
36
  export * from './elements/Content.js';
35
37
  export * from './elements/Delete.js';