@allxsmith/bestax-bulma 5.35.0 → 5.36.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 (44) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/esm/components/EmptyState.js +2 -2
  4. package/dist/esm/components/EmptyState.js.map +1 -1
  5. package/dist/esm/components/Timeline.js +150 -0
  6. package/dist/esm/components/Timeline.js.map +1 -0
  7. package/dist/esm/elements/Icon.js +2 -3
  8. package/dist/esm/elements/Icon.js.map +1 -1
  9. package/dist/esm/form/Autocomplete.js +2 -2
  10. package/dist/esm/form/Autocomplete.js.map +1 -1
  11. package/dist/esm/form/Taginput.js +64 -55
  12. package/dist/esm/form/Taginput.js.map +1 -1
  13. package/dist/esm/form/useAutoLabelId.js +3 -3
  14. package/dist/esm/form/useAutoLabelId.js.map +1 -1
  15. package/dist/extras.css +1 -1
  16. package/dist/extras.css.map +1 -1
  17. package/dist/index.cjs +896 -748
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.esm.js +1 -0
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/types/components/Timeline.d.ts +130 -0
  22. package/dist/types/elements/Icon.d.ts +6 -1
  23. package/dist/types/form/Taginput.d.ts +12 -5
  24. package/dist/types/index.d.ts +1 -0
  25. package/dist/types-cjs/components/Timeline.d.ts +130 -0
  26. package/dist/types-cjs/elements/Icon.d.ts +6 -1
  27. package/dist/types-cjs/form/Taginput.d.ts +12 -5
  28. package/dist/types-cjs/index.d.ts +1 -0
  29. package/dist/versions/bestax-no-dark-mode.css +1 -1
  30. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  31. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  32. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  33. package/dist/versions/bestax-no-helpers.css +1 -1
  34. package/dist/versions/bestax-no-helpers.css.map +1 -1
  35. package/dist/versions/bestax-prefixed.css +1 -1
  36. package/dist/versions/bestax-prefixed.css.map +1 -1
  37. package/package.json +1 -1
  38. package/src/scss/components/_index.scss +2 -0
  39. package/src/scss/components/_loading.scss +26 -0
  40. package/src/scss/components/_menu.scss +37 -0
  41. package/src/scss/components/_timeline.scss +371 -0
  42. package/src/scss/form/_checkbox.scss +14 -14
  43. package/src/scss/form/_radio.scss +12 -12
  44. package/src/scss/form/_switch.scss +21 -21
package/dist/index.cjs CHANGED
@@ -3199,8 +3199,7 @@ function getIconClasses(library, name, variant, features) {
3199
3199
  */
3200
3200
  const Icon = ({ className, textColor, bgColor, name, library, variant, features, libraryFeatures, // Deprecated but maintained for backward compatibility
3201
3201
  size, ariaLabel, style, icon, // Capture and exclude the deprecated 'icon' prop from DOM
3202
- color: _color, // Exclude 'color' prop if passed directly
3203
- containerClassName, children, ...restProps }) => {
3202
+ color, containerClassName, children, ...restProps }) => {
3204
3203
  // Get the default icon library from context, fallback to 'fa' if not set
3205
3204
  const defaultLibrary = useIconLibrary();
3206
3205
  /**
@@ -3208,7 +3207,7 @@ containerClassName, children, ...restProps }) => {
3208
3207
  * Note: variant, features, and libraryFeatures are excluded from props spread
3209
3208
  */
3210
3209
  const { bulmaHelperClasses, rest } = useBulmaClasses({
3211
- color: textColor,
3210
+ color: textColor ?? color,
3212
3211
  backgroundColor: bgColor,
3213
3212
  ...restProps,
3214
3213
  });
@@ -3474,7 +3473,7 @@ const EmptyStateActions = ({ className, ...props }) => {
3474
3473
  };
3475
3474
  EmptyStateActions.displayName = 'EmptyState.Actions';
3476
3475
  /** Whether a shorthand prop was given something to render. */
3477
- const isGiven = (node) => node !== undefined &&
3476
+ const isGiven$1 = (node) => node !== undefined &&
3478
3477
  node !== null &&
3479
3478
  typeof node !== 'boolean' &&
3480
3479
  node !== '';
@@ -3521,7 +3520,7 @@ const EmptyStateRoot = React.forwardRef(({ className, icon, title, titleAs = 'h2
3521
3520
  });
3522
3521
  // The props' parts first, each only when it's given, then the children
3523
3522
  // exactly as they were written: nothing here looks at them.
3524
- return (jsxRuntime.jsxs("div", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, children: [isGiven(icon) && (jsxRuntime.jsx(EmptyStateIndicator, { children: typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon })), isGiven(title) && (jsxRuntime.jsx(EmptyStateTitle, { as: titleAs, children: title })), isGiven(description) && (jsxRuntime.jsx(EmptyStateDescription, { children: description })), children] }));
3523
+ return (jsxRuntime.jsxs("div", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, children: [isGiven$1(icon) && (jsxRuntime.jsx(EmptyStateIndicator, { children: typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon })), isGiven$1(title) && (jsxRuntime.jsx(EmptyStateTitle, { as: titleAs, children: title })), isGiven$1(description) && (jsxRuntime.jsx(EmptyStateDescription, { children: description })), children] }));
3525
3524
  });
3526
3525
  const EmptyState = withSubComponents(EmptyStateRoot, {
3527
3526
  Indicator: EmptyStateIndicator,
@@ -7835,213 +7834,634 @@ const StepsComponent = ({ value = 0, items, size, color, hasMarker = true, anima
7835
7834
  const Steps = withSubComponents(StepsComponent, { Step }, 'Steps');
7836
7835
 
7837
7836
  /**
7838
- * The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.
7839
- *
7840
- * @function
7841
- * @param {SidebarProps} props - Props for the Sidebar component.
7842
- * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the sidebar element.
7843
- * @returns {JSX.Element | null} The rendered sidebar component.
7844
- *
7845
- * @example
7846
- * // Basic sidebar
7847
- * const [isOpen, setIsOpen] = useState(false);
7848
- * <Sidebar isOpen={isOpen} onClose={() => setIsOpen(false)}>
7849
- * <Menu>...</Menu>
7850
- * </Sidebar>
7851
- *
7852
- * @example
7853
- * // Right-side sidebar with custom width
7854
- * <Sidebar
7855
- * isOpen={isOpen}
7856
- * onClose={() => setIsOpen(false)}
7857
- * position="right"
7858
- * width="320px"
7859
- * >
7860
- * <div>Panel content</div>
7861
- * </Sidebar>
7862
- */
7863
- const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left', width = '260px', isFullwidth, fullWidth, overlay = true, overlayClose = true, escapeClose = true, canCancel = true, children, inline = false, className, style, ...props }, ref) => {
7864
- const { bulmaHelperClasses, rest } = useBulmaClasses(props);
7865
- const resolveContainer = usePortalContainer();
7866
- const { portalContainer } = useConfig();
7867
- // Where the configured default puts the sidebar. An `inline` one doesn't
7868
- // portal, so a change to the default moves nothing.
7869
- const defaultTarget = inline ? undefined : portalContainer;
7870
- const sidebarRef = React.useRef(null);
7871
- // Cleanup returned by a consumer's callback ref on attach, held until detach.
7872
- const consumerCleanupRef = React.useRef(null);
7873
- const resolvedFullwidth = isFullwidth ?? fullWidth ?? false;
7874
- // Close handler
7875
- const handleClose = React.useCallback(() => {
7876
- if (canCancel && onClose) {
7877
- onClose();
7878
- }
7879
- }, [canCancel, onClose]);
7880
- // Handle overlay click
7881
- const handleOverlayClick = React.useCallback(() => {
7882
- if (overlayClose) {
7883
- handleClose();
7884
- }
7885
- }, [overlayClose, handleClose]);
7886
- // Read when Escape arrives, so the key listener stays put while
7887
- // `onClose` changes identity, as an inline one does on every render of
7888
- // the parent.
7889
- const handleCloseRef = useLatestRef(handleClose);
7890
- // Joins the stack of open overlays, so the sidebar answers Escape only
7891
- // while it's the topmost overlay, and keeps it from those under it.
7892
- const isTopmost = useOverlayStack(isOpen);
7893
- const closesOnEscape = escapeClose && canCancel && Boolean(onClose);
7894
- // Handle escape key. On `window`, as in `Modal`, so everything inside the
7895
- // sidebar has had the key first, and an Escape it used is left to it. One
7896
- // the sidebar closes on is marked used, so nothing under it acts on it.
7897
- React.useEffect(() => {
7898
- if (!isOpen || !closesOnEscape)
7899
- return;
7900
- const handleKeyDown = (e) => {
7901
- if (e.key !== 'Escape' || e.defaultPrevented || !isTopmost())
7902
- return;
7903
- e.preventDefault();
7904
- handleCloseRef.current();
7905
- };
7906
- window.addEventListener('keydown', handleKeyDown);
7907
- return () => window.removeEventListener('keydown', handleKeyDown);
7908
- }, [isOpen, closesOnEscape, handleCloseRef, isTopmost]);
7909
- // Prevent body scroll when sidebar is open. Ref-counted through the shared
7910
- // helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.
7911
- useScrollLock(isOpen && overlay);
7912
- // Focus trap (basic - focus sidebar when opened). A different
7913
- // `defaultTarget` remounts the portaled sidebar, so it focuses the new
7914
- // nodes then too.
7915
- React.useEffect(() => {
7916
- if (isOpen) {
7917
- const element = sidebarRef.current;
7918
- /* istanbul ignore else: the aside renders whenever the sidebar is open, so its ref is set before this runs */
7919
- if (element) {
7920
- // Focus first focusable element or sidebar itself
7921
- const focusable = element.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
7922
- if (focusable) {
7923
- focusable.focus();
7924
- }
7925
- else {
7926
- element.focus();
7927
- }
7928
- }
7929
- }
7930
- }, [isOpen, defaultTarget]);
7931
- // Use combined ref
7932
- const combinedRef = React.useCallback((node) => {
7933
- sidebarRef.current =
7934
- node;
7935
- if (typeof ref === 'function') {
7936
- // React 19 lets a callback ref return a cleanup function and detaches by
7937
- // running it instead of calling the ref with `null`; React 18 discards the
7938
- // return value entirely. Returning it from here would be a React-19-only
7939
- // contract, so we hold the cleanup and run it ourselves on detach — the
7940
- // same shape as `Dropdown`, `Modal`, `Dialog`, `Toast` and `Carousel`.
7941
- // The form controls that merge a forwarded ref still discard the cleanup
7942
- // and detach with `ref(null)`, so this is not yet library-wide — grep
7943
- // `typeof ref === 'function'` for the current split.
7944
- if (node === null) {
7945
- const consumerCleanup = consumerCleanupRef.current;
7946
- consumerCleanupRef.current = null;
7947
- if (consumerCleanup) {
7948
- consumerCleanup();
7949
- }
7950
- else {
7951
- ref(null);
7952
- }
7953
- return;
7954
- }
7955
- const cleanup = ref(node);
7956
- consumerCleanupRef.current =
7957
- typeof cleanup === 'function' ? cleanup : null;
7958
- }
7959
- else if (ref) {
7960
- ref.current = node;
7961
- }
7962
- }, [ref]);
7963
- // Generate classes
7964
- const sidebarClasses = usePrefixedClassNames('sidebar', {
7965
- 'is-active': isOpen,
7966
- [`is-${position}`]: position,
7967
- 'is-fullwidth': resolvedFullwidth,
7968
- });
7969
- const backgroundClass = usePrefixedClassNames('sidebar-background', {
7970
- 'is-active': isOpen,
7971
- });
7972
- const contentClass = usePrefixedClassNames('sidebar-content');
7973
- const combinedClasses = classNames(sidebarClasses, bulmaHelperClasses, className);
7974
- // Custom style for width
7975
- const sidebarStyle = {
7976
- ...style,
7977
- '--bulma-sidebar-width': resolvedFullwidth ? '100%' : width,
7978
- };
7979
- const sidebarContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [overlay && (jsxRuntime.jsx("div", { className: backgroundClass, onClick: handleOverlayClick, "aria-hidden": "true" })), jsxRuntime.jsx("aside", { ref: combinedRef, className: combinedClasses, style: sidebarStyle, role: "dialog", "aria-modal": overlay ? 'true' : undefined, "aria-hidden": !isOpen, tabIndex: -1, ...rest, children: jsxRuntime.jsx("div", { className: contentClass, children: children }) })] }));
7980
- if (inline) {
7981
- return sidebarContent;
7982
- }
7983
- // Render to portal for proper stacking
7984
- if (typeof document !== 'undefined') {
7985
- return reactDom.createPortal(sidebarContent, resolveContainer());
7986
- }
7987
- return null;
7988
- });
7989
- /**
7990
- * Container for the sidebar header.
7991
- *
7992
- * @function
7993
- * @param {SidebarHeaderProps} props - Props for the SidebarHeader component.
7994
- * @returns {JSX.Element} The rendered sidebar header.
7837
+ * How long after an item appears its announcement is written, in ms. The wait
7838
+ * lets a screen reader register a region the container has only just mounted,
7839
+ * and items shown in quick succession are written, and read out, together.
7995
7840
  */
