@allxsmith/bestax-bulma 5.37.0 → 5.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.
Files changed (83) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/esm/components/Card.js +2 -2
  4. package/dist/esm/components/Carousel.js +14 -6
  5. package/dist/esm/components/Carousel.js.map +1 -1
  6. package/dist/esm/components/Dropdown.js +7 -2
  7. package/dist/esm/components/Dropdown.js.map +1 -1
  8. package/dist/esm/components/Loading.js +7 -1
  9. package/dist/esm/components/Loading.js.map +1 -1
  10. package/dist/esm/components/Panel.js +34 -4
  11. package/dist/esm/components/Panel.js.map +1 -1
  12. package/dist/esm/components/Popover.js +7 -2
  13. package/dist/esm/components/Popover.js.map +1 -1
  14. package/dist/esm/components/Sidebar.js +46 -17
  15. package/dist/esm/components/Sidebar.js.map +1 -1
  16. package/dist/esm/form/Autocomplete.js +28 -18
  17. package/dist/esm/form/Autocomplete.js.map +1 -1
  18. package/dist/esm/form/Combobox.js +592 -0
  19. package/dist/esm/form/Combobox.js.map +1 -0
  20. package/dist/esm/form/Field.js +3 -3
  21. package/dist/esm/form/Field.js.map +1 -1
  22. package/dist/esm/form/_pickerInternals/PickerPopover.js +10 -5
  23. package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
  24. package/dist/esm/form/useAutoLabelId.js.map +1 -1
  25. package/dist/esm/helpers/Theme.js.map +1 -1
  26. package/dist/esm/helpers/useAnchoredPosition.js +35 -6
  27. package/dist/esm/helpers/useAnchoredPosition.js.map +1 -1
  28. package/dist/esm/helpers/useComboboxEngine.js +331 -0
  29. package/dist/esm/helpers/useComboboxEngine.js.map +1 -0
  30. package/dist/esm/helpers/useFocusTrap.js +87 -49
  31. package/dist/esm/helpers/useFocusTrap.js.map +1 -1
  32. package/dist/esm/helpers/useInertSiblings.js +106 -0
  33. package/dist/esm/helpers/useInertSiblings.js.map +1 -0
  34. package/dist/esm/helpers/useTreeView.js +2 -2
  35. package/dist/esm/layout/Container.js +2 -2
  36. package/dist/esm/layout/Container.js.map +1 -1
  37. package/dist/esm/layout/Hero.js +6 -6
  38. package/dist/esm/layout/Hero.js.map +1 -1
  39. package/dist/extras.css +1 -1
  40. package/dist/extras.css.map +1 -1
  41. package/dist/index.cjs +1504 -351
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/index.esm.js +1 -0
  44. package/dist/index.esm.js.map +1 -1
  45. package/dist/types/components/Loading.d.ts +16 -2
  46. package/dist/types/components/Panel.d.ts +45 -5
  47. package/dist/types/components/Sidebar.d.ts +17 -2
  48. package/dist/types/form/Combobox.d.ts +416 -0
  49. package/dist/types/form/Field.d.ts +5 -5
  50. package/dist/types/form/useAutoLabelId.d.ts +3 -2
  51. package/dist/types/helpers/Theme.d.ts +6 -0
  52. package/dist/types/helpers/useAnchoredPosition.d.ts +25 -2
  53. package/dist/types/helpers/useComboboxEngine.d.ts +111 -0
  54. package/dist/types/helpers/useFocusTrap.d.ts +15 -2
  55. package/dist/types/helpers/useInertSiblings.d.ts +30 -0
  56. package/dist/types/index.d.ts +1 -0
  57. package/dist/types/layout/Container.d.ts +9 -1
  58. package/dist/types/layout/Hero.d.ts +27 -2
  59. package/dist/types-cjs/components/Loading.d.ts +16 -2
  60. package/dist/types-cjs/components/Panel.d.ts +45 -5
  61. package/dist/types-cjs/components/Sidebar.d.ts +17 -2
  62. package/dist/types-cjs/form/Combobox.d.ts +416 -0
  63. package/dist/types-cjs/form/Field.d.ts +5 -5
  64. package/dist/types-cjs/form/useAutoLabelId.d.ts +3 -2
  65. package/dist/types-cjs/helpers/Theme.d.ts +6 -0
  66. package/dist/types-cjs/helpers/useAnchoredPosition.d.ts +25 -2
  67. package/dist/types-cjs/helpers/useComboboxEngine.d.ts +111 -0
  68. package/dist/types-cjs/helpers/useFocusTrap.d.ts +15 -2
  69. package/dist/types-cjs/helpers/useInertSiblings.d.ts +30 -0
  70. package/dist/types-cjs/index.d.ts +1 -0
  71. package/dist/types-cjs/layout/Container.d.ts +9 -1
  72. package/dist/types-cjs/layout/Hero.d.ts +27 -2
  73. package/dist/versions/bestax-no-dark-mode.css +1 -1
  74. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  75. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  76. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  77. package/dist/versions/bestax-no-helpers.css +1 -1
  78. package/dist/versions/bestax-no-helpers.css.map +1 -1
  79. package/dist/versions/bestax-prefixed.css +1 -1
  80. package/dist/versions/bestax-prefixed.css.map +1 -1
  81. package/package.json +1 -1
  82. package/src/scss/form/_combobox.scss +323 -0
  83. package/src/scss/form/_index.scss +1 -0
