@allxsmith/bestax-bulma 5.28.0 → 5.30.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 (94) 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/Dialog.js +8 -4
  6. package/dist/esm/components/Dialog.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/Modal.js +19 -11
  10. package/dist/esm/components/Modal.js.map +1 -1
  11. package/dist/esm/components/Popover.js.map +1 -1
  12. package/dist/esm/components/Sidebar.js +12 -3
  13. package/dist/esm/components/Sidebar.js.map +1 -1
  14. package/dist/esm/components/Toast.js +5 -3
  15. package/dist/esm/components/Toast.js.map +1 -1
  16. package/dist/esm/components/Tooltip.js +36 -61
  17. package/dist/esm/components/Tooltip.js.map +1 -1
  18. package/dist/esm/elements/Button.js.map +1 -1
  19. package/dist/esm/elements/Notification.js +3 -1
  20. package/dist/esm/elements/Notification.js.map +1 -1
  21. package/dist/esm/elements/ToggleButton.js +60 -0
  22. package/dist/esm/elements/ToggleButton.js.map +1 -0
  23. package/dist/esm/form/DateInputBase.js.map +1 -1
  24. package/dist/esm/form/DateRangeInputBase.js.map +1 -1
  25. package/dist/esm/form/DateTimeInputBase.js.map +1 -1
  26. package/dist/esm/form/TimeInputBase.js.map +1 -1
  27. package/dist/esm/form/_pickerInternals/PickerPopover.js +3 -1
  28. package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
  29. package/dist/esm/form/_pickerInternals/pickerTypes.js +14 -6
  30. package/dist/esm/form/_pickerInternals/pickerTypes.js.map +1 -1
  31. package/dist/esm/helpers/Config.js +5 -2
  32. package/dist/esm/helpers/Config.js.map +1 -1
  33. package/dist/esm/helpers/portal.js +30 -5
  34. package/dist/esm/helpers/portal.js.map +1 -1
  35. package/dist/esm/helpers/useContextMenuTrigger.js +166 -0
  36. package/dist/esm/helpers/useContextMenuTrigger.js.map +1 -0
  37. package/dist/esm/helpers/useHoverOpen.js +40 -6
  38. package/dist/esm/helpers/useHoverOpen.js.map +1 -1
  39. package/dist/esm/helpers/useMenuKeys.js +182 -0
  40. package/dist/esm/helpers/useMenuKeys.js.map +1 -0
  41. package/dist/extras.css +1 -1
  42. package/dist/extras.css.map +1 -1
  43. package/dist/index.cjs +2150 -1474
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.esm.js +2 -0
  46. package/dist/index.esm.js.map +1 -1
  47. package/dist/types/components/ContextMenu.d.ts +66 -0
  48. package/dist/types/components/HoverCard.d.ts +14 -12
  49. package/dist/types/components/Modal.d.ts +2 -1
  50. package/dist/types/components/Popover.d.ts +8 -7
  51. package/dist/types/components/Tooltip.d.ts +45 -4
  52. package/dist/types/elements/Button.d.ts +1 -1
  53. package/dist/types/elements/ToggleButton.d.ts +90 -0
  54. package/dist/types/form/DateInputBase.d.ts +4 -1
  55. package/dist/types/form/DateRangeInputBase.d.ts +4 -1
  56. package/dist/types/form/DateTimeInputBase.d.ts +8 -2
  57. package/dist/types/form/TimeInputBase.d.ts +7 -3
  58. package/dist/types/form/_pickerInternals/pickerTypes.d.ts +16 -1
  59. package/dist/types/helpers/Config.d.ts +34 -3
  60. package/dist/types/helpers/portal.d.ts +27 -5
  61. package/dist/types/helpers/useContextMenuTrigger.d.ts +67 -0
  62. package/dist/types/helpers/useHoverOpen.d.ts +19 -3
  63. package/dist/types/helpers/useMenuKeys.d.ts +57 -0
  64. package/dist/types/index.d.ts +2 -0
  65. package/dist/types-cjs/components/ContextMenu.d.ts +66 -0
  66. package/dist/types-cjs/components/HoverCard.d.ts +14 -12
  67. package/dist/types-cjs/components/Modal.d.ts +2 -1
  68. package/dist/types-cjs/components/Popover.d.ts +8 -7
  69. package/dist/types-cjs/components/Tooltip.d.ts +45 -4
  70. package/dist/types-cjs/elements/Button.d.ts +1 -1
  71. package/dist/types-cjs/elements/ToggleButton.d.ts +90 -0
  72. package/dist/types-cjs/form/DateInputBase.d.ts +4 -1
  73. package/dist/types-cjs/form/DateRangeInputBase.d.ts +4 -1
  74. package/dist/types-cjs/form/DateTimeInputBase.d.ts +8 -2
  75. package/dist/types-cjs/form/TimeInputBase.d.ts +7 -3
  76. package/dist/types-cjs/form/_pickerInternals/pickerTypes.d.ts +16 -1
  77. package/dist/types-cjs/helpers/Config.d.ts +34 -3
  78. package/dist/types-cjs/helpers/portal.d.ts +27 -5
  79. package/dist/types-cjs/helpers/useContextMenuTrigger.d.ts +67 -0
  80. package/dist/types-cjs/helpers/useHoverOpen.d.ts +19 -3
  81. package/dist/types-cjs/helpers/useMenuKeys.d.ts +57 -0
  82. package/dist/types-cjs/index.d.ts +2 -0
  83. package/dist/versions/bestax-no-dark-mode.css +1 -1
  84. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  85. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  86. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  87. package/dist/versions/bestax-no-helpers.css +1 -1
  88. package/dist/versions/bestax-no-helpers.css.map +1 -1
  89. package/dist/versions/bestax-prefixed.css +1 -1
  90. package/dist/versions/bestax-prefixed.css.map +1 -1
  91. package/package.json +1 -1
  92. package/src/scss/components/_contextmenu.scss +50 -0
  93. package/src/scss/components/_index.scss +1 -0
  94. package/src/scss/components/_tooltip.scss +76 -1
