@allxsmith/bestax-bulma 5.33.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/esm/form/Autocomplete.js +59 -14
- package/dist/esm/form/Autocomplete.js.map +1 -1
- package/dist/esm/helpers/statusRegion.js +19 -12
- package/dist/esm/helpers/statusRegion.js.map +1 -1
- package/dist/index.cjs +77 -26
- package/dist/index.cjs.map +1 -1
- package/dist/types/form/Autocomplete.d.ts +18 -3
- package/dist/types/helpers/statusRegion.d.ts +4 -4
- package/dist/types-cjs/form/Autocomplete.d.ts +18 -3
- package/dist/types-cjs/helpers/statusRegion.d.ts +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9185,7 +9185,8 @@ const visuallyHidden = {
|
|
|
9185
9185
|
border: 0,
|
|
9186
9186
|
};
|
|
9187
9187
|
/**
|
|
9188
|
-
* 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
|
|
9189
9190
|
* announcement is written `announceDelay` after it appears, or after the last
|
|
9190
9191
|
* item to join it when several appear in quick succession, alongside whatever
|
|
9191
9192
|
* the region still says. Once written, it stays for `announcementLifetime`,
|
|
@@ -9220,8 +9221,8 @@ function useAnnouncements(items, describe) {
|
|
|
9220
9221
|
React.useEffect(() => {
|
|
9221
9222
|
const announced = onScreenIdsRef.current;
|
|
9222
9223
|
const appeared = items.filter(item => !announced.has(item.id));
|
|
9223
|
-
// Items that have gone drop out here too
|
|
9224
|
-
//
|
|
9224
|
+
// Items that have gone drop out here too, so one that comes back has
|
|
9225
|
+
// appeared again.
|
|
9225
9226
|
onScreenIdsRef.current = new Set(items.map(item => item.id));
|
|
9226
9227
|
const next = [];
|
|
9227
9228
|
for (const item of appeared) {
|
|
@@ -9234,9 +9235,13 @@ function useAnnouncements(items, describe) {
|
|
|
9234
9235
|
setAnnouncements(current => ({
|
|
9235
9236
|
...current,
|
|
9236
9237
|
key: current.key + 1,
|
|
9237
|
-
// Announcements still waiting go out with the new ones
|
|
9238
|
-
//
|
|
9239
|
-
|
|
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
|
+
],
|
|
9240
9245
|
}));
|
|
9241
9246
|
}
|
|
9242
9247
|
}, [items, describe]);
|
|
@@ -9395,10 +9400,10 @@ function readAloud(element) {
|
|
|
9395
9400
|
return name || blockLevelTags.has(element.tagName) ? ` ${text} ` : text;
|
|
9396
9401
|
}
|
|
9397
9402
|
/**
|
|
9398
|
-
* A visually hidden, polite `status` live region that a
|
|
9399
|
-
*
|
|
9400
|
-
*
|
|
9401
|
-
*
|
|
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.
|
|
9402
9407
|
*
|
|
9403
9408
|
* The announcements are this component's own state, so writing and clearing
|
|
9404
9409
|
* them re-renders the region and leaves the container's items alone. Each one
|
|
@@ -9415,8 +9420,10 @@ function StatusRegion({ items, describe, }) {
|
|
|
9415
9420
|
// reads the whole region on every change. Each announcement is its own
|
|
9416
9421
|
// node, and only additions are relevant by default, so with atomic off a
|
|
9417
9422
|
// screen reader reads just what was written, and removing one announcement
|
|
9418
|
-
// doesn't read out the ones still there.
|
|
9419
|
-
|
|
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}`))) }));
|
|
9420
9427
|
}
|
|
9421
9428
|
|
|
9422
9429
|
/**
|
|
@@ -12492,6 +12499,10 @@ const Rate = React.forwardRef(({ label, labelSize, labelProps, horizontal, messa
|
|
|
12492
12499
|
});
|
|
12493
12500
|
Rate.displayName = 'Rate';
|
|
12494
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 = [];
|
|
12495
12506
|
/**
|
|
12496
12507
|
* The `Autocomplete` component provides an input field with dropdown suggestions that filter based on user input.
|
|
12497
12508
|
*
|
|
@@ -12536,6 +12547,13 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12536
12547
|
const dropdownRef = React.useRef(null);
|
|
12537
12548
|
const [internalValue, setInternalValue] = React.useState('');
|
|
12538
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);
|
|
12539
12557
|
const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
|
|
12540
12558
|
const listboxId = `${React.useId()}-listbox`;
|
|
12541
12559
|
// Use controlled or internal value
|
|
@@ -12554,6 +12572,11 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12554
12572
|
const displayValue = getDisplayValue(item);
|
|
12555
12573
|
return displayValue.toLowerCase().includes(inputValue.toLowerCase());
|
|
12556
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;
|
|
12557
12580
|
// Handle input change
|
|
12558
12581
|
const handleInputChange = React.useCallback((e) => {
|
|
12559
12582
|
const newValue = e.target.value;
|
|
@@ -12561,6 +12584,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12561
12584
|
setInternalValue(newValue);
|
|
12562
12585
|
}
|
|
12563
12586
|
onInput?.(newValue);
|
|
12587
|
+
setTyped(true);
|
|
12564
12588
|
if (!isActive && newValue) {
|
|
12565
12589
|
setIsActive(true);
|
|
12566
12590
|
}
|
|
@@ -12711,7 +12735,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12711
12735
|
});
|
|
12712
12736
|
const controlClasses = usePrefixedClassNames('control', 'is-expanded', 'has-icons-right');
|
|
12713
12737
|
const dropdownMenuClasses = usePrefixedClassNames('dropdown-menu', {
|
|
12714
|
-
'is-active':
|
|
12738
|
+
'is-active': menuOpen,
|
|
12715
12739
|
});
|
|
12716
12740
|
const dropdownContentClass = usePrefixedClassNames('dropdown-content');
|
|
12717
12741
|
const dropdownHeaderClass = usePrefixedClassNames('dropdown-header');
|
|
@@ -12733,24 +12757,51 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12733
12757
|
// The listbox takes its name from the label that names the combobox, the
|
|
12734
12758
|
// one this renders or a surrounding Field's, so the two say the same
|
|
12735
12759
|
// thing. When no label names the combobox, or the one that does has no
|
|
12736
|
-
// id to point at, the list keeps a fallback name.
|
|
12737
|
-
|
|
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.
|
|
12738
12763
|
const activeOptionId = listOpen &&
|
|
12739
12764
|
highlightedIndex >= 0 &&
|
|
12740
12765
|
highlightedIndex < filteredData.length
|
|
12741
12766
|
? `${listboxId}-${highlightedIndex}`
|
|
12742
12767
|
: undefined;
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
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 })] }));
|
|
12754
12805
|
if (!insideField) {
|
|
12755
12806
|
return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [autocompleteElement, messageEl] }));
|
|
12756
12807
|
}
|