@@ -10,6 +10,11 @@ export interface AnchoredPosition {
10
10
  position: AnchoredCorner;
11
11
  top?: number;
12
12
  left?: number;
13
+ /**
14
+ * With `matchWidth`, the anchor's width, which a fixed panel takes as its
15
+ * `min-width`.
16
+ */
17
+ minWidth?: number;
13
18
  }
14
19
  /**
15
20
  * Options for `useAnchoredPosition`.
@@ -41,6 +46,23 @@ export interface UseAnchoredPositionOptions {
41
46
  * well; the coordinates don't include it, because the stylesheet applies it.
42
47
  */
43
48
  styledGap?: (panel: HTMLElement) => number;
49
+ /**
50
+ * Whether the panel is at least as wide as the anchor, as a list under its
51
+ * field is. A panel in place gets that from its stylesheet. A fixed one
52
+ * can't, so its placement carries the anchor's width as `minWidth` for the
53
+ * panel to set, and its coordinates and `auto` already allow for it.
54
+ * @defaultValue false
55
+ */
56
+ matchWidth?: boolean;
57
+ /**
58
+ * Measures again after each render of the component holding the hook, for
59
+ * a panel whose content changes while it's open, as a filtered list's
60
+ * does. A fixed panel above its anchor is placed from its height, and
61
+ * `auto` reads the height too, so a measurement taken when it opened goes
62
+ * stale as the content changes.
63
+ * @defaultValue false
64
+ */
65
+ followContent?: boolean;
44
66
  }
45
67
  /**
46
68
  * A `styledGap` for a panel whose stylesheet puts the gap in a margin on the
@@ -53,7 +75,8 @@ export declare function marginGap(panel: HTMLElement): number;
53
75
  * and, for a panel fixed to the viewport, works out its coordinates. It
54
76
  * measures again on resize and on any scroll of the anchor's window (an
55
77
  * iframe's, when the anchor renders into one), so a fixed panel follows its
56
- * anchor. A measurement that changes nothing doesn't render again.
78
+ * anchor, and with `followContent` after each render too. A measurement that
79
+ * changes nothing doesn't render again.
57
80
  *
58
81
  * Measuring happens in a layout effect, so the panel is placed before it
59
82
  * paints. It needs both elements on the page while `active` is on, and
@@ -65,4 +88,4 @@ export declare function marginGap(panel: HTMLElement): number;
65
88
  * @param options - Whether to measure, the corner asked for, and how the panel is placed.
66
89
  * @returns The corner the panel opens from, with coordinates when it is fixed.
67
90
  */