7996
- const SidebarHeader = ({ className, children, ...props }) => {
7997
- const headerClass = usePrefixedClassNames('sidebar-header');
7998
- return (jsxRuntime.jsx("div", { className: classNames(headerClass, className), ...props, children: children }));
7999
- };
7841
+ const announceDelay = 100;
8000
7842
  /**
8001
- * Title text inside the header.
8002
- *
8003
- * @function
8004
- * @param {SidebarTitleProps} props - Props for the SidebarTitle component.
8005
- * @returns {JSX.Element} The rendered sidebar title.
7843
+ * How long an announcement stays in the region once written, in ms. That's
7844
+ * long enough for a screen reader to pick it up. Clearing it after keeps a
7845
+ * second copy of the item's text from sitting in the page, where someone
7846
+ * reading through the page, or a test looking the text up, would find it
7847
+ * again.
8006
7848
  */
8007
- const SidebarTitle = ({ className, children, ...props }) => {
8008
- const titleClass = usePrefixedClassNames('sidebar-title');
8009
- return (jsxRuntime.jsx("p", { className: classNames(titleClass, className), ...props, children: children }));
7849
+ const announcementLifetime = 1000;
7850
+ const noAnnouncements = [];
7851
+ const nothingWritten = [];
7852
+ const noAnnouncementsYet = {
7853
+ key: 0,
7854
+ waiting: noAnnouncements,
7855
+ written: nothingWritten,
8010
7856
  };
8011
- /**
8012
- * Close button for the sidebar.
8013
- *
8014
- * @function
8015
- * @param {SidebarCloseProps} props - Props for the SidebarClose component.
8016
- * @returns {JSX.Element} The rendered close button.
8017
- */
8018
- const SidebarClose = ({ className, children, ...props }) => {
8019
- const closeClass = usePrefixedClassNames('sidebar-close');
8020
- return (jsxRuntime.jsx("button", { className: classNames(closeClass, className), ...props, "aria-label": props['aria-label'] || 'Close', type: buttonType(props.type), children: children }));
7857
+ // Hides the region with inline styles rather than a class, so it needs no
7858
+ // stylesheet, including the helper classes a modular Bulma build can leave
7859
+ // out. Clipping it, rather than using `display` or `visibility`, keeps it in
7860
+ // the accessibility tree. The declarations match the `extras-sr-only` mixin,
7861
+ // plus `clip-path`, which replaces the deprecated `clip`. The negative margin
7862
+ // pulls the 1px box back inside the page, so it doesn't add scrollable
7863
+ // overflow. Exported for other live regions that must work without a
7864
+ // stylesheet, such as a range calendar's.
7865
+ const visuallyHidden = {
7866
+ position: 'absolute',
7867
+ width: '1px',
7868
+ height: '1px',
7869
+ padding: 0,
7870
+ margin: '-1px',
7871
+ overflow: 'hidden',
7872
+ clip: 'rect(0, 0, 0, 0)',
7873
+ clipPath: 'inset(50%)',
7874
+ whiteSpace: 'nowrap',
7875
+ border: 0,
8021
7876
  };
8022
7877
  /**
8023
- * Main content area of the sidebar.
7878
+ * Works out what a status region says. Each item is announced once each time
7879
+ * it appears, so one that goes and comes back is announced again. Its
7880
+ * announcement is written `announceDelay` after it appears, or after the last
7881
+ * item to join it when several appear in quick succession, alongside whatever
7882
+ * the region still says. Once written, it stays for `announcementLifetime`,
7883
+ * whatever is written after it and whether or not its item goes in the
7884
+ * meantime. An item that goes before its announcement is written isn't
7885
+ * announced.
8024
7886
  *
8025
- * @function
8026
- * @param {SidebarBodyProps} props - Props for the SidebarBody component.
8027
- * @returns {JSX.Element} The rendered sidebar body.
8028
- */
8029
- const SidebarBody = ({ className, children, ...props }) => {
8030
- const bodyClass = usePrefixedClassNames('sidebar-body');
8031
- return (jsxRuntime.jsx("div", { className: classNames(bodyClass, className), ...props, children: children }));
8032
- };
8033
- /**
8034
- * Footer area of the sidebar.
7887
+ * Nothing is written in the commit that mounts the region or the item. A
7888
+ * screen reader reliably announces a polite live region whose content changes
7889
+ * after the region is already in the page, and not always one that arrives
7890
+ * with its content. By the time `describe` runs, an item's rendered text can
7891
+ * be read.
8035
7892
  *
8036
- * @function
8037
- * @param {SidebarFooterProps} props - Props for the SidebarFooter component.
8038
- * @returns {JSX.Element} The rendered sidebar footer.
7893
+ * @function useAnnouncements
7894
+ * @param items - The items to announce, in the order they were shown.
7895
+ * @param describe - The text to announce for an item, or nothing to skip it.
7896
+ * It runs once the item is on screen, and it must keep its identity between
7897
+ * renders (a module-level function or a `useCallback`), or every render
7898
+ * re-checks the items.
7899
+ * @returns The announcements to render, in the order they were written.
8039
7900
  */
8040
- const SidebarFooter = ({ className, children, ...props }) => {
8041
- const footerClass = usePrefixedClassNames('sidebar-footer');
8042
- return (jsxRuntime.jsx("div", { className: classNames(footerClass, className), ...props, children: children }));
8043
- };
8044
- // Attach static subcomponents
7901
+ function useAnnouncements(items, describe) {
7902
+ // The ids of the items on screen as of the last change, every one of which
7903
+ // has been looked at for an announcement already.
7904
+ const onScreenIdsRef = React.useRef(new Set());
7905
+ // Clears that haven't run yet. They outlive the batch that started them, so
7906
+ // they're only stopped when the region unmounts.
7907
+ const clearTimersRef = React.useRef(new Set());
7908
+ const [announcements, setAnnouncements] = React.useState(noAnnouncementsYet);
7909
+ // Runs after the commit that puts an item on screen, which is the first
7910
+ // point its rendered text can be read.
7911
+ React.useEffect(() => {
7912
+ const announced = onScreenIdsRef.current;
7913
+ const appeared = items.filter(item => !announced.has(item.id));
7914
+ // Items that have gone drop out here too, so one that comes back has
7915
+ // appeared again.
7916
+ onScreenIdsRef.current = new Set(items.map(item => item.id));
7917
+ const next = [];
7918
+ for (const item of appeared) {
7919
+ const text = describe(item);
7920
+ if (text)
7921
+ next.push({ id: item.id, text });
7922
+ }
7923
+ if (next.length > 0) {
7924
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- an item's rendered text can only be read once it's on screen
7925
+ setAnnouncements(current => ({
7926
+ ...current,
7927
+ key: current.key + 1,
7928
+ // Announcements still waiting go out with the new ones, apart from
7929
+ // one whose item came back before it was written, which the new one
7930
+ // replaces. What's already written stays put.
7931
+ waiting: [
7932
+ ...current.waiting.filter(waiting => !next.some(({ id }) => id === waiting.id)),
7933
+ ...next,
7934
+ ],
7935
+ }));
7936
+ }
7937
+ }, [items, describe]);
7938
+ // The write only touches the batch it was started for: a newer batch can
7939
+ // be set before the render that cancels this timer. Its clear is started
7940
+ // by the write itself, so it's timed from the write without waiting on a
7941
+ // render, and a newer batch arriving doesn't cut short what's written.
7942
+ const { key } = announcements;
7943
+ React.useEffect(() => {
7944
+ if (key === noAnnouncementsYet.key)
7945
+ return undefined;
7946
+ const clearTimers = clearTimersRef.current;
7947
+ const write = setTimeout(() => {
7948
+ // Whether an item is on screen is decided here, once. An item that
7949
+ // went before its announcement was due isn't announced, and one that
7950
+ // goes after keeps its announcement for the rest of its lifetime, when
7951
+ // the region holds the only copy of its text rather than a second one.
7952
+ const onScreen = onScreenIdsRef.current;
7953
+ setAnnouncements(current => current.key === key
7954
+ ? {
7955
+ ...current,
7956
+ waiting: noAnnouncements,
7957
+ written: [
7958
+ ...current.written,
7959
+ ...current.waiting
7960
+ .filter(announcement => onScreen.has(announcement.id))
7961
+ .map(announcement => ({ ...announcement, batch: key })),
7962
+ ],
7963
+ }
7964
+ : current);
7965
+ // eslint-disable-next-line @eslint-react/web-api-no-leaked-timeout -- it has to outlive this effect when a newer batch arrives, so the unmount effect below clears it
7966
+ const clear = setTimeout(() => {
7967
+ clearTimers.delete(clear);
7968
+ setAnnouncements(current => {
7969
+ const written = current.written.filter(announcement => announcement.batch !== key);
7970
+ return written.length === current.written.length
7971
+ ? current
7972
+ : { ...current, written };
7973
+ });
7974
+ }, announcementLifetime);
7975
+ clearTimers.add(clear);
7976
+ }, announceDelay);
7977
+ return () => clearTimeout(write);
7978
+ }, [key]);
7979
+ React.useEffect(() => {
7980
+ const clearTimers = clearTimersRef.current;
7981
+ return () => {
7982
+ clearTimers.forEach(clearTimeout);
7983
+ clearTimers.clear();
7984
+ };
7985
+ }, []);
7986
+ return announcements.written;
7987
+ }
7988
+ /**
7989
+ * Reads an element out roughly the way a screen reader would: its text, with
7990
+ * an element's `aria-label`, or an image's `alt`, standing in for what's
7991
+ * inside it, a break between block-level elements, and parts that are
7992
+ * `aria-hidden`, `hidden` or inline-styled `display: none` left out. It isn't
7993
+ * the full accessible name computation, so `aria-labelledby`, CSS-generated
7994
+ * content, content hidden by a stylesheet and other ways of naming or hiding
7995
+ * content aren't followed.
7996
+ *
7997
+ * @function spokenText
7998
+ * @param element - The element to read.
7999
+ * @returns Its text, with runs of whitespace collapsed and the ends trimmed.
8000
+ */
8001
+ function spokenText(element) {
8002
+ return readAloud(element).replace(/\s+/g, ' ').trim();
8003
+ }
8004
+ // Elements a browser displays as blocks by default, and `br`. A screen reader
8005
+ // pauses between them, so their text shouldn't run into the text beside them.
8006
+ // They're known by tag rather than by computed `display`, which would depend
8007
+ // on a stylesheet and on layout.
8008
+ const blockLevelTags = new Set([
8009
+ 'ADDRESS',
8010
+ 'ARTICLE',
8011
+ 'ASIDE',
8012
+ 'BLOCKQUOTE',
8013
+ 'BR',
8014
+ 'CAPTION',
8015
+ 'DD',
8016
+ 'DETAILS',
8017
+ 'DIALOG',
8018
+ 'DIV',
8019
+ 'DL',
8020
+ 'DT',
8021
+ 'FIELDSET',
8022
+ 'FIGCAPTION',
8023
+ 'FIGURE',
8024
+ 'FOOTER',
8025
+ 'FORM',
8026
+ 'H1',
8027
+ 'H2',
8028
+ 'H3',
8029
+ 'H4',
8030
+ 'H5',
8031
+ 'H6',
8032
+ 'HEADER',
8033
+ 'HGROUP',
8034
+ 'HR',
8035
+ 'LEGEND',
8036
+ 'LI',
8037
+ 'MAIN',
8038
+ 'MENU',
8039
+ 'NAV',
8040
+ 'OL',
8041
+ 'P',
8042
+ 'PRE',
8043
+ 'SEARCH',
8044
+ 'SECTION',
8045
+ 'SUMMARY',
8046
+ 'TABLE',
8047
+ 'TBODY',
8048
+ 'TD',
8049
+ 'TFOOT',
8050
+ 'TH',
8051
+ 'THEAD',
8052
+ 'TR',
8053
+ 'UL',
8054
+ ]);
8055
+ /**
8056
+ * Whether an element is left out of what a screen reader reads.
8057
+ *
8058
+ * @function isHidden
8059
+ * @param element - The element to check.
8060
+ * @returns True for `aria-hidden="true"`, the `hidden` attribute, and an
8061
+ * inline `display: none`, on an HTML, SVG or any other element with an
8062
+ * inline style.
8063
+ */
8064
+ const isHidden = (element) => element.getAttribute('aria-hidden') === 'true' ||
8065
+ element.hasAttribute('hidden') ||
8066
+ element.style?.display === 'none';
8067
+ /**
8068
+ * The text `spokenText` reads for an element, before its whitespace is
8069
+ * tidied.
8070
+ *
8071
+ * @function readAloud
8072
+ * @param element - The element to read.
8073
+ * @returns Its text.
8074
+ */
8075
+ function readAloud(element) {
8076
+ if (isHidden(element))
8077
+ return '';
8078
+ const name = element.getAttribute('aria-label') ||
8079
+ (element.tagName === 'IMG' ? element.getAttribute('alt') : null);
8080
+ const text = name ||
8081
+ Array.from(element.childNodes, child => {
8082
+ if (child instanceof Element)
8083
+ return readAloud(child);
8084
+ return child.nodeType === Node.TEXT_NODE ? child.textContent : '';
8085
+ }).join('');
8086
+ // A name stands in for the element's content, and a block's content stands
8087
+ // apart from what's around it, so both are padded to keep them from running
8088
+ // into the neighboring text. An inline run, like `<span>Up</span>` then
8089
+ // `<span>loaded</span>`, still reads as one word.
8090
+ return name || blockLevelTags.has(element.tagName) ? ` ${text} ` : text;
8091
+ }
8092
+ /**
8093
+ * A visually hidden, polite `status` live region that a container announces
8094
+ * its items through. The container renders it from the moment it mounts,
8095
+ * whether or not it has anything to show, so the region is already in the
8096
+ * page when an announcement is written into it.
8097
+ *
8098
+ * The announcements are this component's own state, so writing and clearing
8099
+ * them re-renders the region and leaves the container's items alone. Each one
8100
+ * is its own keyed node, so a re-render that leaves the announcements alone
8101
+ * leaves the region's content alone too, and nothing is read out twice.
8102
+ *
8103
+ * @function StatusRegion
8104
+ * @param props - The items to announce, and the text to announce for each.
8105
+ * @returns The region.
8106
+ */
8107
+ function StatusRegion({ items, describe, }) {
8108
+ const announcements = useAnnouncements(items, describe);
8109
+ // `role="status"` makes a region atomic unless it says otherwise, which
8110
+ // reads the whole region on every change. Each announcement is its own
8111
+ // node, and only additions are relevant by default, so with atomic off a
8112
+ // screen reader reads just what was written, and removing one announcement
8113
+ // doesn't read out the ones still there. A node is keyed by its batch as
8114
+ // well as its item, since an item that comes back can be written again
8115
+ // while what it said before is still there.
8116
+ 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}`))) }));
8117
+ }
8118
+
8119
+ const validTimelineColors = [
8120
+ 'primary',
8121
+ 'link',
8122
+ 'info',
8123
+ 'success',
8124
+ 'warning',
8125
+ 'danger',
8126
+ ];
8127
+ const validTimelineTitleElements = ['h2', 'h3', 'h4', 'h5', 'h6', 'p'];
8128
+ // An item outside a Timeline is a record item with an h2 title.
8129
+ const TimelineItemContext = React.createContext({
8130
+ titleAs: 'h2',
8131
+ });
8132
+ /** Whether a prop was given something to render. */
8133
+ const isGiven = (node) => node !== undefined &&
8134
+ node !== null &&
8135
+ typeof node !== 'boolean' &&
8136
+ node !== '';
8137
+ const isTimelineColor = (color) => !!color && validTimelineColors.includes(color);
8138
+ /**
8139
+ * One event: a marker on the rail, then its title, its time and anything else about it.
8140
+ *
8141
+ * Outside a `Timeline` it's a record item with an `h2` title, though the
8142
+ * styles are registered on `.timeline`, so the rail and the marker need one.
8143
+ *
8144
+ * @function
8145
+ * @param {TimelineItemProps} props - Props for Timeline.Item.
8146
+ * @param {React.Ref<HTMLLIElement>} ref - Forwarded ref to the `<li>`.
8147
+ * @returns {JSX.Element} The rendered event.
8148
+ *
8149
+ * @example
8150
+ * <Timeline.Item title="Shipped" time="14 October 2026" dateTime="2026-10-14">
8151
+ * Left the warehouse in Leeds.
8152
+ * </Timeline.Item>
8153
+ */
8154
+ const TimelineItem = React.forwardRef(({ className, title, time, dateTime, icon, color, textColor, bgColor, children, ...props }, ref) => {
8155
+ const { titleAs: TitleTag, state, label } = React.useContext(TimelineItemContext);
8156
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
8157
+ color: textColor,
8158
+ backgroundColor: bgColor,
8159
+ ...props,
8160
+ });
8161
+ const itemClass = usePrefixedClassNames('timeline-item', {
8162
+ [`is-${state}`]: !!state,
8163
+ [`is-${color}`]: isTimelineColor(color),
8164
+ });
8165
+ const markerClass = usePrefixedClassNames('timeline-marker', {
8166
+ 'is-icon': isGiven(icon),
8167
+ });
8168
+ const contentClass = usePrefixedClassNames('timeline-content');
8169
+ const titleClass = usePrefixedClassNames('timeline-title');
8170
+ const timeClass = usePrefixedClassNames('timeline-time');
8171
+ // The state in words, read after the title so it's in the heading too:
8172
+ // "Packed completed". The current item has none, since aria-current
8173
+ // says it. The styles are inline, so the words stay hidden in a modular
8174
+ // build that leaves out Bulma's helper classes.
8175
+ const words = label ? (jsxRuntime.jsx("span", { style: visuallyHidden, children: ` ${label}` })) : null;
8176
+ return (jsxRuntime.jsxs("li", { ref: ref, className: classNames(itemClass, bulmaHelperClasses, className), ...rest, ...(state && {
8177
+ 'aria-current': state === 'active' ? 'step' : undefined,
8178
+ }), children: [jsxRuntime.jsx("div", { className: markerClass, "aria-hidden": "true", children: isGiven(icon) &&
8179
+ (typeof icon === 'string' ? jsxRuntime.jsx(Icon, { name: icon }) : icon) }), jsxRuntime.jsxs("div", { className: contentClass, children: [isGiven(title) ? (jsxRuntime.jsxs(TitleTag, { className: titleClass, children: [title, words] })) : (words), isGiven(time) &&
8180
+ (dateTime ? (jsxRuntime.jsx("time", { className: timeClass, dateTime: dateTime, children: time })) : (jsxRuntime.jsx("span", { className: timeClass, children: time }))), children] })] }));
8181
+ });
8182
+ TimelineItem.displayName = 'Timeline.Item';
8183
+ /**
8184
+ * The `Timeline` component renders a list of events in the order they happened, each with a marker on a line down the side.
8185
+ *
8186
+ * By default it's a record, an order's history, a ticket's activity or a
8187
+ * changelog, and it marks nothing. With `active` it marks progress, for
8188
+ * something that moves along on its own and is reported to the user: a
8189
+ * parcel, a deployment, a claim. That reads oldest first, and `pending` ends
8190
+ * it on what's under way. A journey the user moves through themselves, like
8191
+ * a wizard or a checkout, is `Steps`.
8192
+ *
8193
+ * It's an `<ol role="list">`, so it stays a list in Safari with its numbers
8194
+ * hidden, and each title is a heading at the level `titleAs` sets. The
8195
+ * markers are decoration and hidden from assistive tech, so the states are
8196
+ * read as words: `aria-current="step"` on the current item, and "completed",
8197
+ * "upcoming" or "in progress" after the other titles, in the language the
8198
+ * label props set. Nothing here is a control or a live region, so a timeline
8199
+ * that changes on screen keeps a `role="status"` region beside it.
8200
+ *
8201
+ * @function
8202
+ * @param {TimelineProps} props - Props for the Timeline component.
8203
+ * @param {React.Ref<HTMLOListElement>} ref - Forwarded ref to the `<ol>`.
8204
+ * @returns {JSX.Element} The rendered timeline.
8205
+ *
8206
+ * @example
8207
+ * <Timeline titleAs="h3">
8208
+ * <Timeline.Item title="Delivered" time="16 October 2026" dateTime="2026-10-16" />
8209
+ * <Timeline.Item title="Order placed" time="12 October 2026" dateTime="2026-10-12" />
8210
+ * </Timeline>
8211
+ *
8212
+ * @example
8213
+ * <Timeline titleAs="h3" active={1} pending="Out for delivery">
8214
+ * <Timeline.Item title="Order placed" />
8215
+ * <Timeline.Item title="Shipped" />
8216
+ * </Timeline>
8217
+ */
8218
+ const TimelineRoot = React.forwardRef(({ className, children, active, pending, align = 'start', titleAs = 'h2', color, completedLabel = 'completed', upcomingLabel = 'upcoming', pendingLabel = 'in progress', textColor, bgColor, ...props }, ref) => {
8219
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
8220
+ color: textColor,
8221
+ backgroundColor: bgColor,
8222
+ ...props,
8223
+ });
8224
+ const rootClass = usePrefixedClassNames('timeline', {
8225
+ 'is-end': align === 'end',
8226
+ 'is-alternate': align === 'alternate',
8227
+ [`is-${color}`]: isTimelineColor(color),
8228
+ });
8229
+ const level = validTimelineTitleElements.includes(titleAs) ? titleAs : 'h2';
8230
+ const labels = {
8231
+ completed: completedLabel,
8232
+ active: undefined,
8233
+ upcoming: upcomingLabel,
8234
+ pending: pendingLabel,
8235
+ };
8236
+ const contextFor = (state) => ({
8237
+ titleAs: level,
8238
+ state,
8239
+ label: state && labels[state],
8240
+ });
8241
+ // A fraction counts as the item it has reached, so one item is current.
8242
+ const reached = typeof active === 'number' ? Math.floor(active) : undefined;
8243
+ const stateAt = (index) => {
8244
+ if (reached === undefined)
8245
+ return undefined;
8246
+ if (index === reached)
8247
+ return 'active';
8248
+ return index < reached ? 'completed' : 'upcoming';
8249
+ };
8250
+ // Each child gets its state through context rather than a clone, so an
8251
+ // app's own component around a Timeline.Item still gets it. Fragments
8252
+ // and arrays are opened up first, so each item inside one counts.
8253
+ return (jsxRuntime.jsxs("ol", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, role: "list", children: [flattenChildren(children).map((child, index) => (jsxRuntime.jsx(TimelineItemContext.Provider, { value: contextFor(stateAt(index)), children: child }, React.isValidElement(child) ? child.key : index))), isGiven(pending) && (jsxRuntime.jsx(TimelineItemContext.Provider, { value: contextFor('pending'), children: jsxRuntime.jsx(TimelineItem, { title: pending }) }))] }));
8254
+ });
8255
+ const Timeline = withSubComponents(TimelineRoot, { Item: TimelineItem }, 'Timeline');
8256
+
8257
+ /**
8258
+ * The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.
8259
+ *
8260
+ * @function
8261
+ * @param {SidebarProps} props - Props for the Sidebar component.
8262
+ * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the sidebar element.
8263
+ * @returns {JSX.Element | null} The rendered sidebar component.
8264
+ *
8265
+ * @example
8266
+ * // Basic sidebar
8267
+ * const [isOpen, setIsOpen] = useState(false);
8268
+ * <Sidebar isOpen={isOpen} onClose={() => setIsOpen(false)}>
8269
+ * <Menu>...</Menu>
8270
+ * </Sidebar>
8271
+ *
8272
+ * @example
8273
+ * // Right-side sidebar with custom width
8274
+ * <Sidebar
8275
+ * isOpen={isOpen}
8276
+ * onClose={() => setIsOpen(false)}
8277
+ * position="right"
8278
+ * width="320px"
8279
+ * >
8280
+ * <div>Panel content</div>
8281
+ * </Sidebar>
8282
+ */
8283
+ const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left', width = '260px', isFullwidth, fullWidth, overlay = true, overlayClose = true, escapeClose = true, canCancel = true, children, inline = false, className, style, ...props }, ref) => {
8284
+ const { bulmaHelperClasses, rest } = useBulmaClasses(props);
8285
+ const resolveContainer = usePortalContainer();
8286
+ const { portalContainer } = useConfig();
8287
+ // Where the configured default puts the sidebar. An `inline` one doesn't
8288
+ // portal, so a change to the default moves nothing.
8289
+ const defaultTarget = inline ? undefined : portalContainer;
8290
+ const sidebarRef = React.useRef(null);
8291
+ // Cleanup returned by a consumer's callback ref on attach, held until detach.
8292
+ const consumerCleanupRef = React.useRef(null);
8293
+ const resolvedFullwidth = isFullwidth ?? fullWidth ?? false;
8294
+ // Close handler
8295
+ const handleClose = React.useCallback(() => {
8296
+ if (canCancel && onClose) {
8297
+ onClose();
8298
+ }
8299
+ }, [canCancel, onClose]);
8300
+ // Handle overlay click
8301
+ const handleOverlayClick = React.useCallback(() => {
8302
+ if (overlayClose) {
8303
+ handleClose();
8304
+ }
8305
+ }, [overlayClose, handleClose]);
8306
+ // Read when Escape arrives, so the key listener stays put while
8307
+ // `onClose` changes identity, as an inline one does on every render of
8308
+ // the parent.
8309
+ const handleCloseRef = useLatestRef(handleClose);
8310
+ // Joins the stack of open overlays, so the sidebar answers Escape only
8311
+ // while it's the topmost overlay, and keeps it from those under it.
8312
+ const isTopmost = useOverlayStack(isOpen);
8313
+ const closesOnEscape = escapeClose && canCancel && Boolean(onClose);
8314
+ // Handle escape key. On `window`, as in `Modal`, so everything inside the
8315
+ // sidebar has had the key first, and an Escape it used is left to it. One
8316
+ // the sidebar closes on is marked used, so nothing under it acts on it.
8317
+ React.useEffect(() => {
8318
+ if (!isOpen || !closesOnEscape)
8319
+ return;
8320
+ const handleKeyDown = (e) => {
8321
+ if (e.key !== 'Escape' || e.defaultPrevented || !isTopmost())
8322
+ return;
8323
+ e.preventDefault();
8324
+ handleCloseRef.current();
8325
+ };
8326
+ window.addEventListener('keydown', handleKeyDown);
8327
+ return () => window.removeEventListener('keydown', handleKeyDown);
8328
+ }, [isOpen, closesOnEscape, handleCloseRef, isTopmost]);
8329
+ // Prevent body scroll when sidebar is open. Ref-counted through the shared
8330
+ // helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.
8331
+ useScrollLock(isOpen && overlay);
8332
+ // Focus trap (basic - focus sidebar when opened). A different
8333
+ // `defaultTarget` remounts the portaled sidebar, so it focuses the new
8334
+ // nodes then too.
8335
+ React.useEffect(() => {
8336
+ if (isOpen) {
8337
+ const element = sidebarRef.current;
8338
+ /* istanbul ignore else: the aside renders whenever the sidebar is open, so its ref is set before this runs */
8339
+ if (element) {
8340
+ // Focus first focusable element or sidebar itself
8341
+ const focusable = element.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
8342
+ if (focusable) {
8343
+ focusable.focus();
8344
+ }
8345
+ else {
8346
+ element.focus();
8347
+ }
8348
+ }
8349
+ }
8350
+ }, [isOpen, defaultTarget]);
8351
+ // Use combined ref
8352
+ const combinedRef = React.useCallback((node) => {
8353
+ sidebarRef.current =
8354
+ node;
8355
+ if (typeof ref === 'function') {
8356
+ // React 19 lets a callback ref return a cleanup function and detaches by
8357
+ // running it instead of calling the ref with `null`; React 18 discards the
8358
+ // return value entirely. Returning it from here would be a React-19-only
8359
+ // contract, so we hold the cleanup and run it ourselves on detach — the
8360
+ // same shape as `Dropdown`, `Modal`, `Dialog`, `Toast` and `Carousel`.
8361
+ // The form controls that merge a forwarded ref still discard the cleanup
8362
+ // and detach with `ref(null)`, so this is not yet library-wide — grep
8363
+ // `typeof ref === 'function'` for the current split.
8364
+ if (node === null) {
8365
+ const consumerCleanup = consumerCleanupRef.current;
8366
+ consumerCleanupRef.current = null;
8367
+ if (consumerCleanup) {
8368
+ consumerCleanup();
8369
+ }
8370
+ else {
8371
+ ref(null);
8372
+ }
8373
+ return;
8374
+ }
8375
+ const cleanup = ref(node);
8376
+ consumerCleanupRef.current =
8377
+ typeof cleanup === 'function' ? cleanup : null;
8378
+ }
8379
+ else if (ref) {
8380
+ ref.current = node;
8381
+ }
8382
+ }, [ref]);
8383
+ // Generate classes
8384
+ const sidebarClasses = usePrefixedClassNames('sidebar', {
8385
+ 'is-active': isOpen,
8386
+ [`is-${position}`]: position,
8387
+ 'is-fullwidth': resolvedFullwidth,
8388
+ });
8389
+ const backgroundClass = usePrefixedClassNames('sidebar-background', {
8390
+ 'is-active': isOpen,
8391
+ });
8392
+ const contentClass = usePrefixedClassNames('sidebar-content');
8393
+ const combinedClasses = classNames(sidebarClasses, bulmaHelperClasses, className);
8394
+ // Custom style for width
8395
+ const sidebarStyle = {
8396
+ ...style,
8397
+ '--bulma-sidebar-width': resolvedFullwidth ? '100%' : width,
8398
+ };
8399
+ const sidebarContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [overlay && (jsxRuntime.jsx("div", { className: backgroundClass, onClick: handleOverlayClick, "aria-hidden": "true" })), jsxRuntime.jsx("aside", { ref: combinedRef, className: combinedClasses, style: sidebarStyle, role: "dialog", "aria-modal": overlay ? 'true' : undefined, "aria-hidden": !isOpen, tabIndex: -1, ...rest, children: jsxRuntime.jsx("div", { className: contentClass, children: children }) })] }));
8400
+ if (inline) {
8401
+ return sidebarContent;
8402
+ }
8403
+ // Render to portal for proper stacking
8404
+ if (typeof document !== 'undefined') {
8405
+ return reactDom.createPortal(sidebarContent, resolveContainer());
8406
+ }
8407
+ return null;
8408
+ });
8409
+ /**
8410
+ * Container for the sidebar header.
8411
+ *
8412
+ * @function
8413
+ * @param {SidebarHeaderProps} props - Props for the SidebarHeader component.
8414
+ * @returns {JSX.Element} The rendered sidebar header.
8415
+ */
8416
+ const SidebarHeader = ({ className, children, ...props }) => {
8417
+ const headerClass = usePrefixedClassNames('sidebar-header');
8418
+ return (jsxRuntime.jsx("div", { className: classNames(headerClass, className), ...props, children: children }));
8419
+ };
8420
+ /**
8421
+ * Title text inside the header.
8422
+ *
8423
+ * @function
8424
+ * @param {SidebarTitleProps} props - Props for the SidebarTitle component.
8425
+ * @returns {JSX.Element} The rendered sidebar title.
8426
+ */
8427
+ const SidebarTitle = ({ className, children, ...props }) => {
8428
+ const titleClass = usePrefixedClassNames('sidebar-title');
8429
+ return (jsxRuntime.jsx("p", { className: classNames(titleClass, className), ...props, children: children }));
8430
+ };
8431
+ /**
8432
+ * Close button for the sidebar.
8433
+ *
8434
+ * @function
8435
+ * @param {SidebarCloseProps} props - Props for the SidebarClose component.
8436
+ * @returns {JSX.Element} The rendered close button.
8437
+ */
8438
+ const SidebarClose = ({ className, children, ...props }) => {
8439
+ const closeClass = usePrefixedClassNames('sidebar-close');
8440
+ return (jsxRuntime.jsx("button", { className: classNames(closeClass, className), ...props, "aria-label": props['aria-label'] || 'Close', type: buttonType(props.type), children: children }));
8441
+ };
8442
+ /**
8443
+ * Main content area of the sidebar.
8444
+ *
8445
+ * @function
8446
+ * @param {SidebarBodyProps} props - Props for the SidebarBody component.
8447
+ * @returns {JSX.Element} The rendered sidebar body.
8448
+ */
8449
+ const SidebarBody = ({ className, children, ...props }) => {
8450
+ const bodyClass = usePrefixedClassNames('sidebar-body');
8451
+ return (jsxRuntime.jsx("div", { className: classNames(bodyClass, className), ...props, children: children }));
8452
+ };
8453
+ /**
8454
+ * Footer area of the sidebar.
8455
+ *
8456
+ * @function
8457
+ * @param {SidebarFooterProps} props - Props for the SidebarFooter component.
8458
+ * @returns {JSX.Element} The rendered sidebar footer.
8459
+ */
8460
+ const SidebarFooter = ({ className, children, ...props }) => {
8461
+ const footerClass = usePrefixedClassNames('sidebar-footer');
8462
+ return (jsxRuntime.jsx("div", { className: classNames(footerClass, className), ...props, children: children }));
8463
+ };
8464
+ // Attach static subcomponents
8045
8465
  const Sidebar = withSubComponents(SidebarComponent, {
8046
8466
  Header: SidebarHeader,
8047
8467
  Title: SidebarTitle,
@@ -9181,208 +9601,25 @@ const Box = ({ className, textColor, color, bgColor, hasShadow = true, children,
9181
9601
  const Code = ({ className, textColor, bgColor, children, ...props }) => {
9182
9602
  const { bulmaHelperClasses, rest } = useBulmaClasses({
9183
9603
  color: textColor,
9184
- backgroundColor: bgColor,
9185
- ...props,
9186
- });
9187
- const bulmaClasses = usePrefixedClassNames();
9188
- const codeClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9189
- return (jsxRuntime.jsx("code", { className: codeClasses || undefined, ...rest, children: children }));
9190
- };
9191
-
9192
- // Valid size modifiers for the content class
9193
- const validSizes = ['small', 'medium', 'large'];
9194
- /**
9195
- * The `Content` component applies Bulma’s typographic styles to its children, enhancing the appearance of HTML elements like paragraphs, headings, lists, and tables.
9196
- *
9197
- * @function
9198
- * @param {ContentProps} props - Props for the Content component.
9199
- * @returns {JSX.Element} The rendered content block.
9200
- * @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
9201
- */
9202
- const Content = ({ className, textColor, color, bgColor, size, children, ...props }) => {
9203
- /**
9204
- * Generates Bulma helper classes and separates out remaining props.
9205
- */
9206
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9207
- color: textColor ?? color,
9208
- backgroundColor: bgColor,
9209
- ...props,
9210
- });
9211
- const bulmaClasses = usePrefixedClassNames('content', {
9212
- [`is-${size}`]: size && size !== 'normal' && validSizes.includes(size),
9213
- });
9214
- const contentClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9215
- return (jsxRuntime.jsx("div", { className: contentClasses, ...rest, children: children }));
9216
- };
9217
-
9218
- /**
9219
- * The `Delete` component provides a Bulma-styled close/delete button for dismissing modals, notifications, tags, messages, and more.
9220
- *
9221
- * It renders `type="button"`, so a delete button inside a form does not submit it.
9222
- *
9223
- * @function
9224
- * @param {DeleteProps} props - Props for the Delete component.
9225
- * @returns {JSX.Element} The rendered delete button.
9226
- * @see {@link https://bulma.io/documentation/elements/delete/ | Bulma Delete documentation}
9227
- */
9228
- const Delete = ({ className, textColor, color, bgColor, onClick, size, ariaLabel, disabled = false, ...props }) => {
9229
- /**
9230
- * Generates Bulma helper classes and separates out remaining props.
9231
- */
9232
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9233
- color: textColor ?? color,
9234
- backgroundColor: bgColor,
9235
- ...props,
9236
- });
9237
- const bulmaClasses = usePrefixedClassNames('delete', {
9238
- [`is-${size}`]: size,
9239
- 'is-disabled': disabled,
9240
- });
9241
- const classes = classNames(bulmaClasses, bulmaHelperClasses, className);
9242
- return (jsxRuntime.jsx("button", { className: classes, onClick: onClick, disabled: disabled, ...rest, "aria-label": rest['aria-label'] || ariaLabel || 'Close', type: buttonType(rest.type) }));
9243
- };
9244
-
9245
- /**
9246
- * The `Divider` component renders a styled horizontal rule (`<hr>`) element with Bulma helper class integration.
9247
- *
9248
- * @function
9249
- * @param {DividerProps} props - Props for the Divider component.
9250
- * @returns {JSX.Element} The rendered hr element.
9251
- */
9252
- const Divider = ({ className, bgColor, ...props }) => {
9253
- /**
9254
- * Generates Bulma helper classes and separates out remaining props.
9255
- */
9256
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9257
- backgroundColor: bgColor,
9258
- ...props,
9259
- });
9260
- const bulmaClasses = usePrefixedClassNames();
9261
- const dividerClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9262
- return jsxRuntime.jsx("hr", { className: dividerClasses || undefined, ...rest });
9263
- };
9264
-
9265
- /**
9266
- * The `Emphasis` component renders a styled `<em>` element with Bulma helper class integration.
9267
- *
9268
- * @function
9269
- * @param {EmphasisProps} props - Props for the Emphasis component.
9270
- * @returns {JSX.Element} The rendered em element.
9271
- */
9272
- const Emphasis = ({ className, textColor, bgColor, children, ...props }) => {
9273
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9274
- color: textColor,
9275
- backgroundColor: bgColor,
9276
- ...props,
9277
- });
9278
- const bulmaClasses = usePrefixedClassNames();
9279
- const emphasisClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9280
- return (jsxRuntime.jsx("em", { className: emphasisClasses || undefined, ...rest, children: children }));
9281
- };
9282
-
9283
- /**
9284
- * FigureCaption component for rendering a styled figcaption element.
9285
- *
9286
- * A FigureCaption wraps the HTML `<figcaption>` element with Bulma helper class integration.
9287
- * Use it inside Figure components to provide captions for images or other media.
9288
- *
9289
- * @function
9290
- * @param {FigureCaptionProps} props - Props for the FigureCaption component.
9291
- * @returns {JSX.Element} The rendered figcaption element.
9292
- */
9293
- const FigureCaption = ({ className, textColor, bgColor, children, ...props }) => {
9294
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9295
- color: textColor,
9296
- backgroundColor: bgColor,
9297
- ...props,
9298
- });
9299
- const bulmaClasses = usePrefixedClassNames();
9300
- const captionClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9301
- return (jsxRuntime.jsx("figcaption", { className: captionClasses || undefined, ...rest, children: children }));
9302
- };
9303
- /**
9304
- * The `Figure` component renders a styled `<figure>` element with Bulma helper class integration.
9305
- *
9306
- * @function
9307
- * @param {FigureProps} props - Props for the Figure component.
9308
- * @returns {JSX.Element} The rendered figure element.
9309
- */
9310
- const FigureComponent = ({ className, textColor, bgColor, children, ...props }) => {
9311
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9312
- color: textColor,
9313
- backgroundColor: bgColor,
9314
- ...props,
9315
- });
9316
- const bulmaClasses = usePrefixedClassNames();
9317
- const figureClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9318
- return (jsxRuntime.jsx("figure", { className: figureClasses || undefined, ...rest, children: children }));
9319
- };
9320
- /**
9321
- * Figure component with Caption subcomponent.
9322
- *
9323
- * @example
9324
- * <Figure>
9325
- * <img src="image.jpg" alt="Description" />
9326
- * <Figure.Caption>Image caption text</Figure.Caption>
9327
- * </Figure>
9328
- */
9329
- const Figure = withSubComponents(FigureComponent, {
9330
- Caption: FigureCaption,
9331
- }, 'Figure');
9332
-
9333
- /**
9334
- * Distinguishes an `IconProps` object from a plain custom node (an inline SVG, a
9335
- * `react-icons` component, …) in a slot that accepts either — `Control`'s
9336
- * `iconLeft`/`iconRight`, `IconText`'s `iconProps`.
9337
- *
9338
- * One copy, imported by both consumers: two hand-maintained copies, one per
9339
- * consumer, is how the deprecated `icon` path came to crash both (#663).
9340
- *
9341
- * Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports
9342
- * that file, and everything published there is public forever. `polymorphic.ts`
9343
- * makes the same call for `isCustomElement`: an internal discriminator is not
9344
- * something to support for the life of the package.
9345
- *
9346
- * It tests each member's VALUE, not merely its key, and only after everything
9347
- * React owns is out of the way. `'icon' in value` was enough
9348
- * to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,
9349
- * path] }` — whose `icon` is a path array rather than a class string. That object
9350
- * is not a renderable node either, so the honest outcome is the one this slot has
9351
- * always had for it: the node branch, and React's own "Objects are not valid as a
9352
- * React child". Claiming it here instead rendered a blank container that still
9353
- * reserved layout.
9354
- *
9355
- * A member added to `IconProps` needs a line here, and a missed one is a crash
9356
- * rather than a fallthrough. That is the cost of a slot taking either a props
9357
- * object or a node; keeping the guard in one place is what keeps the cost to a
9358
- * single line.
9359
- */
9360
- function isIconProps(value) {
9361
- if (typeof value !== 'object' || value === null)
9362
- return false;
9363
- // Anything React owns. `isValidElement` covers elements only; a PORTAL is a
9364
- // valid `ReactNode` that fails it and carries a `children` key, so the member
9365
- // test below claimed it and `Control` spread it into `<Icon>` as props —
9366
- // rendering the portal's child locally instead of in its container. Every
9367
- // React internal carries `$$typeof`, so testing for it covers lazy, context
9368
- // and whatever React adds next, rather than portals alone.
9369
- if (React.isValidElement(value) || '$$typeof' in value)
9370
- return false;
9371
- const candidate = value;
9372
- return (typeof candidate.name === 'string' ||
9373
- typeof candidate.icon === 'string' ||
9374
- 'children' in candidate);
9375
- }
9604
+ backgroundColor: bgColor,
9605
+ ...props,
9606
+ });
9607
+ const bulmaClasses = usePrefixedClassNames();
9608
+ const codeClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9609
+ return (jsxRuntime.jsx("code", { className: codeClasses || undefined, ...rest, children: children }));
9610
+ };
9376
9611
 
