@allxsmith/bestax-bulma 5.32.0 → 5.33.1
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/Avatars.js +4 -21
- package/dist/esm/components/Avatars.js.map +1 -1
- package/dist/esm/components/Collapses.js +1 -20
- package/dist/esm/components/Collapses.js.map +1 -1
- package/dist/esm/elements/Kbd.js +86 -0
- package/dist/esm/elements/Kbd.js.map +1 -0
- package/dist/esm/form/Autocomplete.js +59 -14
- package/dist/esm/form/Autocomplete.js.map +1 -1
- package/dist/esm/helpers/flattenChildren.js +35 -0
- package/dist/esm/helpers/flattenChildren.js.map +1 -0
- package/dist/esm/helpers/statusRegion.js +19 -12
- package/dist/esm/helpers/statusRegion.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +254 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/elements/Kbd.d.ts +100 -0
- package/dist/types/form/Autocomplete.d.ts +18 -3
- package/dist/types/helpers/flattenChildren.d.ts +16 -0
- package/dist/types/helpers/statusRegion.d.ts +4 -4
- package/dist/types/index.d.ts +1 -0
- package/dist/types-cjs/elements/Kbd.d.ts +100 -0
- package/dist/types-cjs/form/Autocomplete.d.ts +18 -3
- package/dist/types-cjs/helpers/flattenChildren.d.ts +16 -0
- package/dist/types-cjs/helpers/statusRegion.d.ts +4 -4
- package/dist/types-cjs/index.d.ts +1 -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/elements/_index.scss +1 -0
- package/src/scss/elements/_kbd.scss +86 -0
package/dist/index.cjs
CHANGED
|
@@ -1762,26 +1762,36 @@ const Avatar = React.forwardRef(function Avatar(avatarProps, ref) {
|
|
|
1762
1762
|
});
|
|
1763
1763
|
Avatar.displayName = 'Avatar';
|
|
1764
1764
|
|
|
1765
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
1765
1766
|
/**
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
1768
|
-
*
|
|
1769
|
-
*
|
|
1770
|
-
*
|
|
1767
|
+
* A component's children as a flat list, with each Fragment opened up
|
|
1768
|
+
* (recursively) so the items inside one count one by one rather than as the
|
|
1769
|
+
* Fragment. `Children.toArray` already drops `null`, `undefined` and
|
|
1770
|
+
* booleans and flattens arrays, but it keeps a Fragment whole, so an empty
|
|
1771
|
+
* one would still count as an item and two items in one would count as one.
|
|
1772
|
+
*
|
|
1773
|
+
* `Children.toArray` keys restart at ".0" inside each Fragment, so the items
|
|
1774
|
+
* in one are re-keyed with the Fragment's own key as a prefix. Otherwise an
|
|
1775
|
+
* item in a Fragment and a sibling outside it could share a key.
|
|
1776
|
+
*
|
|
1777
|
+
* Text and numbers pass through as they are, so a caller that wants only
|
|
1778
|
+
* elements filters with `React.isValidElement`.
|
|
1771
1779
|
*/
|
|
1772
|
-
function flattenChildren
|
|
1780
|
+
function flattenChildren(children, keyPrefix = '') {
|
|
1773
1781
|
return React.Children.toArray(children).flatMap(child => {
|
|
1774
1782
|
if (!React.isValidElement(child))
|
|
1775
|
-
return [];
|
|
1783
|
+
return [child];
|
|
1776
1784
|
if (child.type === React.Fragment) {
|
|
1777
|
-
return flattenChildren
|
|
1785
|
+
return flattenChildren(child.props.children, keyPrefix + child.key);
|
|
1778
1786
|
}
|
|
1779
|
-
const el = child;
|
|
1780
1787
|
return [
|
|
1781
|
-
keyPrefix
|
|
1788
|
+
keyPrefix
|
|
1789
|
+
? React.cloneElement(child, { key: keyPrefix + child.key })
|
|
1790
|
+
: child,
|
|
1782
1791
|
];
|
|
1783
1792
|
});
|
|
1784
1793
|
}
|
|
1794
|
+
|
|
1785
1795
|
/**
|
|
1786
1796
|
* The `Avatars` component renders an overlapping/stacked group of `Avatar`s, the "members" list pattern.
|
|
1787
1797
|
*
|
|
@@ -1808,7 +1818,9 @@ const AvatarsComponent = ({ className, max, size, shape, spacing = 'md', spaced
|
|
|
1808
1818
|
: undefined;
|
|
1809
1819
|
const surplusClass = usePrefixedClassNames('is-surplus');
|
|
1810
1820
|
const combinedClasses = classNames(avatarsClasses, bulmaHelperClasses, className);
|
|
1811
|
-
|
|
1821
|
+
// Fragments are opened up, so the avatars inside one count toward `max` and
|
|
1822
|
+
// take the group's size and shape one by one. Text between them is dropped.
|
|
1823
|
+
const childArray = flattenChildren(children).filter((child) => React.isValidElement(child));
|
|
1812
1824
|
const maxCount = typeof max === 'number' && Number.isInteger(max) && max >= 0
|
|
1813
1825
|
? max
|
|
1814
1826
|
: undefined;
|
|
@@ -5295,26 +5307,6 @@ function toIndexes(value) {
|
|
|
5295
5307
|
return value;
|
|
5296
5308
|
return typeof value === 'number' ? [value] : [];
|
|
5297
5309
|
}
|
|
5298
|
-
/**
|
|
5299
|
-
* Flattens Fragment children (recursively) so each item inside one gets its
|
|
5300
|
-
* own index rather than sharing the fragment's. Children.toArray keys restart
|
|
5301
|
-
* at ".0" inside each fragment, so those children are re-keyed with the
|
|
5302
|
-
* fragment's own key as a prefix, the way Avatars does it.
|
|
5303
|
-
*/
|
|
5304
|
-
function flattenChildren(children, keyPrefix = '') {
|
|
5305
|
-
return React.Children.toArray(children).flatMap(child => {
|
|
5306
|
-
if (!React.isValidElement(child))
|
|
5307
|
-
return [child];
|
|
5308
|
-
if (child.type === React.Fragment) {
|
|
5309
|
-
return flattenChildren(child.props.children, keyPrefix + child.key);
|
|
5310
|
-
}
|
|
5311
|
-
return [
|
|
5312
|
-
keyPrefix
|
|
5313
|
-
? React.cloneElement(child, { key: keyPrefix + child.key })
|
|
5314
|
-
: child,
|
|
5315
|
-
];
|
|
5316
|
-
});
|
|
5317
|
-
}
|
|
5318
5310
|
/**
|
|
5319
5311
|
* The `Collapses` component groups `Collapse` items into an accordion that keeps one item open at a time, or any number with `multiple`.
|
|
5320
5312
|
*
|
|
@@ -9151,83 +9143,6 @@ const Image = ({ as, className, textColor, color, bgColor, size, isRounded, isRe
|
|
|
9151
9143
|
return (jsxRuntime.jsx(Tag, { className: imageClasses, ...rest, children: content }));
|
|
9152
9144
|
};
|
|
9153
9145
|
|
|
9154
|
-
/**
|
|
9155
|
-
* The `Link` component renders a styled anchor (`<a>`) element with Bulma helper class integration.
|
|
9156
|
-
*
|
|
9157
|
-
* @function
|
|
9158
|
-
* @param {LinkProps} props - Props for the Link component.
|
|
9159
|
-
* @param {React.Ref} ref - Forwarded ref to the element `as` renders.
|
|
9160
|
-
* @returns {JSX.Element} The rendered anchor, or whatever `as` names.
|
|
9161
|
-
* @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
|
|
9162
|
-
*/
|
|
9163
|
-
const Link = React.forwardRef(function Link(linkProps, ref) {
|
|
9164
|
-
const { className, textColor, bgColor, isActive, as: Component = 'a', children, ...props } = linkProps;
|
|
9165
|
-
/**
|
|
9166
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
9167
|
-
*/
|
|
9168
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9169
|
-
color: textColor,
|
|
9170
|
-
backgroundColor: bgColor,
|
|
9171
|
-
...props,
|
|
9172
|
-
});
|
|
9173
|
-
const bulmaClasses = usePrefixedClassNames({
|
|
9174
|
-
'is-active': isActive,
|
|
9175
|
-
});
|
|
9176
|
-
const linkClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9177
|
-
return (jsxRuntime.jsx(Component, { ref: ref, className: linkClasses || undefined, ...rest, ...(Component === 'button' ? { type: buttonType(rest.type) } : {}), children: children }));
|
|
9178
|
-
});
|
|
9179
|
-
Link.displayName = 'Link';
|
|
9180
|
-
|
|
9181
|
-
/**
|
|
9182
|
-
* The `ListItem` component renders a styled list item (`<li>`) element with Bulma helper class integration.
|
|
9183
|
-
*
|
|
9184
|
-
* @function
|
|
9185
|
-
* @param {ListItemProps} props - Props for the ListItem component.
|
|
9186
|
-
* @returns {JSX.Element} The rendered li element.
|
|
9187
|
-
*/
|
|
9188
|
-
const ListItem = ({ className, textColor, bgColor, children, ...props }) => {
|
|
9189
|
-
/**
|
|
9190
|
-
* Generates Bulma helper classes and separates out remaining props.
|
|
9191
|
-
*/
|
|
9192
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9193
|
-
color: textColor,
|
|
9194
|
-
backgroundColor: bgColor,
|
|
9195
|
-
...props,
|
|
9196
|
-
});
|
|
9197
|
-
const bulmaClasses = usePrefixedClassNames();
|
|
9198
|
-
const itemClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9199
|
-
return (jsxRuntime.jsx("li", { className: itemClasses || undefined, ...rest, children: children }));
|
|
9200
|
-
};
|
|
9201
|
-
|
|
9202
|
-
/**
|
|
9203
|
-
* The `Loader` component renders Bulma's `.loader`, a small spinning ring for inline loading states.
|
|
9204
|
-
*
|
|
9205
|
-
* The ring is `1em` square, so `textSize` scales it. It is exposed as an
|
|
9206
|
-
* indeterminate `progressbar` named by `ariaLabel`, and it stops spinning
|
|
9207
|
-
* under `prefers-reduced-motion: reduce` while staying drawn.
|
|
9208
|
-
*
|
|
9209
|
-
* @function
|
|
9210
|
-
* @param {LoaderProps} props - Props for the Loader component.
|
|
9211
|
-
* @param {React.Ref<HTMLSpanElement>} ref - Forwarded ref to the loader element.
|
|
9212
|
-
* @returns {JSX.Element} The rendered loader element.
|
|
9213
|
-
*
|
|
9214
|
-
* @example
|
|
9215
|
-
* <Loader />
|
|
9216
|
-
*
|
|
9217
|
-
* @example
|
|
9218
|
-
* <Loader textSize="3" ariaLabel="Saving" />
|
|
9219
|
-
*/
|
|
9220
|
-
const Loader = React.forwardRef(({ className, ariaLabel = 'Loading', ...props }, ref) => {
|
|
9221
|
-
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
9222
|
-
const loaderClass = usePrefixedClassNames('loader');
|
|
9223
|
-
// role="progressbar", not role="status": a status region mounted together
|
|
9224
|
-
// with its text is not reliably announced, so the loader claims only what
|
|
9225
|
-
// it can deliver, a named indicator found by reading the page. No
|
|
9226
|
-
// aria-valuenow, because the progress is indeterminate.
|
|
9227
|
-
return (jsxRuntime.jsx("span", { ref: ref, className: classNames(loaderClass, bulmaHelperClasses, className), role: "progressbar", "aria-label": ariaLabel, ...rest }));
|
|
9228
|
-
});
|
|
9229
|
-
Loader.displayName = 'Loader';
|
|
9230
|
-
|
|
9231
9146
|
/**
|
|
9232
9147
|
* How long after an item appears its announcement is written, in ms. The wait
|
|
9233
9148
|
* lets a screen reader register a region the container has only just mounted,
|
|
@@ -9270,7 +9185,8 @@ const visuallyHidden = {
|
|
|
9270
9185
|
border: 0,
|
|
9271
9186
|
};
|
|
9272
9187
|
/**
|
|
9273
|
-
* Works out what a status region says. Each item is announced once
|
|
9188
|
+
* Works out what a status region says. Each item is announced once each time
|
|
9189
|
+
* it appears, so one that goes and comes back is announced again. Its
|
|
9274
9190
|
* announcement is written `announceDelay` after it appears, or after the last
|
|
9275
9191
|
* item to join it when several appear in quick succession, alongside whatever
|
|
9276
9192
|
* the region still says. Once written, it stays for `announcementLifetime`,
|
|
@@ -9305,8 +9221,8 @@ function useAnnouncements(items, describe) {
|
|
|
9305
9221
|
React.useEffect(() => {
|
|
9306
9222
|
const announced = onScreenIdsRef.current;
|
|
9307
9223
|
const appeared = items.filter(item => !announced.has(item.id));
|
|
9308
|
-
// Items that have gone drop out here too
|
|
9309
|
-
//
|
|
9224
|
+
// Items that have gone drop out here too, so one that comes back has
|
|
9225
|
+
// appeared again.
|
|
9310
9226
|
onScreenIdsRef.current = new Set(items.map(item => item.id));
|
|
9311
9227
|
const next = [];
|
|
9312
9228
|
for (const item of appeared) {
|
|
@@ -9319,9 +9235,13 @@ function useAnnouncements(items, describe) {
|
|
|
9319
9235
|
setAnnouncements(current => ({
|
|
9320
9236
|
...current,
|
|
9321
9237
|
key: current.key + 1,
|
|
9322
|
-
// Announcements still waiting go out with the new ones
|
|
9323
|
-
//
|
|
9324
|
-
|
|
9238
|
+
// Announcements still waiting go out with the new ones, apart from
|
|
9239
|
+
// one whose item came back before it was written, which the new one
|
|
9240
|
+
// replaces. What's already written stays put.
|
|
9241
|
+
waiting: [
|
|
9242
|
+
...current.waiting.filter(waiting => !next.some(({ id }) => id === waiting.id)),
|
|
9243
|
+
...next,
|
|
9244
|
+
],
|
|
9325
9245
|
}));
|
|
9326
9246
|
}
|
|
9327
9247
|
}, [items, describe]);
|
|
@@ -9480,10 +9400,10 @@ function readAloud(element) {
|
|
|
9480
9400
|
return name || blockLevelTags.has(element.tagName) ? ` ${text} ` : text;
|
|
9481
9401
|
}
|
|
9482
9402
|
/**
|
|
9483
|
-
* A visually hidden, polite `status` live region that a
|
|
9484
|
-
*
|
|
9485
|
-
*
|
|
9486
|
-
*
|
|
9403
|
+
* A visually hidden, polite `status` live region that a container announces
|
|
9404
|
+
* its items through. The container renders it from the moment it mounts,
|
|
9405
|
+
* whether or not it has anything to show, so the region is already in the
|
|
9406
|
+
* page when an announcement is written into it.
|
|
9487
9407
|
*
|
|
9488
9408
|
* The announcements are this component's own state, so writing and clearing
|
|
9489
9409
|
* them re-renders the region and leaves the container's items alone. Each one
|
|
@@ -9500,10 +9420,164 @@ function StatusRegion({ items, describe, }) {
|
|
|
9500
9420
|
// reads the whole region on every change. Each announcement is its own
|
|
9501
9421
|
// node, and only additions are relevant by default, so with atomic off a
|
|
9502
9422
|
// screen reader reads just what was written, and removing one announcement
|
|
9503
|
-
// doesn't read out the ones still there.
|
|
9504
|
-
|
|
9423
|
+
// doesn't read out the ones still there. A node is keyed by its batch as
|
|
9424
|
+
// well as its item, since an item that comes back can be written again
|
|
9425
|
+
// while what it said before is still there.
|
|
9426
|
+
return (jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "false", style: visuallyHidden, children: announcements.map(announcement => (jsxRuntime.jsx("div", { children: announcement.text }, `${announcement.batch}:${announcement.id}`))) }));
|
|
9505
9427
|
}
|
|
9506
9428
|
|
|
9429
|
+
/**
|
|
9430
|
+
* Keys pressed together or one after another, in the outer `<kbd>` HTML gives a combination, with the separator between each pair as text.
|
|
9431
|
+
*
|
|
9432
|
+
* It doesn't wrap in the middle and isn't drawn as a key itself. It sets no
|
|
9433
|
+
* font size, so the keys in it are the size of a key on its own.
|
|
9434
|
+
*
|
|
9435
|
+
* @function
|
|
9436
|
+
* @param {KbdGroupProps} props - Props for Kbd.Group.
|
|
9437
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the outer `<kbd>`.
|
|
9438
|
+
* @returns {JSX.Element} The rendered combination.
|
|
9439
|
+
*
|
|
9440
|
+
* @example
|
|
9441
|
+
* <Kbd.Group>
|
|
9442
|
+
* <Kbd>Ctrl</Kbd>
|
|
9443
|
+
* <Kbd>Shift</Kbd>
|
|
9444
|
+
* <Kbd>P</Kbd>
|
|
9445
|
+
* </Kbd.Group>
|
|
9446
|
+
*
|
|
9447
|
+
* @example
|
|
9448
|
+
* <Kbd.Group separator="then">
|
|
9449
|
+
* <Kbd>G</Kbd>
|
|
9450
|
+
* <Kbd>I</Kbd>
|
|
9451
|
+
* </Kbd.Group>
|
|
9452
|
+
*/
|
|
9453
|
+
const KbdGroup = React.forwardRef(({ className, textColor, separator = '+', children, ...props }, ref) => {
|
|
9454
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9455
|
+
color: textColor,
|
|
9456
|
+
...props,
|
|
9457
|
+
});
|
|
9458
|
+
const groupClass = usePrefixedClassNames('kbd-group');
|
|
9459
|
+
const separatorClass = usePrefixedClassNames('kbd-separator');
|
|
9460
|
+
// Null, undefined and booleans drop out, and Fragments are opened up, so
|
|
9461
|
+
// an empty Fragment leaves no separator behind and the keys in one are
|
|
9462
|
+
// separated like the rest. The spaces are part of the text, so a screen
|
|
9463
|
+
// reader reads "Ctrl plus S" rather than one run-together word, whatever
|
|
9464
|
+
// the CSS does.
|
|
9465
|
+
const keys = flattenChildren(children);
|
|
9466
|
+
return (jsxRuntime.jsx("kbd", { ref: ref, className: classNames(groupClass, bulmaHelperClasses, className), ...rest, children: keys.map((key, index) => (jsxRuntime.jsxs(React.Fragment, { children: [index > 0 && (jsxRuntime.jsx("span", { className: separatorClass, children: ` ${separator} ` })), key] }, React.isValidElement(key) && key.key != null ? key.key : index))) }));
|
|
9467
|
+
});
|
|
9468
|
+
KbdGroup.displayName = 'Kbd.Group';
|
|
9469
|
+
/**
|
|
9470
|
+
* The `Kbd` component renders a `<kbd>` drawn as a keycap, for a key the reader presses.
|
|
9471
|
+
*
|
|
9472
|
+
* It only shows the key: it doesn't listen for it. The key takes its colors
|
|
9473
|
+
* from the text it sits in, and its size in `em`, so it fits a paragraph, a
|
|
9474
|
+
* title or a filled button as it is, and `textSize` sizes it outright. For a
|
|
9475
|
+
* combination or a sequence, put the keys in a `Kbd.Group`. What tells
|
|
9476
|
+
* assistive tech a control has a shortcut is `aria-keyshortcuts` on that
|
|
9477
|
+
* control, with `Kbd` as the hint people see next to it.
|
|
9478
|
+
*
|
|
9479
|
+
* @function
|
|
9480
|
+
* @param {KbdProps} props - Props for the Kbd component.
|
|
9481
|
+
* @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<kbd>`.
|
|
9482
|
+
* @returns {JSX.Element} The rendered key.
|
|
9483
|
+
*
|
|
9484
|
+
* @example
|
|
9485
|
+
* <Kbd>Esc</Kbd>
|
|
9486
|
+
*
|
|
9487
|
+
* @example
|
|
9488
|
+
* <Kbd label="Command">⌘</Kbd>
|
|
9489
|
+
*/
|
|
9490
|
+
const KbdRoot = React.forwardRef(({ className, textColor, bgColor, label, children, ...props }, ref) => {
|
|
9491
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9492
|
+
color: textColor,
|
|
9493
|
+
backgroundColor: bgColor,
|
|
9494
|
+
...props,
|
|
9495
|
+
});
|
|
9496
|
+
const kbdClass = usePrefixedClassNames('kbd');
|
|
9497
|
+
// The name is text rather than an aria-label, which ARIA in HTML
|
|
9498
|
+
// prohibits on kbd. Its styles are inline, so it stays hidden in a
|
|
9499
|
+
// modular build that leaves out Bulma's helper classes.
|
|
9500
|
+
return (jsxRuntime.jsx("kbd", { ref: ref, className: classNames(kbdClass, bulmaHelperClasses, className), ...rest, children: label ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { "aria-hidden": "true", children: children }), jsxRuntime.jsx("span", { style: visuallyHidden, children: label })] })) : (children) }));
|
|
9501
|
+
});
|
|
9502
|
+
const Kbd = withSubComponents(KbdRoot, { Group: KbdGroup }, 'Kbd');
|
|
9503
|
+
|
|
9504
|
+
/**
|
|
9505
|
+
* The `Link` component renders a styled anchor (`<a>`) element with Bulma helper class integration.
|
|
9506
|
+
*
|
|
9507
|
+
* @function
|
|
9508
|
+
* @param {LinkProps} props - Props for the Link component.
|
|
9509
|
+
* @param {React.Ref} ref - Forwarded ref to the element `as` renders.
|
|
9510
|
+
* @returns {JSX.Element} The rendered anchor, or whatever `as` names.
|
|
9511
|
+
* @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
|
|
9512
|
+
*/
|
|
9513
|
+
const Link = React.forwardRef(function Link(linkProps, ref) {
|
|
9514
|
+
const { className, textColor, bgColor, isActive, as: Component = 'a', children, ...props } = linkProps;
|
|
9515
|
+
/**
|
|
9516
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
9517
|
+
*/
|
|
9518
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9519
|
+
color: textColor,
|
|
9520
|
+
backgroundColor: bgColor,
|
|
9521
|
+
...props,
|
|
9522
|
+
});
|
|
9523
|
+
const bulmaClasses = usePrefixedClassNames({
|
|
9524
|
+
'is-active': isActive,
|
|
9525
|
+
});
|
|
9526
|
+
const linkClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9527
|
+
return (jsxRuntime.jsx(Component, { ref: ref, className: linkClasses || undefined, ...rest, ...(Component === 'button' ? { type: buttonType(rest.type) } : {}), children: children }));
|
|
9528
|
+
});
|
|
9529
|
+
Link.displayName = 'Link';
|
|
9530
|
+
|
|
9531
|
+
/**
|
|
9532
|
+
* The `ListItem` component renders a styled list item (`<li>`) element with Bulma helper class integration.
|
|
9533
|
+
*
|
|
9534
|
+
* @function
|
|
9535
|
+
* @param {ListItemProps} props - Props for the ListItem component.
|
|
9536
|
+
* @returns {JSX.Element} The rendered li element.
|
|
9537
|
+
*/
|
|
9538
|
+
const ListItem = ({ className, textColor, bgColor, children, ...props }) => {
|
|
9539
|
+
/**
|
|
9540
|
+
* Generates Bulma helper classes and separates out remaining props.
|
|
9541
|
+
*/
|
|
9542
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
9543
|
+
color: textColor,
|
|
9544
|
+
backgroundColor: bgColor,
|
|
9545
|
+
...props,
|
|
9546
|
+
});
|
|
9547
|
+
const bulmaClasses = usePrefixedClassNames();
|
|
9548
|
+
const itemClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
|
|
9549
|
+
return (jsxRuntime.jsx("li", { className: itemClasses || undefined, ...rest, children: children }));
|
|
9550
|
+
};
|
|
9551
|
+
|
|
9552
|
+
/**
|
|
9553
|
+
* The `Loader` component renders Bulma's `.loader`, a small spinning ring for inline loading states.
|
|
9554
|
+
*
|
|
9555
|
+
* The ring is `1em` square, so `textSize` scales it. It is exposed as an
|
|
9556
|
+
* indeterminate `progressbar` named by `ariaLabel`, and it stops spinning
|
|
9557
|
+
* under `prefers-reduced-motion: reduce` while staying drawn.
|
|
9558
|
+
*
|
|
9559
|
+
* @function
|
|
9560
|
+
* @param {LoaderProps} props - Props for the Loader component.
|
|
9561
|
+
* @param {React.Ref<HTMLSpanElement>} ref - Forwarded ref to the loader element.
|
|
9562
|
+
* @returns {JSX.Element} The rendered loader element.
|
|
9563
|
+
*
|
|
9564
|
+
* @example
|
|
9565
|
+
* <Loader />
|
|
9566
|
+
*
|
|
9567
|
+
* @example
|
|
9568
|
+
* <Loader textSize="3" ariaLabel="Saving" />
|
|
9569
|
+
*/
|
|
9570
|
+
const Loader = React.forwardRef(({ className, ariaLabel = 'Loading', ...props }, ref) => {
|
|
9571
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
9572
|
+
const loaderClass = usePrefixedClassNames('loader');
|
|
9573
|
+
// role="progressbar", not role="status": a status region mounted together
|
|
9574
|
+
// with its text is not reliably announced, so the loader claims only what
|
|
9575
|
+
// it can deliver, a named indicator found by reading the page. No
|
|
9576
|
+
// aria-valuenow, because the progress is indeterminate.
|
|
9577
|
+
return (jsxRuntime.jsx("span", { ref: ref, className: classNames(loaderClass, bulmaHelperClasses, className), role: "progressbar", "aria-label": ariaLabel, ...rest }));
|
|
9578
|
+
});
|
|
9579
|
+
Loader.displayName = 'Loader';
|
|
9580
|
+
|
|
9507
9581
|
/**
|
|
9508
9582
|
* The `Notification` component is a Bulma-styled alert/message area for providing feedback, warnings, or information to users.
|
|
9509
9583
|
*
|
|
@@ -12425,6 +12499,10 @@ const Rate = React.forwardRef(({ label, labelSize, labelProps, horizontal, messa
|
|
|
12425
12499
|
});
|
|
12426
12500
|
Rate.displayName = 'Rate';
|
|
12427
12501
|
|
|
12502
|
+
// What the status region announces: the `empty` content, as the one item
|
|
12503
|
+
// it's handed while that content is on screen because of the user's typing.
|
|
12504
|
+
const typedNoMatches = [{ id: 'empty' }];
|
|
12505
|
+
const nothingToAnnounce = [];
|
|
12428
12506
|
/**
|
|
12429
12507
|
* The `Autocomplete` component provides an input field with dropdown suggestions that filter based on user input.
|
|
12430
12508
|
*
|
|
@@ -12469,6 +12547,13 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12469
12547
|
const dropdownRef = React.useRef(null);
|
|
12470
12548
|
const [internalValue, setInternalValue] = React.useState('');
|
|
12471
12549
|
const [isActive, setIsActive] = React.useState(false);
|
|
12550
|
+
// Whether the user has typed since the list opened, since only typing
|
|
12551
|
+
// gets the `empty` content announced. Closing the list resets it (during
|
|
12552
|
+
// render, React's pattern for adjusting state when other state changes),
|
|
12553
|
+
// so opening the list again without typing stays quiet.
|
|
12554
|
+
const [typed, setTyped] = React.useState(false);
|
|
12555
|
+
if (typed && !isActive)
|
|
12556
|
+
setTyped(false);
|
|
12472
12557
|
const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
|
|
12473
12558
|
const listboxId = `${React.useId()}-listbox`;
|
|
12474
12559
|
// Use controlled or internal value
|
|
@@ -12487,6 +12572,11 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12487
12572
|
const displayValue = getDisplayValue(item);
|
|
12488
12573
|
return displayValue.toLowerCase().includes(inputValue.toLowerCase());
|
|
12489
12574
|
});
|
|
12575
|
+
// The dropdown shows the options or, when nothing matches, the `empty`
|
|
12576
|
+
// content. The listbox holds only options, so it's on screen only while
|
|
12577
|
+
// there are some.
|
|
12578
|
+
const menuOpen = isActive && (filteredData.length > 0 || !!empty);
|
|
12579
|
+
const listOpen = isActive && filteredData.length > 0;
|
|
12490
12580
|
// Handle input change
|
|
12491
12581
|
const handleInputChange = React.useCallback((e) => {
|
|
12492
12582
|
const newValue = e.target.value;
|
|
@@ -12494,6 +12584,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12494
12584
|
setInternalValue(newValue);
|
|
12495
12585
|
}
|
|
12496
12586
|
onInput?.(newValue);
|
|
12587
|
+
setTyped(true);
|
|
12497
12588
|
if (!isActive && newValue) {
|
|
12498
12589
|
setIsActive(true);
|
|
12499
12590
|
}
|
|
@@ -12644,7 +12735,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12644
12735
|
});
|
|
12645
12736
|
const controlClasses = usePrefixedClassNames('control', 'is-expanded', 'has-icons-right');
|
|
12646
12737
|
const dropdownMenuClasses = usePrefixedClassNames('dropdown-menu', {
|
|
12647
|
-
'is-active':
|
|
12738
|
+
'is-active': menuOpen,
|
|
12648
12739
|
});
|
|
12649
12740
|
const dropdownContentClass = usePrefixedClassNames('dropdown-content');
|
|
12650
12741
|
const dropdownHeaderClass = usePrefixedClassNames('dropdown-header');
|
|
@@ -12666,24 +12757,51 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12666
12757
|
// The listbox takes its name from the label that names the combobox, the
|
|
12667
12758
|
// one this renders or a surrounding Field's, so the two say the same
|
|
12668
12759
|
// thing. When no label names the combobox, or the one that does has no
|
|
12669
|
-
// id to point at, the list keeps a fallback name.
|
|
12670
|
-
|
|
12760
|
+
// id to point at, the list keeps a fallback name. While the dropdown
|
|
12761
|
+
// shows only the `empty` content there's no listbox, so the combobox is
|
|
12762
|
+
// collapsed and points at nothing.
|
|
12671
12763
|
const activeOptionId = listOpen &&
|
|
12672
12764
|
highlightedIndex >= 0 &&
|
|
12673
12765
|
highlightedIndex < filteredData.length
|
|
12674
12766
|
? `${listboxId}-${highlightedIndex}`
|
|
12675
12767
|
: undefined;
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12768
|
+
// A polite status region, in the page from the start so it's there
|
|
12769
|
+
// before anything is written into it, announces the `empty` content
|
|
12770
|
+
// when the user's typing leaves nothing that matches what's in the
|
|
12771
|
+
// field. It follows what's on screen, so results that come in after the
|
|
12772
|
+
// typing count too. It's said once each time that happens, not again on
|
|
12773
|
+
// each keystroke while nothing matches. An empty field isn't a search,
|
|
12774
|
+
// so it stays quiet then, even with no data. While `loading`, or a
|
|
12775
|
+
// loading Control around it, says results are on their way, it waits,
|
|
12776
|
+
// and it's said once loading ends with nothing matching.
|
|
12777
|
+
const emptyItems = typed &&
|
|
12778
|
+
menuOpen &&
|
|
12779
|
+
!listOpen &&
|
|
12780
|
+
!!inputValue &&
|
|
12781
|
+
!loading &&
|
|
12782
|
+
!controlLoading
|
|
12783
|
+
? typedNoMatches
|
|
12784
|
+
: nothingToAnnounce;
|
|
12785
|
+
// The region reads the text on screen, whatever `empty` renders.
|
|
12786
|
+
const emptyRef = React.useRef(null);
|
|
12787
|
+
const describeEmpty = React.useCallback(() => {
|
|
12788
|
+
const node = emptyRef.current;
|
|
12789
|
+
/* istanbul ignore if: it's only announced while it's on screen */
|
|
12790
|
+
if (!node)
|
|
12791
|
+
return null;
|
|
12792
|
+
return spokenText(node);
|
|
12793
|
+
}, []);
|
|
12794
|
+
const autocompleteElement = (jsxRuntime.jsxs("div", { ref: containerRef, className: combinedClasses, ...rest, children: [jsxRuntime.jsxs("div", { className: controlClasses, children: [jsxRuntime.jsx("input", { ref: combinedRef, type: "text", className: inputClasses, id: controlId, value: inputValue, placeholder: placeholder, disabled: disabled, name: name, form: form, required: required, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, role: "combobox", "aria-expanded": listOpen, "aria-controls": listOpen ? listboxId : undefined, "aria-activedescendant": activeOptionId, "aria-haspopup": "listbox", "aria-autocomplete": "list", autoComplete: "off" }), showClear && (jsxRuntime.jsx("button", { type: "button", className: iconRightClickableClass, onClick: handleClear, "aria-label": "Clear", children: jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "16", height: "16", "aria-hidden": "true", children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })), loading && (jsxRuntime.jsx("span", { className: iconRightClass, children: jsxRuntime.jsx("span", { className: loaderClass }) }))] }), menuOpen && (jsxRuntime.jsx("div", { className: dropdownMenuClasses, children: jsxRuntime.jsxs("div", { ref: dropdownRef, className: dropdownContentClass, style: { maxHeight: `${maxHeight}px`, overflowY: 'auto' }, onScroll: handleDropdownScroll, children: [header && jsxRuntime.jsx("div", { className: dropdownHeaderClass, children: header }), listOpen ? (jsxRuntime.jsx("div", { id: listboxId, role: "listbox", "aria-labelledby": listLabelId, "aria-label": listLabelId ? undefined : 'Suggestions', children: filteredData.map((item, index) => {
|
|
12795
|
+
const isDisabled = typeof item !== 'string' && item.disabled;
|
|
12796
|
+
const isHighlighted = index === highlightedIndex;
|
|
12797
|
+
const itemClasses = prefixedClassNames(classPrefix, 'dropdown-item', {
|
|
12798
|
+
'is-active': isHighlighted,
|
|
12799
|
+
'is-disabled': isDisabled,
|
|
12800
|
+
});
|
|
12801
|
+
return (jsxRuntime.jsx("a", { id: `${listboxId}-${index}`, "data-index": index, className: itemClasses, onClick: () => !isDisabled && handleSelect(item), onMouseEnter: () => !isDisabled && setHighlightedIndex(index), role: "option", "aria-selected": isHighlighted, "aria-disabled": isDisabled, children: itemTemplate
|
|
12802
|
+
? itemTemplate(item)
|
|
12803
|
+
: getDisplayValue(item) }, index));
|
|
12804
|
+
}) })) : (jsxRuntime.jsx("div", { ref: emptyRef, className: emptyItemClasses, children: empty })), footer && jsxRuntime.jsx("div", { className: dropdownFooterClass, children: footer })] }) })), jsxRuntime.jsx(StatusRegion, { items: emptyItems, describe: describeEmpty })] }));
|
|
12687
12805
|
if (!insideField) {
|
|
12688
12806
|
return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [autocompleteElement, messageEl] }));
|
|
12689
12807
|
}
|
|
@@ -19362,6 +19480,8 @@ exports.IconText = IconText;
|
|
|
19362
19480
|
exports.Image = Image;
|
|
19363
19481
|
exports.Input = Input;
|
|
19364
19482
|
exports.InputBase = InputBase;
|
|
19483
|
+
exports.Kbd = Kbd;
|
|
19484
|
+
exports.KbdGroup = KbdGroup;
|
|
19365
19485
|
exports.Level = Level;
|
|
19366
19486
|
exports.LevelItem = LevelItem;
|
|
19367
19487
|
exports.LevelLeft = LevelLeft;
|