68
- export declare function useAnchoredPosition(anchorRef: RefObject<HTMLElement | null>, panelRef: RefObject<HTMLElement | null>, { active, position, fixed, offset, styledGap }: UseAnchoredPositionOptions): AnchoredPosition;
91
+ export declare function useAnchoredPosition(anchorRef: RefObject<HTMLElement | null>, panelRef: RefObject<HTMLElement | null>, { active, position, fixed, offset, styledGap, matchWidth, followContent, }: UseAnchoredPositionOptions): AnchoredPosition;
@@ -0,0 +1,111 @@
1
+ import type React from 'react';
2
+ /** What an item tells the engine about itself. */
3
+ export interface ComboboxItemState {
4
+ /** What a pick hands back. Unique within the combobox. */
5
+ value: string;
6
+ /** Reachable with the arrows, never picked. */
7
+ disabled: boolean;
8
+ /** The value, or one of them. */
9
+ checked: boolean;
10
+ }
11
+ /** Options for `useComboboxEngine`. */
12
+ export interface ComboboxEngineOptions {
13
+ /** Controlled open state. When set, opening and closing only report through `onOpenChange`. */
14
+ open?: boolean;
15
+ /** Initial open state when uncontrolled. */
16
+ defaultOpen: boolean;
17
+ /** Called with the open state asked for, when it differs from the current one. */
18
+ onOpenChange?: (open: boolean) => void;
19
+ /** Called with an item's `value` when it's picked. */
20
+ onSelect?: (value: string) => void;
21
+ /**
22
+ * Called when Enter goes on to the form with nothing to pick, before the
23
+ * list closes, so an owner that shows something other than the typing can
24
+ * put it back before the form reads the field. An `inline` list doesn't
25
+ * close, so this is the one word it gets.
26
+ */
27
+ onSettle?: () => void;
28
+ /** Highlights the first enabled item whenever the list opens or its items change. */
29
+ keepFirst: boolean;
30
+ /** Several values: a pick keeps the list open. */
31
+ multiple: boolean;
32
+ /** The list is always on screen, so nothing opens or closes it. */
33
+ inline: boolean;
34
+ /**
35
+ * The list's panel is portaled, so it has no server markup and shows only
36
+ * from the commit after hydration. Until it has rendered, an open list
37
+ * isn't taken to be on the page, so no `aria-controls` names it early.
38
+ */
39
+ deferred: boolean;
40
+ }
41
+ /** What `useComboboxEngine` hands the parts of a combobox. */
42
+ export interface ComboboxEngine {
43
+ /** Whether the list is open. Always, with `inline`. */
44
+ open: boolean;
45
+ /**
46
+ * The field's `aria-expanded`: open, with a listbox that has an option in
47
+ * it on the page. Until the open list has rendered, it's taken to have
48
+ * one, so server markup for an open list in place says it's expanded. A
49
+ * `deferred` list isn't in server markup, so it's taken to have none.
50
+ */
51
+ expanded: boolean;
52
+ /** The highlighted option's value, if any. */
53
+ highlighted: string | null;
54
+ /** Whether the user has typed since the list opened. */
55
+ typed: boolean;
56
+ /** The listbox's id. */
57
+ listboxId: string;
58
+ /** An item's id, which follows from its value. */
59
+ itemId: (value: string) => string;
60
+ /** For the element around the field and, unless it's portaled, the list. */
61
+ rootRef: React.MutableRefObject<HTMLDivElement | null>;
62
+ /** For the field. */
63
+ inputRef: React.MutableRefObject<HTMLInputElement | null>;
64
+ /** For the listbox. */
65
+ listboxRef: React.MutableRefObject<HTMLDivElement | null>;
66
+ /**
67
+ * Records the floating panel while it's on screen, which makes it count as
68
+ * inside, and makes an Escape that closes it stop there. Returns the
69
+ * function that forgets it.
70
+ */
71
+ registerPopup: (element: HTMLElement) => () => void;
72
+ /** Records an item's element. Returns the function that forgets it. */
73
+ registerItem: (element: HTMLElement, state: ComboboxItemState) => () => void;
74
+ /** The field's keys, to run after the caller's own handler. */
75
+ onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
76
+ /** A change to the field's value: the user typed. */
77
+ onChange: () => void;
78
+ /** A click in the field. */
79
+ onClick: () => void;
80
+ /** Focus leaving the field. */
81
+ onBlur: (event: React.FocusEvent<HTMLInputElement>) => void;
82
+ /** The pointer moved over an enabled option. */
83
+ hover: (value: string) => void;
84
+ /** A click on an enabled option. Focus ends up in the field. */
85
+ pick: (value: string) => void;
86
+ /** For anything in the list: a press there doesn't take focus from the field. */
87
+ keepFocus: (event: React.MouseEvent) => void;
88
+ /**
89
+ * For the floating panel: marks a press as inside, which React reports from
90
+ * content the panel portals elsewhere as well.
91
+ */
92
+ markInside: (event: React.PointerEvent) => void;
93
+ }
94
+ /**
95
+ * The keys, the open state, the highlight and the ARIA of an editable
96
+ * combobox whose list the app renders and filters (#1083). DOM focus stays
97
+ * in the field the whole time: the highlight is `aria-activedescendant`, as
98
+ * the ARIA Authoring Practices ask of a combobox, where `useMenuKeys` moves
99
+ * real focus, as a menu should.
100
+ *
101
+ * The options are read from the listbox in document order whenever they're
102
+ * needed, so options in groups and options that come and go are found, and
103
+ * the highlight is remembered by value, so it survives a re-render. The text
104
+ * in the field and any value are the app's: a pick only calls
105
+ * `onSelect`.
106
+ *
107
+ * @function useComboboxEngine
108
+ * @param options - The open state, the pick callback and the shape.
109
+ * @returns The state and handlers the parts wire up.
110
+ */
111
+ export declare function useComboboxEngine({ open: controlledOpen, defaultOpen, onOpenChange, onSelect, onSettle, keepFirst, multiple, inline, deferred, }: ComboboxEngineOptions): ComboboxEngine;
@@ -11,10 +11,23 @@ export declare function isTabbableKind(el: Element): boolean;
11
11
  /**
12
12
  * The first tab stop inside `container`, found the way the trap finds it, or
13
13
  * `null` when there is none. For a component that moves focus into a panel
14
- * without trapping it there. `src/index.ts` names its exports from this
15
- * module, so this one stays internal.
14
+ * itself rather than through the hook. `src/index.ts` names its exports from
15
+ * this module, so this one stays internal.
16
16
  */