9612
+ // Valid size modifiers for the content class
9613
+ const validSizes = ['small', 'medium', 'large'];
9377
9614
  /**
9378
- * The `IconText` component provides a Bulma-styled horizontal arrangement of one or more `Icon` components and optional text.
9615
+ * The `Content` component applies Bulma’s typographic styles to its children, enhancing the appearance of HTML elements like paragraphs, headings, lists, and tables.
9379
9616
  *
9380
9617
  * @function
9381
- * @param {IconTextProps} props - Props for the IconText component.
9382
- * @returns {JSX.Element} The rendered icon text element.
9383
- * @see {@link https://bulma.io/documentation/elements/icon/#icon-text | Bulma IconText documentation}
9618
+ * @param {ContentProps} props - Props for the Content component.
9619
+ * @returns {JSX.Element} The rendered content block.
9620
+ * @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
9384
9621
  */
9385
- const IconTextComponent = ({ className, textColor, color, bgColor, iconProps, children, items, ...props }) => {
9622
+ const Content = ({ className, textColor, color, bgColor, size, children, ...props }) => {
9386
9623
  /**
9387
9624
  * Generates Bulma helper classes and separates out remaining props.
9388
9625
  */
@@ -9391,23 +9628,24 @@ const IconTextComponent = ({ className, textColor, color, bgColor, iconProps, ch
9391
9628
  backgroundColor: bgColor,
9392
9629
  ...props,
9393
9630
  });
9394
- const bulmaClasses = usePrefixedClassNames('icon-text');
9395
- const iconTextClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9396
- return (jsxRuntime.jsx("span", { className: iconTextClasses, ...rest, children: items ? (items.map((item, index) => (jsxRuntime.jsxs(React.Fragment, { children: [item.iconProps &&
9397
- (isIconProps(item.iconProps) ? (jsxRuntime.jsx(Icon, { ...item.iconProps })) : (jsxRuntime.jsx(Icon, { children: item.iconProps }))), item.text && jsxRuntime.jsx("span", { children: item.text })] }, index)))) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconProps &&
9398
- (isIconProps(iconProps) ? (jsxRuntime.jsx(Icon, { ...iconProps })) : (jsxRuntime.jsx(Icon, { children: iconProps }))), children && jsxRuntime.jsx("span", { children: children })] })) }));
9631
+ const bulmaClasses = usePrefixedClassNames('content', {
9632
+ [`is-${size}`]: size && size !== 'normal' && validSizes.includes(size),
9633
+ });
9634
+ const contentClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9635
+ return (jsxRuntime.jsx("div", { className: contentClasses, ...rest, children: children }));
9399
9636
  };
