@allxsmith/bestax-bulma 5.59.0 → 5.61.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/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/Affix.js +46 -0
- package/dist/esm/components/Affix.js.map +1 -0
- package/dist/esm/components/Carousel.js +3 -43
- package/dist/esm/components/Carousel.js.map +1 -1
- package/dist/esm/components/Command.js +3 -3
- package/dist/esm/components/Dialog.js +2 -6
- package/dist/esm/components/Dialog.js.map +1 -1
- package/dist/esm/components/Modal.js +12 -24
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/components/Navbar.js +3 -1
- package/dist/esm/components/Navbar.js.map +1 -1
- package/dist/esm/components/Popover.js +7 -27
- package/dist/esm/components/Popover.js.map +1 -1
- package/dist/esm/components/Sidebar.js +17 -58
- package/dist/esm/components/Sidebar.js.map +1 -1
- package/dist/esm/form/ColorPicker.js +4 -4
- package/dist/esm/form/ColorPicker.js.map +1 -1
- package/dist/esm/form/Combobox.js +3 -3
- package/dist/esm/helpers/overlayStack.js +33 -1
- package/dist/esm/helpers/overlayStack.js.map +1 -1
- package/dist/esm/helpers/portal.js +28 -1
- package/dist/esm/helpers/portal.js.map +1 -1
- package/dist/esm/helpers/useBulmaViewport.js +82 -0
- package/dist/esm/helpers/useBulmaViewport.js.map +1 -0
- package/dist/esm/helpers/useInertSiblings.js +7 -3
- package/dist/esm/helpers/useInertSiblings.js.map +1 -1
- package/dist/esm/helpers/useOutsidePress.js +53 -0
- package/dist/esm/helpers/useOutsidePress.js.map +1 -0
- package/dist/esm/helpers/useOverlayFocus.js +74 -0
- package/dist/esm/helpers/useOverlayFocus.js.map +1 -0
- package/dist/esm/layout/AppFrame.js +438 -0
- package/dist/esm/layout/AppFrame.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +754 -160
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/Affix.d.ts +67 -0
- package/dist/types/components/Navbar.d.ts +4 -3
- package/dist/types/helpers/overlayStack.d.ts +17 -0
- package/dist/types/helpers/portal.d.ts +42 -0
- package/dist/types/helpers/useBulmaViewport.d.ts +34 -0
- package/dist/types/helpers/useInertSiblings.d.ts +4 -1
- package/dist/types/helpers/useOutsidePress.d.ts +24 -0
- package/dist/types/helpers/useOverlayFocus.d.ts +44 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/layout/AppFrame.d.ts +306 -0
- package/dist/types-cjs/components/Affix.d.ts +67 -0
- package/dist/types-cjs/components/Navbar.d.ts +4 -3
- package/dist/types-cjs/helpers/overlayStack.d.ts +17 -0
- package/dist/types-cjs/helpers/portal.d.ts +42 -0
- package/dist/types-cjs/helpers/useBulmaViewport.d.ts +34 -0
- package/dist/types-cjs/helpers/useInertSiblings.d.ts +4 -1
- package/dist/types-cjs/helpers/useOutsidePress.d.ts +24 -0
- package/dist/types-cjs/helpers/useOverlayFocus.d.ts +44 -0
- package/dist/types-cjs/index.d.ts +2 -0
- package/dist/types-cjs/layout/AppFrame.d.ts +306 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/components/_affix.scss +74 -0
- package/src/scss/components/_index.scss +1 -0
- package/src/scss/layout/_appframe.scss +306 -0
- package/src/scss/layout/_index.scss +1 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BulmaClassesProps } from '../helpers/useBulmaClasses.js';
|
|
3
|
+
import type { BadgePosition } from './Badge.js';
|
|
4
|
+
/** A corner of the viewport: the same four values as `BadgePosition`. */
|
|
5
|
+
export type AffixPosition = BadgePosition;
|
|
6
|
+
/**
|
|
7
|
+
* Props for the Affix component. The helper props, the other attributes and
|
|
8
|
+
* the `ref` land on its `<div>`.
|
|
9
|
+
*
|
|
10
|
+
* @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the `<div>`, wherever it renders.
|
|
11
|
+
*/
|
|
12
|
+
export interface AffixProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
13
|
+
/** Additional CSS classes to apply. */
|
|
14
|
+
className?: string;
|
|
15
|
+
/** What stays in the corner. */
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* The corner of the viewport it's fixed to. Physical, as Badge's and
|
|
19
|
+
* Toast's are: `'bottom-right'` stays on the right in right-to-left text.
|
|
20
|
+
* Its distance from the edges is `--bulma-affix-offset-vertical` and
|
|
21
|
+
* `--bulma-affix-offset-horizontal` plus the device's safe-area inset; a
|
|
22
|
+
* margin from the spacing helpers (`mb`, `mr`, …) adds to it. A margin on
|
|
23
|
+
* one side also takes that much of the width its content can have, which
|
|
24
|
+
* the offset variables don't, so move it far with those.
|
|
25
|
+
* @defaultValue 'bottom-right'
|
|
26
|
+
*/
|
|
27
|
+
position?: AffixPosition;
|
|
28
|
+
/**
|
|
29
|
+
* Renders it into another container, for a page where an ancestor's
|
|
30
|
+
* `transform`, `filter` or `contain` would capture `position: fixed`.
|
|
31
|
+
* `true` portals to the `portalContainer` a `ConfigProvider` sets,
|
|
32
|
+
* `document.body` by default; a string is used as a
|
|
33
|
+
* `document.querySelector` selector; an element is used directly. Renders
|
|
34
|
+
* in place on the server and while hydrating, moving into the portal once
|
|
35
|
+
* the client takes over, so hydration matches. Portaled, it's appended to
|
|
36
|
+
* that container when it mounts, so it comes after what's already there in
|
|
37
|
+
* the reading and Tab order.
|
|
38
|
+
* @defaultValue false
|
|
39
|
+
*/
|
|
40
|
+
portal?: boolean | string | HTMLElement;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The `Affix` component keeps its content fixed to a corner of the viewport while the page scrolls under it.
|
|
44
|
+
*
|
|
45
|
+
* It's a `<div>` with no role, so what's inside keeps its own. It sits
|
|
46
|
+
* `--bulma-affix-offset-vertical` and `--bulma-affix-offset-horizontal`
|
|
47
|
+
* from its corner's edges, plus the device's safe-area inset, and stacks at
|
|
48
|
+
* `--bulma-affix-z-index`, over the page's content and under its navbar,
|
|
49
|
+
* static or fixed, its menus, popovers, the Sidebar, the modals, Tooltip and
|
|
50
|
+
* Toast. It's as wide as its
|
|
51
|
+
* content and never wider than the viewport less its offsets. It doesn't
|
|
52
|
+
* scroll, so nothing inside it is clipped, and content that can be taller
|
|
53
|
+
* than the screen scrolls itself. Its place in the reading and Tab order is
|
|
54
|
+
* its place in the DOM, whatever corner it shows in.
|
|
55
|
+
*
|
|
56
|
+
* @function
|
|
57
|
+
* @param {AffixProps} props - Props for the Affix component.
|
|
58
|
+
* @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the `<div>`.
|
|
59
|
+
* @returns The `<div>`, in place or in its portal.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* <Affix position="bottom-left">
|
|
63
|
+
* <Button color="primary">Feedback</Button>
|
|
64
|
+
* </Affix>
|
|
65
|
+
*/
|
|
66
|
+
export declare const Affix: React.ForwardRefExoticComponent<AffixProps & React.RefAttributes<HTMLDivElement>>;
|
|
67
|
+
export default Affix;
|
|
@@ -126,7 +126,7 @@ export declare const NavbarItem: PolymorphicComponent<NavbarItemOwnProps, "a">;
|
|
|
126
126
|
* @extraProp {React.Ref<HTMLButtonElement>} [ref] - Ref forwarded to the burger button element.
|
|
127
127
|
* @extraProp {'button' | 'submit' | 'reset'} [type='button'] - Button type. Defaults to `'button'`, so a burger inside a form does not submit it. Pass `'submit'` or `'reset'` and yours is used; any other value, or a spread carrying `type: undefined`, renders `'button'`.
|
|
128
128
|
*/
|
|
129
|
-
export interface NavbarBurgerProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
129
|
+
export interface NavbarBurgerProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'aria-expanded'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
130
130
|
/** Additional CSS classes. */
|
|
131
131
|
className?: string;
|
|
132
132
|
/** Text color for the burger. */
|
|
@@ -155,11 +155,12 @@ export interface NavbarBurgerProps extends React.ButtonHTMLAttributes<HTMLButton
|
|
|
155
155
|
'aria-label'?: string;
|
|
156
156
|
/**
|
|
157
157
|
* Expanded state for assistive technology. It follows `active` unless you
|
|
158
|
-
* pass `true` or `false
|
|
158
|
+
* pass `true` or `false`, and `null` leaves it off, for a burger whose
|
|
159
|
+
* state isn't known yet; a spread carrying `'aria-expanded': undefined`
|
|
159
160
|
* still follows `active`.
|
|
160
161
|
* @defaultValue active
|
|
161
162
|
*/
|
|
162
|
-
'aria-expanded'?: boolean;
|
|
163
|
+
'aria-expanded'?: boolean | null;
|
|
163
164
|
/** Click handler. */
|
|
164
165
|
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
165
166
|
}
|
|
@@ -28,3 +28,20 @@ export declare const isModalOverlayOpen: () => boolean;
|
|
|
28
28
|
* above this overlay.
|
|
29
29
|
*/
|
|
30
30
|
export declare function useOverlayStack(inStack: boolean, modal?: boolean): (among?: 'modal') => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Calls `onEscape` for an Escape while `active` and the overlay is the
|
|
33
|
+
* topmost one, as `isTopmost` (from `useOverlayStack`) says, and marks the key
|
|
34
|
+
* used with `preventDefault()`, so nothing under it acts on it too.
|
|
35
|
+
*
|
|
36
|
+
* It listens on `window`, so everything inside the overlay has had the key
|
|
37
|
+
* first, and an Escape something there used, such as a popup closing, is
|
|
38
|
+
* left to it. The listener stays put while `onEscape` changes identity, as an
|
|
39
|
+
* inline one does on every render of the parent: it reads the latest one as
|
|
40
|
+
* the key arrives.
|
|
41
|
+
*
|
|
42
|
+
* @function useOverlayEscape
|
|
43
|
+
* @param active - Whether the overlay is open and closes on Escape.
|
|
44
|
+
* @param isTopmost - The overlay's check from `useOverlayStack`.
|
|
45
|
+
* @param onEscape - What closing it does.
|
|
46
|
+
*/
|
|
47
|
+
export declare function useOverlayEscape(active: boolean, isTopmost: () => boolean, onEscape: () => void): void;
|
|
@@ -30,6 +30,48 @@ export declare function resolvePortalContainer(container?: string | HTMLElement)
|
|
|
30
30
|
* returns the DOM node to portal into.
|
|
31
31
|
*/
|
|
32
32
|
export declare function usePortalContainer(): (container?: string | HTMLElement) => HTMLElement;
|
|
33
|
+
/**
|
|
34
|
+
* What a component that takes Modal's `portal` prop does with it now. See
|
|
35
|
+
* `usePortalProp`.
|
|
36
|
+
*/
|
|
37
|
+
export interface PortalPropState {
|
|
38
|
+
/**
|
|
39
|
+
* Whether the content renders in its portal: `portal` is set and
|
|
40
|
+
* hydration is over. Before that it renders in place.
|
|
41
|
+
*/
|
|
42
|
+
isPortaled: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The target to pass `Portal` as its `container`: the selector or element
|
|
45
|
+
* `portal` names, or `undefined` for `true`, which leaves the choice to the
|
|
46
|
+
* `portalContainer` a `ConfigProvider` sets and then `document.body`.
|
|
47
|
+
*/
|
|
48
|
+
container?: string | HTMLElement;
|
|
49
|
+
/**
|
|
50
|
+
* The `portalContainer` a `ConfigProvider` sets, while it's what places the
|
|
51
|
+
* content (portaled, with `portal` set to `true`), else `undefined`. A
|
|
52
|
+
* change to it moves the content, so effects that hold its nodes depend
|
|
53
|
+
* on it.
|
|
54
|
+
*/
|
|
55
|
+
defaultTarget?: string | HTMLElement;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Reads Modal's `portal` prop (`boolean | string | HTMLElement`) for a
|
|
59
|
+
* component that takes it: whether the content is in its portal yet, and
|
|
60
|
+
* where it goes.
|
|
61
|
+
*
|
|
62
|
+
* A portal has no server-rendered counterpart, so such a component renders in
|
|
63
|
+
* place on the server and while hydrating, and moves into its portal once the
|
|
64
|
+
* client takes over. Render the content in `Portal` with
|
|
65
|
+
* `disabled={!isPortaled}` and this `container`. Moving remounts the content,
|
|
66
|
+
* so an effect that holds one of its nodes depends on `isPortaled`, `portal`
|
|
67
|
+
* and `defaultTarget`.
|
|
68
|
+
*
|
|
69
|
+
* @function usePortalProp
|
|
70
|
+
* @param portal - The component's `portal` prop.
|
|
71
|
+
* @returns Whether it's portaled now, its `container`, and the provider's
|
|
72
|
+
* target while that places it.
|
|
73
|
+
*/
|
|
74
|
+
export declare function usePortalProp(portal?: boolean | string | HTMLElement): PortalPropState;
|
|
33
75
|
/**
|
|
34
76
|
* Props for the Portal component.
|
|
35
77
|
*/
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** `useBulmaViewport`'s answer below the tablet breakpoint. */
|
|
2
|
+
export declare const MOBILE: number;
|
|
3
|
+
/** Where Bulma's `widescreen`, `desktop` and `tablet` sit in `VIEWPORT_QUERIES`. */
|
|
4
|
+
declare const VIEWPORT_INDEX: {
|
|
5
|
+
readonly widescreen: 1;
|
|
6
|
+
readonly desktop: 2;
|
|
7
|
+
readonly tablet: 3;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Which of Bulma's viewports the window is in: the index of the widest
|
|
11
|
+
* `VIEWPORT_QUERIES` entry that matches, `MOBILE` below them all, or `-1`
|
|
12
|
+
* when that isn't known (on the server, before the first layout effect, and
|
|
13
|
+
* whenever `enabled` is off, so a caller that needs no answer subscribes to
|
|
14
|
+
* nothing). It's read in a layout effect, so the first paint after hydration
|
|
15
|
+
* already has it.
|
|
16
|
+
*
|
|
17
|
+
* @function useBulmaViewport
|
|
18
|
+
* @param enabled - Whether the caller needs the viewport at all.
|
|
19
|
+
* @returns The viewport, as described above.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useBulmaViewport(enabled: boolean): number;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the window is at or past one of Bulma's breakpoints, at its default
|
|
24
|
+
* width, through the one media query Bulma's mixin for it opens with, so a
|
|
25
|
+
* caller re-renders only when the window crosses that breakpoint. `null`
|
|
26
|
+
* until it's known: on the server, and before the first layout effect. Where
|
|
27
|
+
* `matchMedia` isn't there, it's `true`, the wide layout a server renders.
|
|
28
|
+
*
|
|
29
|
+
* @function useBulmaBreakpoint
|
|
30
|
+
* @param breakpoint - The breakpoint: `tablet`, `desktop` or `widescreen`.
|
|
31
|
+
* @returns Whether the window is at or past it, or `null`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useBulmaBreakpoint(breakpoint: keyof typeof VIEWPORT_INDEX): boolean | null;
|
|
34
|
+
export {};
|
|
@@ -26,5 +26,8 @@ import type { RefObject } from 'react';
|
|
|
26
26
|
* @param active - Whether what's beside it is inert.
|
|
27
27
|
* @param wholePage - Whether that goes for the whole page rather than only
|
|
28
28
|
* the element's siblings.
|
|
29
|
+
* @param except - Ref to an element beside it to leave reachable too, such
|
|
30
|
+
* as the overlay's own backdrop, which has to take the presses that close
|
|
31
|
+
* it rather than let them through to what's under it.
|
|
29
32
|
*/
|
|
30
|
-
export declare function useInertSiblings(ref: RefObject<Element | null>, active: boolean, wholePage: boolean): void;
|
|
33
|
+
export declare function useInertSiblings(ref: RefObject<Element | null>, active: boolean, wholePage: boolean, except?: RefObject<Element | null>): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { RefObject } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Calls `onOutsidePress` for a `pointerdown` outside `ref`'s element while
|
|
5
|
+
* `active`, as a Popover closes on one.
|
|
6
|
+
*
|
|
7
|
+
* A press counts as inside in two ways: on the element's own path in the DOM,
|
|
8
|
+
* and anywhere in a React tree the returned handler is put on, as
|
|
9
|
+
* `onPointerDownCapture`. React's tree includes content portaled out of it,
|
|
10
|
+
* which the DOM path doesn't, and React has seen a press there by the time
|
|
11
|
+
* the document hears it.
|
|
12
|
+
*
|
|
13
|
+
* The listener is on the element's own document, an iframe's when the tree
|
|
14
|
+
* renders into one, and reads the latest `onOutsidePress` as a press
|
|
15
|
+
* arrives, so it stays put while the callback changes identity.
|
|
16
|
+
*
|
|
17
|
+
* @function useOutsidePress
|
|
18
|
+
* @param ref - Ref to the element a press inside of doesn't count.
|
|
19
|
+
* @param active - Whether to listen.
|
|
20
|
+
* @param onOutsidePress - Called with each press outside.
|
|
21
|
+
* @returns The `onPointerDownCapture` handler that marks a press in its tree
|
|
22
|
+
* as inside.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useOutsidePress(ref: RefObject<Element | null>, active: boolean, onOutsidePress: (event: PointerEvent) => void): (event: React.PointerEvent) => void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
/** Options for `useOverlayFocus`. */
|
|
3
|
+
export interface OverlayFocusOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Whether the overlay is modal. One that isn't, opened while a modal
|
|
6
|
+
* overlay is open under it, leaves focus where it is: that overlay keeps
|
|
7
|
+
* Tab, so a Tab from this one's controls would only go back to it. It's
|
|
8
|
+
* read as the overlay opens, so a change while it's open moves nothing.
|
|
9
|
+
*/
|
|
10
|
+
modal: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* A value that remounts the overlay's nodes while it's open when it
|
|
13
|
+
* changes, such as a new portal target. Focus is handed back from the old
|
|
14
|
+
* nodes and moved into the new ones.
|
|
15
|
+
*/
|
|
16
|
+
remountKey?: unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Asked as the overlay closes, while it still holds focus: `false` leaves
|
|
19
|
+
* focus where it is, for a close that shouldn't send it back, such as one
|
|
20
|
+
* whose overlay stays on the page as something else.
|
|
21
|
+
*/
|
|
22
|
+
restore?: () => boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Moves focus into an overlay as it opens, to the first stop Tab wraps to
|
|
26
|
+
* inside `ref`'s element or the element itself, and hands it back to
|
|
27
|
+
* whatever had it as it closes, as `Modal` does. Focus goes back only while
|
|
28
|
+
* the overlay still holds it, or it's been dropped to `<body>`: closing one
|
|
29
|
+
* under another overlay mustn't pull focus out of the one on top, and focus
|
|
30
|
+
* moved out to the page stays there. The opener is found wherever it sits,
|
|
31
|
+
* inside a shadow root too, in the overlay's own document, an iframe's when
|
|
32
|
+
* it renders into one. It's read in a layout effect as the overlay opens, so
|
|
33
|
+
* before the commit's passive effects, such as one that makes the page
|
|
34
|
+
* inert. Content inside the overlay with a layout effect of its own, or an
|
|
35
|
+
* `autoFocus`, can still move focus first, and then there's no opener to go
|
|
36
|
+
* back to: focus is never handed back into the overlay that's closing.
|
|
37
|
+
*
|
|
38
|
+
* @function useOverlayFocus
|
|
39
|
+
* @param ref - Ref to the overlay's element.
|
|
40
|
+
* @param open - Whether the overlay is open.
|
|
41
|
+
* @param options - Whether it's modal, what remounts it, and whether a
|
|
42
|
+
* close hands focus back.
|
|
43
|
+
*/
|
|
44
|
+
export declare function useOverlayFocus(ref: RefObject<HTMLElement | null>, open: boolean, { modal, remountKey, restore }: OverlayFocusOptions): void;
|
|
@@ -33,6 +33,7 @@ export * from './components/TreeView.js';
|
|
|
33
33
|
export * from './components/ScrollArea.js';
|
|
34
34
|
export * from './components/Command.js';
|
|
35
35
|
export * from './components/DataTable.js';
|
|
36
|
+
export * from './components/Affix.js';
|
|
36
37
|
export * from './elements/Block.js';
|
|
37
38
|
export * from './elements/Box.js';
|
|
38
39
|
export * from './elements/Button.js';
|
|
@@ -150,6 +151,7 @@ export { useNumberFormat } from './helpers/localeFormat.js';
|
|
|
150
151
|
export type { NumberFormatFunction } from './helpers/localeFormat.js';
|
|
151
152
|
export { useFocusTrap } from './helpers/useFocusTrap.js';
|
|
152
153
|
export type { UseFocusTrapOptions } from './helpers/useFocusTrap.js';
|
|
154
|
+
export * from './layout/AppFrame.js';
|
|
153
155
|
export * from './layout/Center.js';
|
|
154
156
|
export * from './layout/Container.js';
|
|
155
157
|
export * from './layout/Flex.js';
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BulmaClassesProps, validColors } from '../helpers/useBulmaClasses.js';
|
|
3
|
+
import type { NavbarBurgerProps } from '../components/Navbar.js';
|
|
4
|
+
import type { ContainerBreakpoint } from './Container.js';
|
|
5
|
+
/**
|
|
6
|
+
* The strings AppFrame renders, each with an English default. A key left
|
|
7
|
+
* out, or set to an empty string, keeps its default.
|
|
8
|
+
*/
|
|
9
|
+
export interface AppFrameLabels {
|
|
10
|
+
/**
|
|
11
|
+
* The skip link's text.
|
|
12
|
+
* @defaultValue 'Skip to main content'
|
|
13
|
+
*/
|
|
14
|
+
skipLink?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The nav's accessible name, when `AppFrame.Nav` has no `aria-label` or
|
|
17
|
+
* `aria-labelledby`. Leave the word "navigation" out: the role says it.
|
|
18
|
+
* @defaultValue 'Sections'
|
|
19
|
+
*/
|
|
20
|
+
nav?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The name of the close button at the top of the drawer.
|
|
23
|
+
* @defaultValue 'Close navigation'
|
|
24
|
+
*/
|
|
25
|
+
closeNav?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The burger's name, when `AppFrame.Burger` has no `aria-label`.
|
|
28
|
+
* @defaultValue 'menu', Navbar.Burger's
|
|
29
|
+
*/
|
|
30
|
+
burger?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Props for the AppFrame component. The helper props, the other attributes
|
|
34
|
+
* and the `ref` land on the root `<div>`.
|
|
35
|
+
*
|
|
36
|
+
* @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the root `<div>`.
|
|
37
|
+
*/
|
|
38
|
+
export interface AppFrameProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
39
|
+
/**
|
|
40
|
+
* The parts, each a direct child: `Header`, `Nav`, `Main`, `Aside` and
|
|
41
|
+
* `Footer`, in that order, since it's the Tab order. The frame reads the
|
|
42
|
+
* `id` of `Main` and `Nav` off its children for the skip link and the
|
|
43
|
+
* burger, so a part wrapped in a component of your own takes no `id` of
|
|
44
|
+
* its own. Put the frame at the top of the page, outside any `main` or
|
|
45
|
+
* sectioning element.
|
|
46
|
+
*/
|
|
47
|
+
children?: React.ReactNode;
|
|
48
|
+
/**
|
|
49
|
+
* The width from which the nav is a column beside the page. Below it, the
|
|
50
|
+
* nav is a drawer the burger opens. The stylesheet switches the layout at
|
|
51
|
+
* Bulma's breakpoint, and the script reads the width at Bulma's default
|
|
52
|
+
* breakpoints, so a theme that moves Bulma's breakpoints moves the layout
|
|
53
|
+
* but not which state the burger toggles.
|
|
54
|
+
* @defaultValue 'desktop'
|
|
55
|
+
*/
|
|
56
|
+
breakpoint?: ContainerBreakpoint;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the drawer is open, below the breakpoint. With it, the state is
|
|
59
|
+
* yours: update it from `onNavOpenChange`. It has no effect from the
|
|
60
|
+
* breakpoint up, where the frame asks to close it.
|
|
61
|
+
*/
|
|
62
|
+
navOpen?: boolean;
|
|
63
|
+
/** Whether the drawer starts open. */
|
|
64
|
+
defaultNavOpen?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Called with the state asked for: by the burger, `useAppFrame()`, Escape,
|
|
67
|
+
* a press outside, the close button, a followed link (see
|
|
68
|
+
* `AppFrame.Nav`'s `closeOnNavigate`), or the window being at or past the
|
|
69
|
+
* breakpoint while it's open, which closes it.
|
|
70
|
+
*/
|
|
71
|
+
onNavOpenChange?: (open: boolean) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Whether the column is collapsed out of view, from the breakpoint up.
|
|
74
|
+
* With it, the state is yours: update it from `onNavCollapsedChange`. It
|
|
75
|
+
* has no effect below the breakpoint, where the drawer always shows the
|
|
76
|
+
* whole nav.
|
|
77
|
+
*/
|
|
78
|
+
navCollapsed?: boolean;
|
|
79
|
+
/** Whether the column starts collapsed. */
|
|
80
|
+
defaultNavCollapsed?: boolean;
|
|
81
|
+
/** Called with the state asked for, by the burger or `useAppFrame()`. */
|
|
82
|
+
onNavCollapsedChange?: (collapsed: boolean) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Renders a skip link first, visible when it has focus, that moves focus
|
|
85
|
+
* to `AppFrame.Main`. Turn it off only for a page that has its own.
|
|
86
|
+
*/
|
|
87
|
+
skipLink?: boolean;
|
|
88
|
+
/** The strings the frame renders. Each one left out keeps its English default. */
|
|
89
|
+
labels?: AppFrameLabels;
|
|
90
|
+
/** Text color helper. */
|
|
91
|
+
textColor?: (typeof validColors)[number] | 'inherit' | 'current';
|
|
92
|
+
/** Background color helper. */
|
|
93
|
+
bgColor?: (typeof validColors)[number] | 'inherit' | 'current';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Props for AppFrame.Header, on its `<header>`, which sticks to the top.
|
|
97
|
+
*
|
|
98
|
+
* @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<header>`.
|
|
99
|
+
*/
|
|
100
|
+
export interface AppFrameHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
101
|
+
/**
|
|
102
|
+
* Usually a `Navbar`, not `fixed`, with `AppFrame.Burger` in its
|
|
103
|
+
* `Navbar.Brand`. A header taller than Bulma's navbar sets
|
|
104
|
+
* `--bulma-app-frame-header-height` on `:root`, so the nav column, an
|
|
105
|
+
* in-page link and focus clear it.
|
|
106
|
+
*/
|
|
107
|
+
children?: React.ReactNode;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Props for AppFrame.Nav, on its `<nav>`.
|
|
111
|
+
*
|
|
112
|
+
* @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<nav>`.
|
|
113
|
+
* @extraProp {string} [id] - The nav's id, which the burger's `aria-controls` points at. The frame makes one when there's none.
|
|
114
|
+
* @extraProp {string} [aria-label] - The nav's name. Without it or `aria-labelledby`, it's the frame's `labels.nav`, "Sections".
|
|
115
|
+
*/
|
|
116
|
+
export interface AppFrameNavProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
117
|
+
/**
|
|
118
|
+
* Usually a `Menu`. A nav taller than the window scrolls on its own. With
|
|
119
|
+
* `overlay`, the rest of the frame is inert while the drawer is open, so an
|
|
120
|
+
* overlay opened from the drawer, such as a `Modal`, goes inside the nav or
|
|
121
|
+
* renders outside the frame (Modal's `portal`).
|
|
122
|
+
*/
|
|
123
|
+
children?: React.ReactNode;
|
|
124
|
+
/**
|
|
125
|
+
* The column's width, and the drawer's up to the window's width less a
|
|
126
|
+
* strip of backdrop. A number is pixels, and a string any CSS length. It
|
|
127
|
+
* writes `--bulma-app-frame-nav-width`, 16rem by default.
|
|
128
|
+
*/
|
|
129
|
+
width?: number | string;
|
|
130
|
+
/**
|
|
131
|
+
* Shows a backdrop behind the drawer and makes it modal: Tab stays inside,
|
|
132
|
+
* the page scroll locks, and the rest of the frame is inert, while what
|
|
133
|
+
* renders outside it, such as a Modal opened from the drawer or a toast,
|
|
134
|
+
* stays live. Without it the drawer covers the page without holding Tab,
|
|
135
|
+
* and focus can move to the page beside it. Either way focus moves in as
|
|
136
|
+
* it opens. Sidebar's `overlay`.
|
|
137
|
+
*/
|
|
138
|
+
overlay?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Closes the drawer on Escape, while it's the topmost overlay, so Escape
|
|
141
|
+
* closes one layer at a time.
|
|
142
|
+
*/
|
|
143
|
+
closeOnEscape?: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Closes the drawer on a press outside it while it's the topmost overlay:
|
|
146
|
+
* a click on the backdrop, or without `overlay` a press anywhere outside
|
|
147
|
+
* it. Focus goes back to the burger as it closes. Without `overlay`, the
|
|
148
|
+
* press's own mousedown then moves it, as on any page: to what was pressed
|
|
149
|
+
* if that takes focus, and otherwise off the burger to the page. A touch
|
|
150
|
+
* that scrolls leaves it on the burger. A press on a control whose
|
|
151
|
+
* `aria-controls` names the nav, such as the burger, is left to that
|
|
152
|
+
* control. Popover's `closeOnClickOutside`.
|
|
153
|
+
*/
|
|
154
|
+
closeOnClickOutside?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Closes the drawer when a link inside it is followed with a plain click:
|
|
157
|
+
* not with a modifier key held, and not on a link with a `target` other
|
|
158
|
+
* than `_self` or a `download`. Only a link the nav holds in the page
|
|
159
|
+
* counts, not one in a Modal or a popup it portals elsewhere. Focus goes
|
|
160
|
+
* back to the burger, and the navigation takes it from there: a router
|
|
161
|
+
* that moves focus on navigation moves it, and an in-page `#` link leaves
|
|
162
|
+
* it at its target, as it does anywhere on the page. Turn it off for a
|
|
163
|
+
* drawer of filters rather than links.
|
|
164
|
+
*/
|
|
165
|
+
closeOnNavigate?: boolean;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Props for AppFrame.Main, on the page's one `<main>`. Don't put another
|
|
169
|
+
* `<main>` inside it. It takes your `id`, or a generated one, for the skip
|
|
170
|
+
* link, and `tabIndex={-1}` so the link can move focus to it.
|
|
171
|
+
*
|
|
172
|
+
* @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<main>`.
|
|
173
|
+
* @extraProp {string} [id] - Its id, which the skip link points at. The frame makes one when there's none.
|
|
174
|
+
*/
|
|
175
|
+
export interface AppFrameMainProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
176
|
+
/** The page. A `Container` or a `Section` inside it gives Bulma's widths and padding. */
|
|
177
|
+
children?: React.ReactNode;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Props for AppFrame.Aside, on its `<aside>`, a complementary landmark.
|
|
181
|
+
* Give it an `aria-label` when the page has more than one.
|
|
182
|
+
*
|
|
183
|
+
* @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<aside>`.
|
|
184
|
+
*/
|
|
185
|
+
export interface AppFrameAsideProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
186
|
+
/** What sits beside the page: an outline, details, filters. */
|
|
187
|
+
children?: React.ReactNode;
|
|
188
|
+
/**
|
|
189
|
+
* Its width from the breakpoint up; below it, the aside moves under the
|
|
190
|
+
* page. A number is pixels, and a string any CSS length. It writes
|
|
191
|
+
* `--bulma-app-frame-aside-width`, 20rem by default.
|
|
192
|
+
*/
|
|
193
|
+
width?: number | string;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Props for AppFrame.Footer, on its `<footer>`.
|
|
197
|
+
*
|
|
198
|
+
* @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<footer>`.
|
|
199
|
+
*/
|
|
200
|
+
export interface AppFrameFooterProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>, Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
|
|
201
|
+
/** Plain content, or bestax's `Footer` with `as="div"` for Bulma's footer look. */
|
|
202
|
+
children?: React.ReactNode;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Props for AppFrame.Burger: Navbar.Burger's, less the state the frame
|
|
206
|
+
* fills in. Its `<button>` opens and closes the drawer below the breakpoint
|
|
207
|
+
* and collapses and expands the column from it up, with `aria-expanded` and
|
|
208
|
+
* `aria-controls` following the nav. It shows at every width. Hide it from
|
|
209
|
+
* the breakpoint up with the visibility helpers when the column never
|
|
210
|
+
* collapses. Outside an AppFrame it controls nothing.
|
|
211
|
+
*/
|
|
212
|
+
export type AppFrameBurgerProps = Omit<NavbarBurgerProps, 'active' | 'aria-expanded' | 'aria-controls'>;
|
|
213
|
+
/** What `useAppFrame()` returns. */
|
|
214
|
+
export interface AppFrameState {
|
|
215
|
+
/** Whether the drawer is open. */
|
|
216
|
+
navOpen: boolean;
|
|
217
|
+
/** Asks to open or close the drawer, through `onNavOpenChange`. */
|
|
218
|
+
setNavOpen: (open: boolean) => void;
|
|
219
|
+
/** Whether the column is collapsed. */
|
|
220
|
+
navCollapsed: boolean;
|
|
221
|
+
/** Asks to collapse or expand the column, through `onNavCollapsedChange`. */
|
|
222
|
+
setNavCollapsed: (collapsed: boolean) => void;
|
|
223
|
+
/**
|
|
224
|
+
* Whether the nav is a drawer at the current width. `false` on the server
|
|
225
|
+
* and until the first layout effect has read the width.
|
|
226
|
+
*/
|
|
227
|
+
isDrawer: boolean;
|
|
228
|
+
/** Toggles whichever state applies at the current width, as the burger does. */
|
|
229
|
+
toggleNav: () => void;
|
|
230
|
+
/** The nav's `id`, for a custom toggle's `aria-controls`. */
|
|
231
|
+
navId: string;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The top bar of an AppFrame, which sticks while the page scrolls.
|
|
235
|
+
*
|
|
236
|
+
* @function
|
|
237
|
+
* @param {AppFrameHeaderProps} props - Props for AppFrame.Header.
|
|
238
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<header>`.
|
|
239
|
+
* @returns {JSX.Element} The rendered header.
|
|
240
|
+
*/
|
|
241
|
+
export declare const AppFrameHeader: React.ForwardRefExoticComponent<AppFrameHeaderProps & React.RefAttributes<HTMLElement>>;
|
|
242
|
+
/**
|
|
243
|
+
* The navigation of an AppFrame: a column beside the page from the breakpoint up, and a drawer below it.
|
|
244
|
+
*
|
|
245
|
+
* @function
|
|
246
|
+
* @param {AppFrameNavProps} props - Props for AppFrame.Nav.
|
|
247
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<nav>`.
|
|
248
|
+
* @returns {JSX.Element} The rendered nav, and the drawer's backdrop.
|
|
249
|
+
*/
|
|
250
|
+
export declare const AppFrameNav: React.ForwardRefExoticComponent<AppFrameNavProps & React.RefAttributes<HTMLElement>>;
|
|
251
|
+
/**
|
|
252
|
+
* The page's main content in an AppFrame, which the skip link moves focus to.
|
|
253
|
+
*
|
|
254
|
+
* @function
|
|
255
|
+
* @param {AppFrameMainProps} props - Props for AppFrame.Main.
|
|
256
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<main>`.
|
|
257
|
+
* @returns {JSX.Element} The rendered main.
|
|
258
|
+
*/
|
|
259
|
+
export declare const AppFrameMain: React.ForwardRefExoticComponent<AppFrameMainProps & React.RefAttributes<HTMLElement>>;
|
|
260
|
+
/**
|
|
261
|
+
* Content beside the page in an AppFrame, which moves under it below the breakpoint.
|
|
262
|
+
*
|
|
263
|
+
* @function
|
|
264
|
+
* @param {AppFrameAsideProps} props - Props for AppFrame.Aside.
|
|
265
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<aside>`.
|
|
266
|
+
* @returns {JSX.Element} The rendered aside.
|
|
267
|
+
*/
|
|
268
|
+
export declare const AppFrameAside: React.ForwardRefExoticComponent<AppFrameAsideProps & React.RefAttributes<HTMLElement>>;
|
|
269
|
+
/**
|
|
270
|
+
* The bottom of an AppFrame, under the page.
|
|
271
|
+
*
|
|
272
|
+
* @function
|
|
273
|
+
* @param {AppFrameFooterProps} props - Props for AppFrame.Footer.
|
|
274
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<footer>`.
|
|
275
|
+
* @returns {JSX.Element} The rendered footer.
|
|
276
|
+
*/
|
|
277
|
+
export declare const AppFrameFooter: React.ForwardRefExoticComponent<AppFrameFooterProps & React.RefAttributes<HTMLElement>>;
|
|
278
|
+
/**
|
|
279
|
+
* The button that opens the drawer, or collapses the column from the breakpoint up: Navbar.Burger, wired to the frame.
|
|
280
|
+
*
|
|
281
|
+
* @function
|
|
282
|
+
* @param {AppFrameBurgerProps} props - Props for AppFrame.Burger.
|
|
283
|
+
* @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref to the `<button>`.
|
|
284
|
+
* @returns {JSX.Element} The rendered burger.
|
|
285
|
+
*/
|
|
286
|
+
export declare const AppFrameBurger: React.ForwardRefExoticComponent<AppFrameBurgerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
287
|
+
/**
|
|
288
|
+
* The frame's state, for a control of your own inside it: whether the
|
|
289
|
+
* drawer is open and the column collapsed, setters that go through the
|
|
290
|
+
* frame's callbacks, and `toggleNav()`, which does what the burger does.
|
|
291
|
+
* Outside an AppFrame it's `null`, as the library's other context hooks
|
|
292
|
+
* are, so a control that can sit outside one renders a fallback or nothing.
|
|
293
|
+
*
|
|
294
|
+
* @function useAppFrame
|
|
295
|
+
* @returns The frame's state, or `null` outside an AppFrame.
|
|
296
|
+
*/
|
|
297
|
+
export declare function useAppFrame(): AppFrameState | null;
|
|
298
|
+
export declare const AppFrame: React.ForwardRefExoticComponent<AppFrameProps & React.RefAttributes<HTMLDivElement>> & {
|
|
299
|
+
Header: React.ForwardRefExoticComponent<AppFrameHeaderProps & React.RefAttributes<HTMLElement>>;
|
|
300
|
+
Nav: React.ForwardRefExoticComponent<AppFrameNavProps & React.RefAttributes<HTMLElement>>;
|
|
301
|
+
Main: React.ForwardRefExoticComponent<AppFrameMainProps & React.RefAttributes<HTMLElement>>;
|
|
302
|
+
Aside: React.ForwardRefExoticComponent<AppFrameAsideProps & React.RefAttributes<HTMLElement>>;
|
|
303
|
+
Footer: React.ForwardRefExoticComponent<AppFrameFooterProps & React.RefAttributes<HTMLElement>>;
|
|
304
|
+
Burger: React.ForwardRefExoticComponent<AppFrameBurgerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
305
|
+
};
|
|
306
|
+
export default AppFrame;
|