17
17
  export declare function firstTabStop(container: HTMLElement): HTMLElement | null;
18
+ /**
19
+ * Holds Tab inside `container` until the returned function is called. Tab
20
+ * and Shift+Tab heard on `keyTarget` wrap at the ends, unless something
21
+ * already marked the key used, and if a Tab the trap let through still takes
22
+ * focus out, it sends focus back to the other end.
23
+ *
24
+ * `holds` is asked on every Tab, and the trap leaves the key alone while it
25
+ * says no. Internal like `firstTabStop`. It's for an overlay that holds Tab
26
+ * from a listener of its own on `window` and asks the overlay stack first,
27
+ * so it stands aside while another overlay is open over it, which a trap
28
+ * listening on the container can't do.
29
+ */
30
+ export declare function trapTab(container: HTMLElement, keyTarget?: EventTarget, holds?: () => boolean): () => void;
18
31
  /**
19
32
  * Options for `useFocusTrap`.
20
33
  */
@@ -0,0 +1,30 @@
1
+ import type { RefObject } from 'react';
2
+ /**
3
+ * Makes the elements beside `ref`'s element inert while `active` is true,
4
+ * and takes it off again when `active` turns false or the caller unmounts.
5
+ * With `wholePage`, it does the same beside each element above it up to
6
+ * `<body>`, so everything on the page but `ref`'s element is inert, the way a
7
+ * modal layer leaves it. The element itself and its own content are never
8
+ * touched.
9
+ *
10
+ * It sets the `inert` attribute through the DOM in an effect, so it works the
11
+ * same on React 18 and 19 and a server render has nothing to show for it. An
12
+ * element that was inert already is left alone, and keeps its `inert` once
13
+ * this lets go. It makes inert what's there when it turns on: an element that
14
+ * mounts beside it afterwards isn't. It follows the DOM, so content a web
15
+ * component renders around a slot this sits in, and text with no element of
16
+ * its own, stays as it is.
17
+ *
18
+ * Focus on an element that turns inert is lost: the browser moves it to
19
+ * `<body>`. So when focus was inside what this made inert, and is still on
20
+ * `<body>` when this lets go, it goes back to the element that had it. Focus
21
+ * this never took isn't handed anywhere, and focus the user or the app has
22
+ * put somewhere else meanwhile stays there.
23
+ *
24
+ * @function useInertSiblings
25
+ * @param ref - Ref to the element that stays reachable.
26
+ * @param active - Whether what's beside it is inert.
27
+ * @param wholePage - Whether that goes for the whole page rather than only
28
+ * the element's siblings.
29
+ */
30
+ export declare function useInertSiblings(ref: RefObject<Element | null>, active: boolean, wholePage: boolean): void;
@@ -81,6 +81,7 @@ export * from './form/Slider.js';
81
81
  export * from './form/Numberinput.js';