9400
- const IconText = withSubComponents(IconTextComponent, { Icon }, 'IconText');
9401
9637
 
9402
9638
  /**
9403
- * The `Image` component wraps images, iframes, or custom content in a Bulma-styled container, supporting fixed sizes, aspect ratios, rounded corners, retina images, and all Bulma helper props for color and spacing.
9639
+ * The `Delete` component provides a Bulma-styled close/delete button for dismissing modals, notifications, tags, messages, and more.
9640
+ *
9641
+ * It renders `type="button"`, so a delete button inside a form does not submit it.
9404
9642
  *
9405
9643
  * @function
9406
- * @param {ImageProps} props - Props for the Image component.
9407
- * @returns {JSX.Element} The rendered image element.
9408
- * @see {@link https://bulma.io/documentation/elements/image/ | Bulma Image documentation}
9644
+ * @param {DeleteProps} props - Props for the Delete component.
9645
+ * @returns {JSX.Element} The rendered delete button.
9646
+ * @see {@link https://bulma.io/documentation/elements/delete/ | Bulma Delete documentation}
9409
9647
  */
9410
- const Image = ({ as, className, textColor, color, bgColor, size, isRounded, isRetina, src, alt, children, ...props }) => {
9648
+ const Delete = ({ className, textColor, color, bgColor, onClick, size, ariaLabel, disabled = false, ...props }) => {
9411
9649
  /**
9412
9650
  * Generates Bulma helper classes and separates out remaining props.
9413
9651
  */
@@ -9416,310 +9654,209 @@ const Image = ({ as, className, textColor, color, bgColor, size, isRounded, isRe
9416
9654
  backgroundColor: bgColor,
9417
9655
  ...props,
9418
9656
  });
9419
- const bulmaClasses = usePrefixedClassNames('image', {
9657
+ const bulmaClasses = usePrefixedClassNames('delete', {
9420
9658
  [`is-${size}`]: size,
9421
- 'has-ratio': size && typeof size === 'string' && size.includes('by'),
9659
+ 'is-disabled': disabled,
9422
9660
  });
9423
- const imageClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9424
- // Default tag logic: if "as" is provided, use it.
9425
- // If not, use <figure> for aspect ratios or children, <div> otherwise.
9426
- let Tag;
9427
- if (as) {
9428
- Tag = as;
9429
- }
9430
- else if (size && typeof size === 'string' && size.includes('by')) {
9431
- Tag = 'figure';
9432
- }
9433
- else {
9434
- Tag = 'div';
9435
- }
9436
- const roundedClass = usePrefixedClassNames('is-rounded');
9437
- const content = children ? (children) : (jsxRuntime.jsx("img", { className: classNames({ [roundedClass]: isRounded }) || undefined, src: src, alt: alt, ...(isRetina && src ? { srcSet: `${src} 2x` } : {}) }));
9438
- return (jsxRuntime.jsx(Tag, { className: imageClasses, ...rest, children: content }));
9661
+ const classes = classNames(bulmaClasses, bulmaHelperClasses, className);
9662
+ return (jsxRuntime.jsx("button", { className: classes, onClick: onClick, disabled: disabled, ...rest, "aria-label": rest['aria-label'] || ariaLabel || 'Close', type: buttonType(rest.type) }));
9439
9663
  };
9440
9664
 
9441
9665
  /**
9442
- * How long after an item appears its announcement is written, in ms. The wait
9443
- * lets a screen reader register a region the container has only just mounted,
9444
- * and items shown in quick succession are written, and read out, together.
9666
+ * The `Divider` component renders a styled horizontal rule (`<hr>`) element with Bulma helper class integration.
9667
+ *
9668
+ * @function
9669
+ * @param {DividerProps} props - Props for the Divider component.
9670
+ * @returns {JSX.Element} The rendered hr element.
9445
9671
  */
9446
- const announceDelay = 100;
9672
+ const Divider = ({ className, bgColor, ...props }) => {
9673
+ /**
9674
+ * Generates Bulma helper classes and separates out remaining props.
9675
+ */
9676
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
9677
+ backgroundColor: bgColor,
9678
+ ...props,
9679
+ });
9680
+ const bulmaClasses = usePrefixedClassNames();
9681
+ const dividerClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9682
+ return jsxRuntime.jsx("hr", { className: dividerClasses || undefined, ...rest });
9683
+ };
9684
+
9447
9685
  /**
9448
- * How long an announcement stays in the region once written, in ms. That's
9449
- * long enough for a screen reader to pick it up. Clearing it after keeps a
9450
- * second copy of the item's text from sitting in the page, where someone
9451
- * reading through the page, or a test looking the text up, would find it
9452
- * again.
9686
+ * The `Emphasis` component renders a styled `<em>` element with Bulma helper class integration.
9687
+ *
9688
+ * @function
9689
+ * @param {EmphasisProps} props - Props for the Emphasis component.
9690
+ * @returns {JSX.Element} The rendered em element.
9453
9691
  */
9454
- const announcementLifetime = 1000;
9455
- const noAnnouncements = [];
9456
- const nothingWritten = [];
9457
- const noAnnouncementsYet = {
9458
- key: 0,
9459
- waiting: noAnnouncements,
9460
- written: nothingWritten,
9461
- };
9462
- // Hides the region with inline styles rather than a class, so it needs no
9463
- // stylesheet, including the helper classes a modular Bulma build can leave
9464
- // out. Clipping it, rather than using `display` or `visibility`, keeps it in
9465
- // the accessibility tree. The declarations match the `extras-sr-only` mixin,
9466
- // plus `clip-path`, which replaces the deprecated `clip`. The negative margin
9467
- // pulls the 1px box back inside the page, so it doesn't add scrollable
9468
- // overflow. Exported for other live regions that must work without a
9469
- // stylesheet, such as a range calendar's.
9470
- const visuallyHidden = {
9471
- position: 'absolute',
9472
- width: '1px',
9473
- height: '1px',
9474
- padding: 0,
9475
- margin: '-1px',
9476
- overflow: 'hidden',
9477
- clip: 'rect(0, 0, 0, 0)',
9478
- clipPath: 'inset(50%)',
9479
- whiteSpace: 'nowrap',
9480
- border: 0,
9692
+ const Emphasis = ({ className, textColor, bgColor, children, ...props }) => {
9693
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
9694
+ color: textColor,
9695
+ backgroundColor: bgColor,
9696
+ ...props,
9697
+ });
9698
+ const bulmaClasses = usePrefixedClassNames();
9699
+ const emphasisClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9700
+ return (jsxRuntime.jsx("em", { className: emphasisClasses || undefined, ...rest, children: children }));
9481
9701
  };