@@ -3,13 +3,14 @@ import React, { ReactNode } from 'react';
3
3
  type IconLibrary = 'fa' | 'mdi' | 'ion' | 'material-icons' | 'material-symbols';
4
4
  /**
5
5
  * Shape of the configuration context value.
6
- *
7
- * @property {string} [classPrefix] - Prefix applied to all Bulma class names.
8
- * @property {IconLibrary} [iconLibrary] - Default icon library for Icon components.
9
6
  */
10
7
  export interface ConfigContextProps {
8
+ /** Prefix applied to all Bulma class names. */
11
9
  classPrefix?: string;
10
+ /** Default icon library for Icon components. */
12
11
  iconLibrary?: IconLibrary;
12
+ /** Where portaled content goes when it has no target of its own. */
13
+ portalContainer?: string | HTMLElement;
13
14
  }
14
15
  /**
15
16
  * Hook to access the bulma-ui configuration context.
@@ -28,10 +29,40 @@ export interface ConfigProviderProps {
28
29
  classPrefix?: string;
29
30
  /** Default icon library for Icon components. */
30
31
  iconLibrary?: IconLibrary;
32
+ /**
33
+ * Where portaled content goes when it has no target of its own: what a
34
+ * `Portal` without a `container` renders, and what the library's components
35
+ * portal, such as Popover's `appendToBody` panel, a `Modal` with `portal`
36
+ * set to `true`, or a `Toast`. An element, or a `document.querySelector`
37
+ * selector. Unset, or a selector that matches nothing, that content goes to
38
+ * `document.body`. A target a component is given, such as a `Toast`'s
39
+ * `container`, still wins, and nothing portals that would otherwise render
40
+ * in place.
41
+ *
42
+ * The overlays and panels this moves are `position: fixed`, so pick an
43
+ * element with no ancestor that has a `transform`, `filter`,
44
+ * `backdrop-filter`, `perspective`, `will-change: transform`, or a
45
+ * `contain` that includes `paint` or `layout`. That ancestor becomes their
46
+ * containing block, and they cover or line up against it instead of the
47
+ * viewport. An ancestor that starts a stacking context, with a `z-index` or
48
+ * `isolation: isolate`, keeps them inside it, under anything the page
49
+ * stacks above it. `NotificationContainer`'s status region moves too, so
50
+ * this also decides where its announcements come from, and an `aria-hidden`
51
+ * ancestor silences them.
52
+ *
53
+ * A selector can't find an element inside a shadow root, so pass the
54
+ * element itself there, held in state from a ref callback. Content that
55
+ * portals before the element is set goes to `document.body` and moves when
56
+ * it is, which remounts it, as `Portal`'s `container` describes.
57
+ */
58
+ portalContainer?: string | HTMLElement;
31
59
  }