82
82
  export * from './form/Rate.js';
83
83
  export * from './form/Autocomplete.js';
84
+ export * from './form/Combobox.js';
84
85
  export * from './form/Taginput.js';
85
86
  export { InputBase } from './form/InputBase.js';
86
87
  export type { InputBaseProps } from './form/InputBase.js';
@@ -7,7 +7,15 @@ export type ContainerBreakpoint = 'tablet' | 'desktop' | 'widescreen';
7
7
  /**
8
8
  * Props for the Container component.
9
9
  */
10
- export interface ContainerProps extends React.HTMLAttributes<HTMLDivElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
10
+ export interface ContainerProps extends React.HTMLAttributes<HTMLElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
11
+ /**
12
+ * HTML element to render (default: `div`). Bulma pages often put the
13
+ * container on a landmark, such as `<main class="container">` or
14
+ * `<footer class="container">`. A page has only one visible `main`, and a
15
+ * `section` is a `region` landmark only once it has an accessible name
16
+ * (`aria-label`, or `aria-labelledby` pointing at its heading).
17
+ */
18
+ as?: 'div' | 'main' | 'section' | 'article' | 'aside' | 'header' | 'footer' | 'nav';
11
19
  /** Additional CSS classes. */
12
20
  className?: string;
13
21
  /** Text color helper. */
@@ -8,6 +8,15 @@ export type HeroSize = 'small' | 'medium' | 'large' | 'fullheight' | 'fullheight
8
8
  * Props for the Hero component.
9
9
  */
10
10
  export interface HeroProps extends React.HTMLAttributes<HTMLElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
11
+ /**
12
+ * HTML element to render (default: `section`). Bulma pages often put the
13
+ * hero on a landmark: `main` when the hero is the page's main content,
14
+ * `header` for a banner above it, `footer` for one below it. A page has
15
+ * only one visible `main`, and a `section` is a `region` landmark only once
16
+ * it has an accessible name (`aria-label`, or `aria-labelledby` pointing at
17
+ * its heading).
18
+ */
19
+ as?: 'section' | 'div' | 'main' | 'header' | 'footer' | 'article' | 'aside';
11
20
  /** Additional CSS classes. */