9702
+
9482
9703
  /**
9483
- * Works out what a status region says. Each item is announced once each time
9484
- * it appears, so one that goes and comes back is announced again. Its
9485
- * announcement is written `announceDelay` after it appears, or after the last
9486
- * item to join it when several appear in quick succession, alongside whatever
9487
- * the region still says. Once written, it stays for `announcementLifetime`,
9488
- * whatever is written after it and whether or not its item goes in the
9489
- * meantime. An item that goes before its announcement is written isn't
9490
- * announced.
9704
+ * FigureCaption component for rendering a styled figcaption element.
9491
9705
  *
9492
- * Nothing is written in the commit that mounts the region or the item. A
9493
- * screen reader reliably announces a polite live region whose content changes
9494
- * after the region is already in the page, and not always one that arrives
9495
- * with its content. By the time `describe` runs, an item's rendered text can
9496
- * be read.
9706
+ * A FigureCaption wraps the HTML `<figcaption>` element with Bulma helper class integration.
9707
+ * Use it inside Figure components to provide captions for images or other media.
9497
9708
  *
9498
- * @function useAnnouncements
9499
- * @param items - The items to announce, in the order they were shown.
9500
- * @param describe - The text to announce for an item, or nothing to skip it.
9501
- * It runs once the item is on screen, and it must keep its identity between
9502
- * renders (a module-level function or a `useCallback`), or every render
9503
- * re-checks the items.
9504
- * @returns The announcements to render, in the order they were written.
9709
+ * @function
9710
+ * @param {FigureCaptionProps} props - Props for the FigureCaption component.
9711
+ * @returns {JSX.Element} The rendered figcaption element.
9505
9712
  */