32
60
  /**
33
61
  * ConfigProvider injects configuration into all child components via React context.
34
62
  *
63
+ * A nested provider replaces the outer one's settings rather than adding to
64
+ * them, so inside it any setting it leaves out is back to its default.
65
+ *
35
66
  * @function
36
67
  * @param {ConfigProviderProps} props - Provider props.
37
68
  * @returns {JSX.Element} The context provider wrapping children.
@@ -14,6 +14,22 @@ import React from 'react';
14
14
  * @returns The resolved DOM node to portal into.
15
15
  */
16
16
  export declare function resolvePortalContainer(container?: string | HTMLElement): HTMLElement;
17
+ /**
18
+ * Where a component's portal goes, as a function to call when the portal is
19
+ * created: the target the component was given, else the `portalContainer` a
20
+ * `ConfigProvider` sets, else `document.body`, each read as
21
+ * `resolvePortalContainer` reads it. A component that portals resolves its
22
+ * target through this, so the provider's default reaches it.
23
+ *
24
+ * The hook only reads context, so it is safe to call on the server, ahead of
25
+ * any early return. The function it returns reads the DOM, so call it only
26
+ * where the portal is created, which the server never reaches.
27
+ *
28
+ * @function usePortalContainer
29
+ * @returns A function that takes the component's own target, if any, and
30
+ * returns the DOM node to portal into.
31
+ */
32
+ export declare function usePortalContainer(): (container?: string | HTMLElement) => HTMLElement;
17
33
  /**
18
34
  * Props for the Portal component.
19
35
  */
@@ -22,8 +38,10 @@ export interface PortalProps {
22
38
  children?: React.ReactNode;
23
39
  /**
24
40
  * Where the content goes: an element, or a `document.querySelector`
25
- * selector. Omitted, empty, or a selector that matches nothing, it goes to
26
- * `document.body`. A selector is looked up again on each render, so the
41
+ * selector. Omitted or empty, it goes to the `portalContainer` a
42
+ * `ConfigProvider` sets, and to `document.body` when none is set. A
43
+ * selector that matches nothing means `document.body` too. A selector is
44
+ * looked up again on each render, so the
27
45
  * content follows the target when it changes. Moving to another target
28
46
  * remounts the children and resets their state, which includes a selector
29
47
  * that only starts matching after the content has shown in `document.body`.
@@ -39,7 +57,7 @@ export interface PortalProps {
39
57
  disabled?: boolean;
40
58
  }
41
59
  /**
42
- * Renders its children into another part of the page, `document.body` unless `container` says otherwise, so floating content escapes an ancestor's `overflow`, `transform` or stacking context.
60
+ * Renders its children into another part of the page, `document.body` unless `container` or a `ConfigProvider` says otherwise, so floating content escapes an ancestor's `overflow`, `transform` or stacking context.
43
61
  *
44
62
  * React's server renderer can't render a portal, so `Portal` renders nothing
45
63
  * on the server and during hydration, and mounts its children in the commit
@@ -50,8 +68,12 @@ export interface PortalProps {
50
68
  * portaled children, and their React events bubble through the React tree.
51
69
  * CSS inheritance follows the DOM instead: a scoped (non-`isRoot`) `Theme`
52
70
  * sets its variables on a wrapper element the portaled content has left, so
53
- * that content sees the page-level theme. Point `container` at an element
54
- * inside the themed area to keep it.
71
+ * that content sees the page-level theme, and styles scoped to a shadow root
72
+ * don't reach `document.body` at all. To keep them, portal into an element
73
+ * inside the themed area or the shadow root: pass it as `container`, or as
74
+ * the `portalContainer` of a `ConfigProvider` around that area. The second
75
+ * also reaches what the library's components portal, such as Popover's
76
+ * `appendToBody` panel or a `Modal` with `portal`.
55
77
  *
56
78
  * Tab order and focus follow the DOM too. Content portaled from inside a
57
79
  * focus trap is no longer inside the trapped element, so the trap neither
@@ -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';