12
21
  className?: string;
13
22
  /**
@@ -37,7 +46,15 @@ export interface HeroProps extends React.HTMLAttributes<HTMLElement>, Omit<Bulma
37
46
  /**
38
47
  * Props for the HeroHead component.
39
48
  */
40
- export interface HeroHeadProps extends React.HTMLAttributes<HTMLDivElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
49
+ export interface HeroHeadProps extends React.HTMLAttributes<HTMLElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
50
+ /**
51
+ * HTML element to render (default: `div`). `nav` suits a head that holds
52
+ * only the site's navigation. A `header` is a `banner` landmark only when
53
+ * no `section`, `article`, `aside`, `nav` or `main` is around it, so inside
54
+ * the default `<section>` hero it is not one: render the hero as a `div`
55
+ * for that. A `header` may not sit inside another `header` or a `footer`.
56
+ */
57
+ as?: 'div' | 'header' | 'nav';
41
58
  /** Additional CSS classes. */
42
59
  className?: string;
43
60
  /** Bulma color modifier for text. */
@@ -93,7 +110,15 @@ export declare const HeroBody: React.FC<HeroBodyProps>;
93
110
  /**
94
111
  * Props for the HeroFoot component.
95
112
  */
96
- export interface HeroFootProps extends React.HTMLAttributes<HTMLDivElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
113
+ export interface HeroFootProps extends React.HTMLAttributes<HTMLElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
114
+ /**
115
+ * HTML element to render (default: `div`). `nav` suits a foot that holds
116
+ * tabs or links. A `footer` is a `contentinfo` landmark only when no
117
+ * `section`, `article`, `aside`, `nav` or `main` is around it, so inside
118
+ * the default `<section>` hero it is not one: render the hero as a `div`
119
+ * for that. A `footer` may not sit inside another `footer` or a `header`.
120
+ */
121
+ as?: 'div' | 'footer' | 'nav';
97
122
  /** Additional CSS classes. */
98
123
  className?: string;
99
124
  /** Bulma color modifier for text. */
@@ -14,10 +14,24 @@ export type LoadingColor = 'primary' | 'link' | 'info' | 'success' | 'warning' |
14
14
  * @extraProp {string} [className] - Additional CSS classes.
15
15
  */
16
16
  export interface LoadingProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor' | 'size' | 'overlay'> {
17
- /** Whether the loading overlay is visible. */
17
+ /**
18
+ * Whether the loading overlay is visible. While it is, what it covers is
19
+ * inert, out of reach of Tab, Enter, Space and screen readers as well as
20
+ * the mouse: the other children of the element it's rendered in, so render
21
+ * it directly inside the element it covers, or with `isFullPage` the rest
22
+ * of the page. Its own content, the cancel button included, stays
23
+ * reachable. Only what's there when it turns on is made inert, and an
24
+ * element that was inert already is left as it was. A control under it
25
+ * that had focus loses it, and gets it back when the loading goes away,
26
+ * unless focus has moved on in the meantime. Show one at a time over the
27
+ * same content: two that cover each other, such as two in one container
28
+ * or two full-page ones, make each other inert, cancel buttons and all.
29
+ */
18
30
  active?: boolean;
19
31
  /**
20
- * Cover the entire viewport. A full-page loading also takes its turn with
32
+ * Cover the entire viewport. While it's active, the rest of the page is
33
+ * inert, as it is under a modal, rather than only the element it's
34
+ * rendered in. A full-page loading also takes its turn with
21
35
  * Modal and Sidebar for Escape: while it's the topmost open overlay, the
22
36
  * Modal or Sidebar under it doesn't answer Escape or hold Tab, whether or
23
37
  * not the loading can be cancelled.
@@ -1,10 +1,19 @@
1
1
  import React from 'react';
2
2
  import { BulmaClassesProps } from '../helpers/useBulmaClasses.js';
3
+ import type { ConstrainedPolymorphicComponentWithoutRef } from '../helpers/polymorphic.js';
3
4
  import { IconChildrenProps, IconDeprecatedProps, IconNameProps } from '../elements/Icon.js';
4
5
  /**
5
6
  * Props for the Panel component.
6
7
  */