9506
- function useAnnouncements(items, describe) {
9507
- // The ids of the items on screen as of the last change, every one of which
9508
- // has been looked at for an announcement already.
9509
- const onScreenIdsRef = React.useRef(new Set());
9510
- // Clears that haven't run yet. They outlive the batch that started them, so
9511
- // they're only stopped when the region unmounts.
9512
- const clearTimersRef = React.useRef(new Set());
9513
- const [announcements, setAnnouncements] = React.useState(noAnnouncementsYet);
9514
- // Runs after the commit that puts an item on screen, which is the first
9515
- // point its rendered text can be read.
9516
- React.useEffect(() => {
9517
- const announced = onScreenIdsRef.current;
9518
- const appeared = items.filter(item => !announced.has(item.id));
9519
- // Items that have gone drop out here too, so one that comes back has
9520
- // appeared again.
9521
- onScreenIdsRef.current = new Set(items.map(item => item.id));
9522
- const next = [];
9523
- for (const item of appeared) {
9524
- const text = describe(item);
9525
- if (text)
9526
- next.push({ id: item.id, text });
9527
- }
9528
- if (next.length > 0) {
9529
- // eslint-disable-next-line react-hooks/set-state-in-effect -- an item's rendered text can only be read once it's on screen
9530
- setAnnouncements(current => ({
9531
- ...current,
9532
- key: current.key + 1,
9533
- // Announcements still waiting go out with the new ones, apart from
9534
- // one whose item came back before it was written, which the new one
9535
- // replaces. What's already written stays put.
9536
- waiting: [
9537
- ...current.waiting.filter(waiting => !next.some(({ id }) => id === waiting.id)),
9538
- ...next,
9539
- ],
9540
- }));
9541
- }
9542
- }, [items, describe]);
9543
- // The write only touches the batch it was started for: a newer batch can
9544
- // be set before the render that cancels this timer. Its clear is started
9545
- // by the write itself, so it's timed from the write without waiting on a
9546
- // render, and a newer batch arriving doesn't cut short what's written.
9547
- const { key } = announcements;
9548
- React.useEffect(() => {
9549
- if (key === noAnnouncementsYet.key)
9550
- return undefined;
9551
- const clearTimers = clearTimersRef.current;
9552
- const write = setTimeout(() => {
9553
- // Whether an item is on screen is decided here, once. An item that
9554
- // went before its announcement was due isn't announced, and one that
9555
- // goes after keeps its announcement for the rest of its lifetime, when
9556
- // the region holds the only copy of its text rather than a second one.
9557
- const onScreen = onScreenIdsRef.current;
9558
- setAnnouncements(current => current.key === key
9559
- ? {
9560
- ...current,
9561
- waiting: noAnnouncements,
9562
- written: [
9563
- ...current.written,
9564
- ...current.waiting
9565
- .filter(announcement => onScreen.has(announcement.id))
9566
- .map(announcement => ({ ...announcement, batch: key })),
9567
- ],
9568
- }
9569
- : current);
9570
- // eslint-disable-next-line @eslint-react/web-api-no-leaked-timeout -- it has to outlive this effect when a newer batch arrives, so the unmount effect below clears it
9571
- const clear = setTimeout(() => {
9572
- clearTimers.delete(clear);
9573
- setAnnouncements(current => {
9574
- const written = current.written.filter(announcement => announcement.batch !== key);
9575
- return written.length === current.written.length
9576
- ? current
9577
- : { ...current, written };
9578
- });
9579
- }, announcementLifetime);
9580
- clearTimers.add(clear);
9581
- }, announceDelay);
9582
- return () => clearTimeout(write);
9583
- }, [key]);
9584
- React.useEffect(() => {
9585
- const clearTimers = clearTimersRef.current;
9586
- return () => {
9587
- clearTimers.forEach(clearTimeout);
9588
- clearTimers.clear();
9589
- };
9590
- }, []);
9591
- return announcements.written;
9592
- }
9713
+ const FigureCaption = ({ className, textColor, bgColor, children, ...props }) => {
9714
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
9715
+ color: textColor,
9716
+ backgroundColor: bgColor,
9717
+ ...props,
9718
+ });
9719
+ const bulmaClasses = usePrefixedClassNames();
9720
+ const captionClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9721
+ return (jsxRuntime.jsx("figcaption", { className: captionClasses || undefined, ...rest, children: children }));
9722
+ };
9593
9723
  /**
9594
- * Reads an element out roughly the way a screen reader would: its text, with
9595
- * an element's `aria-label`, or an image's `alt`, standing in for what's
9596
- * inside it, a break between block-level elements, and parts that are
9597
- * `aria-hidden`, `hidden` or inline-styled `display: none` left out. It isn't
9598
- * the full accessible name computation, so `aria-labelledby`, CSS-generated
9599
- * content, content hidden by a stylesheet and other ways of naming or hiding
9600
- * content aren't followed.
9724
+ * The `Figure` component renders a styled `<figure>` element with Bulma helper class integration.
9601
9725
  *
9602
- * @function spokenText
9603
- * @param element - The element to read.
9604
- * @returns Its text, with runs of whitespace collapsed and the ends trimmed.
9726
+ * @function
9727
+ * @param {FigureProps} props - Props for the Figure component.
9728
+ * @returns {JSX.Element} The rendered figure element.
9605
9729
  */
9606
- function spokenText(element) {
9607
- return readAloud(element).replace(/\s+/g, ' ').trim();
9608
- }
9609
- // Elements a browser displays as blocks by default, and `br`. A screen reader
9610
- // pauses between them, so their text shouldn't run into the text beside them.
9611
- // They're known by tag rather than by computed `display`, which would depend
9612
- // on a stylesheet and on layout.
9613
- const blockLevelTags = new Set([
9614
- 'ADDRESS',
9615
- 'ARTICLE',
9616
- 'ASIDE',
9617
- 'BLOCKQUOTE',
9618
- 'BR',
9619
- 'CAPTION',
9620
- 'DD',
9621
- 'DETAILS',
9622
- 'DIALOG',
9623
- 'DIV',
9624
- 'DL',
9625
- 'DT',
9626
- 'FIELDSET',
9627
- 'FIGCAPTION',
9628
- 'FIGURE',
9629
- 'FOOTER',
9630
- 'FORM',
9631
- 'H1',
9632
- 'H2',
9633
- 'H3',
9634
- 'H4',
9635
- 'H5',
9636
- 'H6',
9637
- 'HEADER',
9638
- 'HGROUP',
9639
- 'HR',
9640
- 'LEGEND',
9641
- 'LI',
9642
- 'MAIN',
9643
- 'MENU',
9644
- 'NAV',
9645
- 'OL',
9646
- 'P',
9647
- 'PRE',
9648
- 'SEARCH',
9649
- 'SECTION',
9650
- 'SUMMARY',
9651
- 'TABLE',
9652
- 'TBODY',
9653
- 'TD',
9654
- 'TFOOT',
9655
- 'TH',
9656
- 'THEAD',
9657
- 'TR',
9658
- 'UL',
9659
- ]);
9730
+ const FigureComponent = ({ className, textColor, bgColor, children, ...props }) => {
9731
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
9732
+ color: textColor,
9733
+ backgroundColor: bgColor,
9734
+ ...props,
9735
+ });
9736
+ const bulmaClasses = usePrefixedClassNames();
9737
+ const figureClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9738
+ return (jsxRuntime.jsx("figure", { className: figureClasses || undefined, ...rest, children: children }));
9739
+ };
9660
9740
  /**
9661
- * Whether an element is left out of what a screen reader reads.
9741
+ * Figure component with Caption subcomponent.
9662
9742
  *
9663
- * @function isHidden
9664
- * @param element - The element to check.
9665
- * @returns True for `aria-hidden="true"`, the `hidden` attribute, and an
9666
- * inline `display: none`, on an HTML, SVG or any other element with an
9667
- * inline style.
9743
+ * @example
9744
+ * <Figure>
9745
+ * <img src="image.jpg" alt="Description" />
9746
+ * <Figure.Caption>Image caption text</Figure.Caption>
9747
+ * </Figure>
9668
9748
  */