7
8
  export interface PanelProps extends React.HTMLAttributes<HTMLElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
9
+ /**
10
+ * HTML element to render (default: `nav`). A `nav` is a `navigation`
11
+ * landmark, which suits a panel of links; a panel that is not navigation
12
+ * takes another tag. Bulma's own colored panels use `article`. A `section`
13
+ * is a `region` landmark only once it has an accessible name, such as
14
+ * `aria-labelledby` pointing at the `id` of its `Panel.Heading`.
15
+ */
16
+ as?: 'nav' | 'article' | 'section' | 'aside' | 'div';
8
17
  /** Bulma color modifier for the panel. */
9
18
  color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger' | 'black' | 'dark' | 'light' | 'white';
10
19
  /** Additional CSS classes. */
@@ -31,9 +40,14 @@ export interface PanelTabsProps extends React.HTMLAttributes<HTMLDivElement> {
31
40
  children?: React.ReactNode;
32
41
  }
33
42
  /**
34
- * Props for the PanelBlock component.
43
+ * The elements a panel block may render as: the tags Bulma's panel markup
44
+ * puts `.panel-block` on.
35
45
  */
36
- export interface PanelBlockProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
46
+ export type PanelBlockElement = 'a' | 'div' | 'label';
47
+ /**
48
+ * The PanelBlock component's own props.
49
+ */
50
+ export interface PanelBlockOwnProps {
37
51
  /** Additional CSS classes. */
38
52
  className?: string;
39
53
  /** Whether the block is active. */
@@ -41,6 +55,32 @@ export interface PanelBlockProps extends React.AnchorHTMLAttributes<HTMLAnchorEl
41
55
  /** Block content. */
42
56
  children?: React.ReactNode;
43
57
  }
58
+ /**
59
+ * Props for the PanelBlock component. Everything the block does not own
60
+ * follows `as`: the anchor's attributes and an `HTMLAnchorElement`
61
+ * `currentTarget` under the default `'a'`, `htmlFor` under `'label'`, and the
62
+ * shared HTML attributes under any of them.
63
+ *
64
+ * The type parameter defaults to `'a'`, the element a block renders with no
65
+ * `as`, rather than to the whole union the way `DropdownItemProps` does. Before
66
+ * `as` existed this type was the anchor's attributes, so bare
67
+ * `PanelBlockProps` stays exactly that, `href` and anchor-typed handlers
68
+ * included. Write `PanelBlockProps<'div'>` or `PanelBlockProps<'label'>` for
69
+ * the others.
70
+ */
71
+ export type PanelBlockProps<T extends PanelBlockElement = 'a'> = PanelBlockOwnProps & Omit<React.ComponentPropsWithoutRef<T>, keyof PanelBlockOwnProps | 'as'> & {
72
+ /**
73
+ * HTML element to render (default: `a`). Bulma styles a `label` block
74
+ * as clickable, like an `a`, and a `div` block as static content, such
75
+ * as a table. The props follow the tag, so the anchor attributes
76
+ * (`href`, `target`, `download` and the rest) are typed only on an `a`
77
+ * and `htmlFor` only on a `label`; at runtime the block also drops them,
78
+ * and `rel`, on the other tags. `Panel.CheckboxBlock`,
79
+ * `Panel.InputBlock` and `Panel.ButtonBlock` render the usual `label`
80
+ * and `div` blocks with their contents.
81
+ */
82
+ as?: T;
83
+ };
44
84
  /**
45
85
  * Props for the PanelIcon component.
46
86
  * Extends IconProps but uses 'panel-icon' as the container class.
@@ -94,13 +134,13 @@ export declare const PanelHeading: React.FC<PanelHeadingProps>;
94
134
  */
95
135
  export declare const PanelTabs: React.FC<PanelTabsProps>;
96
136
  /**
97
- * Individual panel block (renders as `<a class="panel-block">`)
137
+ * Individual panel block (renders as `<a class="panel-block">` unless `as` says otherwise)
98
138
  *
99
139
  * @function
100
140
  * @param {PanelBlockProps} props - Props for the PanelBlock component.
101
141
  * @returns {JSX.Element} The rendered panel block.
102
142
  */
103
- export declare const PanelBlock: React.FC<PanelBlockProps>;
143
+ export declare const PanelBlock: ConstrainedPolymorphicComponentWithoutRef<PanelBlockOwnProps, PanelBlockElement, "a">;
104
144
  /**
105
145
  * Icon wrapper with panel styling (renders as `<span class="panel-icon">`, containing an
106
146
  * `<i/>` when a `name` is given, or the custom node passed as `children`).
@@ -138,7 +178,7 @@ export declare const PanelButtonBlock: React.FC<PanelButtonBlockProps>;
138
178
  export declare const Panel: React.FC<PanelProps> & {
139
179
  Heading: React.FC<PanelHeadingProps>;
140
180
  Tabs: React.FC<PanelTabsProps>;
141
- Block: React.FC<PanelBlockProps>;
181
+ Block: ConstrainedPolymorphicComponentWithoutRef<PanelBlockOwnProps, PanelBlockElement, "a", never>;
142
182
  Icon: React.FC<PanelIconProps>;
143
183
  InputBlock: React.FC<PanelInputBlockProps>;
144
184
  CheckboxBlock: React.FC<PanelCheckboxBlockProps>;
@@ -8,7 +8,15 @@ export type SidebarPosition = 'left' | 'right';
8
8
  * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the sidebar element.
9
9
  */
10
10
  export interface SidebarProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children' | 'color'>, BulmaClassesProps {
11
- /** Whether the sidebar is open (required). */
11
+ /**
12
+ * Whether the sidebar is open (required). Opening moves focus into the
13
+ * sidebar, to its first tab stop or the sidebar itself. Closing hands focus
14
+ * back to whatever had it when the sidebar opened, unless focus has already
15
+ * left the sidebar, as it does for an overlay opened over it. A closed
16
+ * sidebar stays in the page, so it can slide out, and is `inert` and
17
+ * `aria-hidden` there: Tab passes over its content and assistive
18
+ * technology doesn't read it.
19
+ */
12
20
  isOpen: boolean;
13
21
  /** Callback when sidebar should close. */
14
22
  onClose?: () => void;
@@ -20,7 +28,14 @@ export interface SidebarProps extends Omit<React.HTMLAttributes<HTMLElement>, 'c
20
28
  isFullwidth?: boolean;
21
29
  /** Sidebar takes full width (mobile-style). @deprecated Use `isFullwidth` instead — `isFullwidth` wins if both are set. */
22
30
  fullWidth?: boolean;
23
- /** Show overlay behind sidebar. Default: true. */
31
+ /**
32
+ * Show an overlay behind the sidebar, which makes it modal: it gets
33
+ * `aria-modal="true"`, locks body scroll, and keeps Tab and Shift+Tab
34
+ * inside it while it is the topmost overlay. A Modal or Dialog opened over
35
+ * it, even one rendered inside it, holds Tab until it closes. Without an
36
+ * overlay the sidebar isn't modal, and Tab moves in and out of it freely.
37
+ * Default: true.
38
+ */
24
39
  overlay?: boolean;
25
40
  /** Close sidebar when overlay is clicked. Default: true. */
26
41
  overlayClose?: boolean;