9669
- const isHidden = (element) => element.getAttribute('aria-hidden') === 'true' ||
9670
- element.hasAttribute('hidden') ||
9671
- element.style?.display === 'none';
9749
+ const Figure = withSubComponents(FigureComponent, {
9750
+ Caption: FigureCaption,
9751
+ }, 'Figure');
9752
+
9672
9753
  /**
9673
- * The text `spokenText` reads for an element, before its whitespace is
9674
- * tidied.
9754
+ * Distinguishes an `IconProps` object from a plain custom node (an inline SVG, a
9755
+ * `react-icons` component, …) in a slot that accepts either — `Control`'s
9756
+ * `iconLeft`/`iconRight`, `IconText`'s `iconProps`.
9675
9757
  *
9676
- * @function readAloud
9677
- * @param element - The element to read.
9678
- * @returns Its text.
9758
+ * One copy, imported by both consumers: two hand-maintained copies, one per
9759
+ * consumer, is how the deprecated `icon` path came to crash both (#663).
9760
+ *
9761
+ * Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports
9762
+ * that file, and everything published there is public forever. `polymorphic.ts`
9763
+ * makes the same call for `isCustomElement`: an internal discriminator is not
9764
+ * something to support for the life of the package.
9765
+ *
9766
+ * It tests each member's VALUE, not merely its key, and only after everything
9767
+ * React owns is out of the way. `'icon' in value` was enough
9768
+ * to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,
9769
+ * path] }` — whose `icon` is a path array rather than a class string. That object
9770
+ * is not a renderable node either, so the honest outcome is the one this slot has
9771
+ * always had for it: the node branch, and React's own "Objects are not valid as a
9772
+ * React child". Claiming it here instead rendered a blank container that still
9773
+ * reserved layout.
9774
+ *
9775
+ * A member added to `IconProps` needs a line here, and a missed one is a crash
9776
+ * rather than a fallthrough. That is the cost of a slot taking either a props
9777
+ * object or a node; keeping the guard in one place is what keeps the cost to a
9778
+ * single line.
9679
9779
  */
9680
- function readAloud(element) {
9681
- if (isHidden(element))
9682
- return '';
9683
- const name = element.getAttribute('aria-label') ||
9684
- (element.tagName === 'IMG' ? element.getAttribute('alt') : null);
9685
- const text = name ||
9686
- Array.from(element.childNodes, child => {
9687
- if (child instanceof Element)
9688
- return readAloud(child);
9689
- return child.nodeType === Node.TEXT_NODE ? child.textContent : '';
9690
- }).join('');
9691
- // A name stands in for the element's content, and a block's content stands
9692
- // apart from what's around it, so both are padded to keep them from running
9693
- // into the neighboring text. An inline run, like `<span>Up</span>` then
9694
- // `<span>loaded</span>`, still reads as one word.
9695
- return name || blockLevelTags.has(element.tagName) ? ` ${text} ` : text;
9780
+ function isIconProps(value) {
9781
+ if (typeof value !== 'object' || value === null)
9782
+ return false;
9783
+ // Anything React owns. `isValidElement` covers elements only; a PORTAL is a
9784
+ // valid `ReactNode` that fails it and carries a `children` key, so the member
9785
+ // test below claimed it and `Control` spread it into `<Icon>` as props —
9786
+ // rendering the portal's child locally instead of in its container. Every
9787
+ // React internal carries `$$typeof`, so testing for it covers lazy, context
9788
+ // and whatever React adds next, rather than portals alone.
9789
+ if (React.isValidElement(value) || '$$typeof' in value)
9790
+ return false;
9791
+ const candidate = value;
9792
+ return (typeof candidate.name === 'string' ||
9793
+ typeof candidate.icon === 'string' ||
9794
+ 'children' in candidate);
9696
9795
  }
9796
+
9697
9797
  /**
9698
- * A visually hidden, polite `status` live region that a container announces
9699
- * its items through. The container renders it from the moment it mounts,
9700
- * whether or not it has anything to show, so the region is already in the
9701
- * page when an announcement is written into it.
9798
+ * The `IconText` component provides a Bulma-styled horizontal arrangement of one or more `Icon` components and optional text.
9702
9799
  *
9703
- * The announcements are this component's own state, so writing and clearing
9704
- * them re-renders the region and leaves the container's items alone. Each one
9705
- * is its own keyed node, so a re-render that leaves the announcements alone
9706
- * leaves the region's content alone too, and nothing is read out twice.
9800
+ * @function
9801
+ * @param {IconTextProps} props - Props for the IconText component.
9802
+ * @returns {JSX.Element} The rendered icon text element.
9803
+ * @see {@link https://bulma.io/documentation/elements/icon/#icon-text | Bulma IconText documentation}
9804
+ */
9805
+ const IconTextComponent = ({ className, textColor, color, bgColor, iconProps, children, items, ...props }) => {
9806
+ /**
9807
+ * Generates Bulma helper classes and separates out remaining props.
9808
+ */
9809
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
9810
+ color: textColor ?? color,
9811
+ backgroundColor: bgColor,
9812
+ ...props,
9813
+ });
9814
+ const bulmaClasses = usePrefixedClassNames('icon-text');
9815
+ const iconTextClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9816
+ return (jsxRuntime.jsx("span", { className: iconTextClasses, ...rest, children: items ? (items.map((item, index) => (jsxRuntime.jsxs(React.Fragment, { children: [item.iconProps &&
9817
+ (isIconProps(item.iconProps) ? (jsxRuntime.jsx(Icon, { ...item.iconProps })) : (jsxRuntime.jsx(Icon, { children: item.iconProps }))), item.text && jsxRuntime.jsx("span", { children: item.text })] }, index)))) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconProps &&
9818
+ (isIconProps(iconProps) ? (jsxRuntime.jsx(Icon, { ...iconProps })) : (jsxRuntime.jsx(Icon, { children: iconProps }))), children && jsxRuntime.jsx("span", { children: children })] })) }));
9819
+ };
9820
+ const IconText = withSubComponents(IconTextComponent, { Icon }, 'IconText');
9821
+
9822
+ /**
9823
+ * The `Image` component wraps images, iframes, or custom content in a Bulma-styled container, supporting fixed sizes, aspect ratios, rounded corners, retina images, and all Bulma helper props for color and spacing.
9707
9824
  *
9708
- * @function StatusRegion
9709
- * @param props - The items to announce, and the text to announce for each.
9710
- * @returns The region.
9825
+ * @function
9826
+ * @param {ImageProps} props - Props for the Image component.
9827
+ * @returns {JSX.Element} The rendered image element.
9828
+ * @see {@link https://bulma.io/documentation/elements/image/ | Bulma Image documentation}
9711
9829
  */
9712
- function StatusRegion({ items, describe, }) {
9713
- const announcements = useAnnouncements(items, describe);
9714
- // `role="status"` makes a region atomic unless it says otherwise, which
9715
- // reads the whole region on every change. Each announcement is its own
9716
- // node, and only additions are relevant by default, so with atomic off a
9717
- // screen reader reads just what was written, and removing one announcement
9718
- // doesn't read out the ones still there. A node is keyed by its batch as
9719
- // well as its item, since an item that comes back can be written again
9720
- // while what it said before is still there.
9721
- 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}`))) }));
9722
- }
9830
+ const Image = ({ as, className, textColor, color, bgColor, size, isRounded, isRetina, src, alt, children, ...props }) => {
9831
+ /**
9832
+ * Generates Bulma helper classes and separates out remaining props.
9833
+ */
9834
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
9835
+ color: textColor ?? color,
9836
+ backgroundColor: bgColor,
9837
+ ...props,
9838
+ });
9839
+ const bulmaClasses = usePrefixedClassNames('image', {
9840
+ [`is-${size}`]: size,
9841
+ 'has-ratio': size && typeof size === 'string' && size.includes('by'),
9842
+ });
9843
+ const imageClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9844
+ // Default tag logic: if "as" is provided, use it.
9845
+ // If not, use <figure> for aspect ratios or children, <div> otherwise.
9846
+ let Tag;
9847
+ if (as) {
9848
+ Tag = as;
9849
+ }
9850
+ else if (size && typeof size === 'string' && size.includes('by')) {
9851
+ Tag = 'figure';
9852
+ }
9853
+ else {
9854
+ Tag = 'div';
9855
+ }
9856
+ const roundedClass = usePrefixedClassNames('is-rounded');
9857
+ const content = children ? (children) : (jsxRuntime.jsx("img", { className: classNames({ [roundedClass]: isRounded }) || undefined, src: src, alt: alt, ...(isRetina && src ? { srcSet: `${src} 2x` } : {}) }));
9858
+ return (jsxRuntime.jsx(Tag, { className: imageClasses, ...rest, children: content }));
9859
+ };
9723
9860
 
9724
9861
  /**
9725
9862
  * Keys pressed together or one after another, in the outer `<kbd>` HTML gives a combination, with the separator between each pair as text.
@@ -11333,9 +11470,9 @@ function useAutoLabelId({ label, id, labelProps, rendersLabel, hasInput = true,
11333
11470
  ? (labelProps?.id ?? `${generatedId}-label`)
11334
11471
  : undefined;
11335
11472
  // Inactive with a label still means an own Field may render it (pickers'
11336
- // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`
11337
- // tells Field the association is owned here, so it must not generate one
11338
- // that would dangle (#495 presence semantics).
11473
+ // inline mode). The explicit `htmlFor: undefined` tells Field the
11474
+ // association is owned here, so it must not generate one that would
11475
+ // dangle (#495 presence semantics).
11339
11476
  const fieldLabelProps = active
11340
11477
  ? ownLabelId
11341
11478
  ? { ...ownLabelProps, id: ownLabelId }
@@ -12782,7 +12919,7 @@ const keepFocus = (e) => e.preventDefault();
12782
12919
  // An IME that's composing owns every key: Enter confirms what it composed,
12783
12920
  // and the arrows move through its candidates. Safari can say so with
12784
12921
  // `keyCode` 229 rather than `isComposing`.
12785
- const isComposing = (e) => e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229;
12922
+ const isComposing$1 = (e) => e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229;
12786
12923
  // Whether an option can be picked. The arrows still reach a disabled one, so
12787
12924
  // a screen reader user finds it.
12788
12925
  const isEnabled = (item) => typeof item === 'string' || !item.disabled;
@@ -12945,7 +13082,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
12945
13082
  }, [keepFirst, isActive, filteredData.length, inputValue]);
12946
13083
  // Handle keyboard navigation
12947
13084
  const handleKeyDown = React.useCallback((e) => {
12948
- if (disabled || isComposing(e))
13085
+ if (disabled || isComposing$1(e))
12949
13086
  return;
12950
13087
  switch (e.key) {
12951
13088
  case 'ArrowDown':
@@ -13130,6 +13267,10 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13130
13267
  });
13131
13268
  Autocomplete.displayName = 'Autocomplete';
13132
13269
 
13270
+ // An IME that's composing owns every key: Enter confirms what it composed,
13271
+ // the arrows move through its candidates, and Escape cancels it. Safari can
13272
+ // say so with `keyCode` 229 rather than `isComposing` (#1102).
13273
+ const isComposing = (e) => e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229;
13133
13274
  /**
13134
13275
  * The `Taginput` component provides a tag/chip input field for managing multiple tags.
13135
13276
  *
@@ -13178,7 +13319,9 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13178
13319
  const [inputValue, setInputValue] = React.useState('');
13179
13320
  const [isActive, setIsActive] = React.useState(false);
13180
13321
  const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
13181
- const listboxId = `${React.useId()}-listbox`;
13322
+ const baseId = React.useId();
13323
+ const listboxId = `${baseId}-listbox`;
13324
+ const counterId = `${baseId}-counter`;
13182
13325
  // With `data` the input offers suggestions, so it's a combobox, even
13183
13326
  // while none match. Without it there's never a list, so it stays a plain
13184
13327
  // text field.
@@ -13187,16 +13330,13 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13187
13330
  // Use controlled or internal tags
13188
13331
  const tags = controlledValue !== undefined ? controlledValue : internalTags;
13189
13332
  const isMaxReached = maxTags !== undefined && tags.length >= maxTags;
13190
- // At the tag limit the text input is not rendered, so there is nothing to
13191
- // wire a label to, its own or a surrounding Field's. The suggestion list
13192
- // points `aria-labelledby` at the label that names the input, so with
13193
- // suggestions it needs that label's id.
13333
+ // The suggestion list points `aria-labelledby` at the label that names
13334
+ // the input, so with suggestions it needs that label's id.
13194
13335
  const { controlId, fieldLabelProps, labelled, labelId: listLabelId, } = useAutoLabelId({
13195
13336
  label,
13196
13337
  id,
13197
13338
  labelProps,
13198
- rendersLabel: ownField && !isMaxReached,
13199
- hasInput: !isMaxReached,
13339
+ rendersLabel: ownField,
13200
13340
  needsLabelId: hasSuggestions,
13201
13341
  });
13202
13342
  // Get display value from tag
@@ -13217,9 +13357,8 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13217
13357
  const hasInput = inputValue || (openOnFocus && isActive);
13218
13358
  return matchesInput && notAlreadyAdded && hasInput;
13219
13359
  });
13220
- // At the tag limit the input isn't rendered, so the list isn't either.
13221
- // Left open it would be a listbox with no combobox, offering tags past
13222
- // the limit (#1087).
13360
+ // At the tag limit the list stays closed, since it could only offer tags
13361
+ // past the limit (#1087).
13223
13362
  const listOpen = isActive && !isMaxReached && filteredData.length > 0;
13224
13363
  // keepFirst: auto-highlight first item when dropdown opens
13225
13364
  React.useEffect(() => {
@@ -13234,36 +13373,42 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13234
13373
  }
13235
13374
  onChange?.(newTags);
13236
13375
  }, [controlledValue, onChange]);
13237
- // Add a tag from text typed, pasted or picked from the list. Only
13238
- // `createTag` turns it into an object.
13239
- const addTag = React.useCallback((value) => {
13376
+ // Add tags from text typed, pasted or picked from the list. Each one is
13377
+ // checked against the tags before it, this batch's included, so a paste
13378
+ // of several adds them all in one change (#1101). Only `createTag` turns
13379
+ // text into an object.
13380
+ const addTags = React.useCallback((values) => {
13240
13381
  if (disabled || readonly)
13241
13382
  return;
13242
- const displayValue = value.trim();
13243
- if (!displayValue)
13244
- return;
13245
- // No maxTags check: only the input and the list call this, neither
13246
- // renders at the limit, and each call adds at most one tag.
13247
- // Check duplicates
13248
- if (!allowDuplicates) {
13249
- const exists = tags.some(tag => getDisplayValue(tag).toLowerCase() === displayValue.toLowerCase());
13250
- if (exists)
13251
- return;
13383
+ let next = tags;
13384
+ const added = [];
13385
+ for (const value of values) {
13386
+ // A paste can bring more tags than there's room for, and at the
13387
+ // limit the read-only input still takes Enter.
13388
+ if (maxTags !== undefined && next.length >= maxTags)
13389
+ break;
13390
+ const displayValue = value.trim();
13391
+ if (!displayValue)
13392
+ continue;
13393
+ const lower = displayValue.toLowerCase();
13394
+ // Duplicates, data and the dropdown filter all compare
13395
+ // case-insensitively.
13396
+ if (!allowDuplicates &&
13397
+ next.some(tag => getDisplayValue(tag).toLowerCase() === lower))
13398
+ continue;
13399
+ // Without allowNew, only a suggestion can become a tag.
13400
+ if (!allowNew && !data.some(d => d.toLowerCase() === lower))
13401
+ continue;
13402
+ if (beforeAdding && !beforeAdding(displayValue))
13403
+ continue;
13404
+ const tag = createTag ? createTag(displayValue) : displayValue;
13405
+ next = [...next, tag];
13406
+ added.push(tag);
13252
13407
  }
13253
- // Check if allowed (must be in data if allowNew is false). Compare
13254
- // case-insensitively, consistent with the dropdown filter and the
13255
- // duplicate check above.
13256
- if (!allowNew &&
13257
- !data.some(d => d.toLowerCase() === displayValue.toLowerCase()))
13258
- return;
13259
- // beforeAdding validation
13260
- if (beforeAdding && !beforeAdding(displayValue))
13408
+ if (added.length === 0)
13261
13409
  return;
13262
- // createTag transformation
13263
- const finalTag = createTag ? createTag(displayValue) : displayValue;
13264
- const newTags = [...tags, finalTag];
13265
- updateTags(newTags);
13266
- onAdd?.(finalTag);
13410
+ updateTags(next);
13411
+ added.forEach(tag => onAdd?.(tag));
13267
13412
  setInputValue('');
13268
13413
  if (!keepOpen) {
13269
13414
  setIsActive(false);
@@ -13273,6 +13418,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13273
13418
  disabled,
13274
13419
  readonly,
13275
13420
  tags,
13421
+ maxTags,
13276
13422
  allowDuplicates,
13277
13423
  allowNew,
13278
13424
  data,
@@ -13301,7 +13447,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13301
13447
  if (confirmKeys.includes(lastChar) && lastChar !== 'Enter') {
13302
13448
  const valueWithoutKey = newValue.slice(0, -1).trim();
13303
13449
  if (valueWithoutKey) {
13304
- addTag(valueWithoutKey);
13450
+ addTags([valueWithoutKey]);
13305
13451
  }
13306
13452
  return;
13307
13453
  }
@@ -13310,7 +13456,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13310
13456
  if (newValue && data.length > 0) {
13311
13457
  setIsActive(true);
13312
13458
  }
13313
- }, [confirmKeys, addTag, onTyping, data.length]);
13459
+ }, [confirmKeys, addTags, onTyping, data.length]);
13314
13460
  // Handle paste
13315
13461
  const handlePaste = React.useCallback((e) => {
13316
13462
  if (onPasteSeparators.length === 0)
@@ -13321,17 +13467,12 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13321
13467
  if (!hasSeparator)
13322
13468
  return;
13323
13469
  e.preventDefault();
13324
- // Build a regex from all separators
13325
- const escapedSeparators = onPasteSeparators.map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
13470
+ // Build a character class from all separators. A `-` is escaped
13471
+ // too, since between two others it would make a range.
13472
+ const escapedSeparators = onPasteSeparators.map(s => s.replace(/[-.*+?^${}()|[\]\\]/g, '\\$&'));
13326
13473
  const regex = new RegExp(`[${escapedSeparators.join('')}]`);
13327
- const parts = pastedText
13328
- .split(regex)
13329
- .map(s => s.trim())
13330
- .filter(Boolean);
13331
- for (const part of parts) {
13332
- addTag(part);
13333
- }
13334
- }, [onPasteSeparators, addTag]);
13474
+ addTags(pastedText.split(regex));
13475
+ }, [onPasteSeparators, addTags]);
13335
13476
  // Handle focus
13336
13477
  const handleFocus = React.useCallback(() => {
13337
13478
  if (openOnFocus && data.length > 0 && !disabled) {
@@ -13364,16 +13505,16 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13364
13505
  }, [isActive]);
13365
13506
  // Handle keyboard
13366
13507
  const handleKeyDown = React.useCallback((e) => {
13367
- if (disabled || readonly)
13508
+ if (disabled || readonly || isComposing(e))
13368
13509
  return;
13369
13510
  // Handle confirm keys
13370
13511
  if (confirmKeys.includes(e.key)) {
13371
13512
  e.preventDefault();
13372
13513
  if (highlightedIndex >= 0 && filteredData[highlightedIndex]) {
13373
- addTag(filteredData[highlightedIndex]);
13514
+ addTags([filteredData[highlightedIndex]]);
13374
13515
  }
13375
13516
  else if (inputValue.trim()) {
13376
- addTag(inputValue);
13517
+ addTags([inputValue]);
13377
13518
  }
13378
13519
  return;
13379
13520
  }
@@ -13400,7 +13541,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13400
13541
  case 'Escape':
13401
13542
  // Closing the suggestions claims the key, so a Modal around the
13402
13543
  // field stays open. With none showing, the key goes on to it.
13403
- if (isActive && filteredData.length > 0)
13544
+ if (listOpen)
13404
13545
  e.preventDefault();
13405
13546
  setIsActive(false);
13406
13547
  setHighlightedIndex(-1);
@@ -13412,9 +13553,10 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13412
13553
  confirmKeys,
13413
13554
  highlightedIndex,
13414
13555
  filteredData,
13415
- addTag,
13556
+ addTags,
13416
13557
  inputValue,
13417
13558
  isActive,
13559
+ listOpen,
13418
13560
  data.length,
13419
13561
  removeOnKeys,
13420
13562
  tags.length,
@@ -13500,9 +13642,13 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13500
13642
  e.stopPropagation();
13501
13643
  removeTag(index);
13502
13644
  }, "aria-label": ariaCloseLabel || `Remove ${displayVal}` }))] }, index));
13503
- }), !isMaxReached && (jsxRuntime.jsx("input", { ref: combinedRef, type: "text", className: inputClasses, id: controlId, value: inputValue, placeholder: tags.length === 0 ? placeholder : undefined, disabled: disabled, readOnly: readonly, maxLength: maxlength, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, onPaste: handlePaste, ...(hasSuggestions && comboboxProps), "aria-label": labelled ? undefined : 'Add tag' }))] }), loading && (jsxRuntime.jsx("span", { className: iconRightClass, children: jsxRuntime.jsx("span", { className: loaderClass }) }))] }), listOpen && (jsxRuntime.jsx("div", { className: dropdownMenuClasses, children: jsxRuntime.jsx("div", { ref: dropdownRef, id: listboxId, className: dropdownContentClasses, role: "listbox", "aria-labelledby": listLabelId, "aria-label": listLabelId ? undefined : 'Suggestions', children: filteredData.map((item, index) => (jsxRuntime.jsx("a", { id: optionId(index), className: pcn('dropdown-item', {
13645
+ }), jsxRuntime.jsx("input", { ref: combinedRef, type: "text", className: inputClasses, id: controlId, value: inputValue, placeholder: tags.length === 0 ? placeholder : undefined, disabled: disabled,
13646
+ // Read-only at the tag limit rather than gone, so focus stays
13647
+ // put and a label keeps naming it, and Backspace still makes
13648
+ // room (#1103).
13649
+ readOnly: readonly || isMaxReached, maxLength: maxlength, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, onPaste: handlePaste, ...(hasSuggestions && comboboxProps), "aria-label": labelled ? undefined : 'Add tag', "aria-describedby": showCounter ? counterId : undefined })] }), loading && (jsxRuntime.jsx("span", { className: iconRightClass, children: jsxRuntime.jsx("span", { className: loaderClass }) }))] }), listOpen && (jsxRuntime.jsx("div", { className: dropdownMenuClasses, children: jsxRuntime.jsx("div", { ref: dropdownRef, id: listboxId, className: dropdownContentClasses, role: "listbox", "aria-labelledby": listLabelId, "aria-label": listLabelId ? undefined : 'Suggestions', children: filteredData.map((item, index) => (jsxRuntime.jsx("a", { id: optionId(index), className: pcn('dropdown-item', {
13504
13650
  'is-active': index === highlightedIndex,
13505
- }), onClick: () => addTag(item), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": optionId(index) === activeOptionId, children: item }, index))) }) })), showCounter && jsxRuntime.jsx("small", { className: counterClasses, children: counterText }), name &&
13651
+ }), onClick: () => addTags([item]), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": optionId(index) === activeOptionId, children: item }, index))) }) })), showCounter && (jsxRuntime.jsx("small", { id: counterId, className: counterClasses, children: counterText })), name &&
13506
13652
  tags.map((tag, i) => (jsxRuntime.jsx("input", { type: "hidden", name: name, value: getDisplayValue(tag), form: form }, `tag-input-${i}`)))] }));
13507
13653
  if (ownField) {
13508
13654
  return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [taginputElement, messageEl] }));
@@ -19965,6 +20111,8 @@ exports.Thead = Thead;
19965
20111
  exports.Theme = Theme;
19966
20112
  exports.TimeInput = TimeInput;
19967
20113
  exports.TimeInputBase = TimeInputBase;
20114
+ exports.Timeline = Timeline;
20115
+ exports.TimelineItem = TimelineItem;
19968
20116
  exports.Title = Title;
19969
20117
  exports.Toast = Toast;
19970
20118
  exports.ToastContainer = ToastContainer;