@allxsmith/bestax-bulma 5.35.1 → 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 (40) 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/form/Autocomplete.js +2 -2
  8. package/dist/esm/form/Autocomplete.js.map +1 -1
  9. package/dist/esm/form/Taginput.js +64 -55
  10. package/dist/esm/form/Taginput.js.map +1 -1
  11. package/dist/esm/form/useAutoLabelId.js +3 -3
  12. package/dist/esm/form/useAutoLabelId.js.map +1 -1
  13. package/dist/extras.css +1 -1
  14. package/dist/extras.css.map +1 -1
  15. package/dist/index.cjs +896 -747
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.esm.js +1 -0
  18. package/dist/index.esm.js.map +1 -1
  19. package/dist/types/components/Timeline.d.ts +130 -0
  20. package/dist/types/form/Taginput.d.ts +12 -5
  21. package/dist/types/index.d.ts +1 -0
  22. package/dist/types-cjs/components/Timeline.d.ts +130 -0
  23. package/dist/types-cjs/form/Taginput.d.ts +12 -5
  24. package/dist/types-cjs/index.d.ts +1 -0
  25. package/dist/versions/bestax-no-dark-mode.css +1 -1
  26. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  27. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  28. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  29. package/dist/versions/bestax-no-helpers.css +1 -1
  30. package/dist/versions/bestax-no-helpers.css.map +1 -1
  31. package/dist/versions/bestax-prefixed.css +1 -1
  32. package/dist/versions/bestax-prefixed.css.map +1 -1
  33. package/package.json +1 -1
  34. package/src/scss/components/_index.scss +2 -0
  35. package/src/scss/components/_loading.scss +26 -0
  36. package/src/scss/components/_menu.scss +37 -0
  37. package/src/scss/components/_timeline.scss +371 -0
  38. package/src/scss/form/_checkbox.scss +14 -14
  39. package/src/scss/form/_radio.scss +12 -12
  40. package/src/scss/form/_switch.scss +21 -21
package/dist/index.cjs CHANGED
@@ -3473,7 +3473,7 @@ const EmptyStateActions = ({ className, ...props }) => {
3473
3473
  };
3474
3474
  EmptyStateActions.displayName = 'EmptyState.Actions';
3475
3475
  /** Whether a shorthand prop was given something to render. */
3476
- const isGiven = (node) => node !== undefined &&
3476
+ const isGiven$1 = (node) => node !== undefined &&
3477
3477
  node !== null &&
3478
3478
  typeof node !== 'boolean' &&
3479
3479
  node !== '';
@@ -3520,7 +3520,7 @@ const EmptyStateRoot = React.forwardRef(({ className, icon, title, titleAs = 'h2
3520
3520
  });
3521
3521
  // The props' parts first, each only when it's given, then the children
3522
3522
  // exactly as they were written: nothing here looks at them.
3523
- 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] }));
3524
3524
  });
3525
3525
  const EmptyState = withSubComponents(EmptyStateRoot, {
3526
3526
  Indicator: EmptyStateIndicator,
@@ -7834,215 +7834,636 @@ const StepsComponent = ({ value = 0, items, size, color, hasMarker = true, anima
7834
7834
  const Steps = withSubComponents(StepsComponent, { Step }, 'Steps');
7835
7835
 
7836
7836
  /**
7837
- * The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.
7838
- *
7839
- * @function
7840
- * @param {SidebarProps} props - Props for the Sidebar component.
7841
- * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the sidebar element.
7842
- * @returns {JSX.Element | null} The rendered sidebar component.
7843
- *
7844
- * @example
7845
- * // Basic sidebar
7846
- * const [isOpen, setIsOpen] = useState(false);
7847
- * <Sidebar isOpen={isOpen} onClose={() => setIsOpen(false)}>
7848
- * <Menu>...</Menu>
7849
- * </Sidebar>
7850
- *
7851
- * @example
7852
- * // Right-side sidebar with custom width
7853
- * <Sidebar
7854
- * isOpen={isOpen}
7855
- * onClose={() => setIsOpen(false)}
7856
- * position="right"
7857
- * width="320px"
7858
- * >
7859
- * <div>Panel content</div>
7860
- * </Sidebar>
7861
- */
7862
- 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) => {
7863
- const { bulmaHelperClasses, rest } = useBulmaClasses(props);
7864
- const resolveContainer = usePortalContainer();
7865
- const { portalContainer } = useConfig();
7866
- // Where the configured default puts the sidebar. An `inline` one doesn't
7867
- // portal, so a change to the default moves nothing.
7868
- const defaultTarget = inline ? undefined : portalContainer;
7869
- const sidebarRef = React.useRef(null);
7870
- // Cleanup returned by a consumer's callback ref on attach, held until detach.
7871
- const consumerCleanupRef = React.useRef(null);
7872
- const resolvedFullwidth = isFullwidth ?? fullWidth ?? false;
7873
- // Close handler
7874
- const handleClose = React.useCallback(() => {
7875
- if (canCancel && onClose) {
7876
- onClose();
7877
- }
7878
- }, [canCancel, onClose]);
7879
- // Handle overlay click
7880
- const handleOverlayClick = React.useCallback(() => {
7881
- if (overlayClose) {
7882
- handleClose();
7883
- }
7884
- }, [overlayClose, handleClose]);
7885
- // Read when Escape arrives, so the key listener stays put while
7886
- // `onClose` changes identity, as an inline one does on every render of
7887
- // the parent.
7888
- const handleCloseRef = useLatestRef(handleClose);
7889
- // Joins the stack of open overlays, so the sidebar answers Escape only
7890
- // while it's the topmost overlay, and keeps it from those under it.
7891
- const isTopmost = useOverlayStack(isOpen);
7892
- const closesOnEscape = escapeClose && canCancel && Boolean(onClose);
7893
- // Handle escape key. On `window`, as in `Modal`, so everything inside the
7894
- // sidebar has had the key first, and an Escape it used is left to it. One
7895
- // the sidebar closes on is marked used, so nothing under it acts on it.
7896
- React.useEffect(() => {
7897
- if (!isOpen || !closesOnEscape)
7898
- return;
7899
- const handleKeyDown = (e) => {
7900
- if (e.key !== 'Escape' || e.defaultPrevented || !isTopmost())
7901
- return;
7902
- e.preventDefault();
7903
- handleCloseRef.current();
7904
- };
7905
- window.addEventListener('keydown', handleKeyDown);
7906
- return () => window.removeEventListener('keydown', handleKeyDown);
7907
- }, [isOpen, closesOnEscape, handleCloseRef, isTopmost]);
7908
- // Prevent body scroll when sidebar is open. Ref-counted through the shared
7909
- // helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.
7910
- useScrollLock(isOpen && overlay);
7911
- // Focus trap (basic - focus sidebar when opened). A different
7912
- // `defaultTarget` remounts the portaled sidebar, so it focuses the new
7913
- // nodes then too.
7914
- React.useEffect(() => {
7915
- if (isOpen) {
7916
- const element = sidebarRef.current;
7917
- /* istanbul ignore else: the aside renders whenever the sidebar is open, so its ref is set before this runs */
7918
- if (element) {
7919
- // Focus first focusable element or sidebar itself
7920
- const focusable = element.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
7921
- if (focusable) {
7922
- focusable.focus();
7923
- }
7924
- else {
7925
- element.focus();
7926
- }
7927
- }
7928
- }
7929
- }, [isOpen, defaultTarget]);
7930
- // Use combined ref
7931
- const combinedRef = React.useCallback((node) => {
7932
- sidebarRef.current =
7933
- node;
7934
- if (typeof ref === 'function') {
7935
- // React 19 lets a callback ref return a cleanup function and detaches by
7936
- // running it instead of calling the ref with `null`; React 18 discards the
7937
- // return value entirely. Returning it from here would be a React-19-only
7938
- // contract, so we hold the cleanup and run it ourselves on detach — the
7939
- // same shape as `Dropdown`, `Modal`, `Dialog`, `Toast` and `Carousel`.
7940
- // The form controls that merge a forwarded ref still discard the cleanup
7941
- // and detach with `ref(null)`, so this is not yet library-wide — grep
7942
- // `typeof ref === 'function'` for the current split.
7943
- if (node === null) {
7944
- const consumerCleanup = consumerCleanupRef.current;
7945
- consumerCleanupRef.current = null;
7946
- if (consumerCleanup) {
7947
- consumerCleanup();
7948
- }
7949
- else {
7950
- ref(null);
7951
- }
7952
- return;
7953
- }
7954
- const cleanup = ref(node);
7955
- consumerCleanupRef.current =
7956
- typeof cleanup === 'function' ? cleanup : null;
7957
- }
7958
- else if (ref) {
7959
- ref.current = node;
7960
- }
7961
- }, [ref]);
7962
- // Generate classes
7963
- const sidebarClasses = usePrefixedClassNames('sidebar', {
7964
- 'is-active': isOpen,
7965
- [`is-${position}`]: position,
7966
- 'is-fullwidth': resolvedFullwidth,
7967
- });
7968
- const backgroundClass = usePrefixedClassNames('sidebar-background', {
7969
- 'is-active': isOpen,
7970
- });
7971
- const contentClass = usePrefixedClassNames('sidebar-content');
7972
- const combinedClasses = classNames(sidebarClasses, bulmaHelperClasses, className);
7973
- // Custom style for width
7974
- const sidebarStyle = {
7975
- ...style,
7976
- '--bulma-sidebar-width': resolvedFullwidth ? '100%' : width,
7977
- };
7978
- 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 }) })] }));
7979
- if (inline) {
7980
- return sidebarContent;
7981
- }
7982
- // Render to portal for proper stacking
7983
- if (typeof document !== 'undefined') {
7984
- return reactDom.createPortal(sidebarContent, resolveContainer());
7985
- }
7986
- return null;
7987
- });
7988
- /**
7989
- * Container for the sidebar header.
7990
- *
7991
- * @function
7992
- * @param {SidebarHeaderProps} props - Props for the SidebarHeader component.
7993
- * @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.
7994
7840
  */
7995
- const SidebarHeader = ({ className, children, ...props }) => {
7996
- const headerClass = usePrefixedClassNames('sidebar-header');
7997
- return (jsxRuntime.jsx("div", { className: classNames(headerClass, className), ...props, children: children }));
7998
- };
7841
+ const announceDelay = 100;
7999
7842
  /**
8000
- * Title text inside the header.
8001
- *
8002
- * @function
8003
- * @param {SidebarTitleProps} props - Props for the SidebarTitle component.
8004
- * @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.
8005
7848
  */
8006
- const SidebarTitle = ({ className, children, ...props }) => {
8007
- const titleClass = usePrefixedClassNames('sidebar-title');
8008
- 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,
8009
7856
  };
8010
- /**
8011
- * Close button for the sidebar.
8012
- *
8013
- * @function
8014
- * @param {SidebarCloseProps} props - Props for the SidebarClose component.
8015
- * @returns {JSX.Element} The rendered close button.
8016
- */
8017
- const SidebarClose = ({ className, children, ...props }) => {
8018
- const closeClass = usePrefixedClassNames('sidebar-close');
8019
- 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,
8020
7876
  };
8021
7877
  /**
8022
- * 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.
8023
7886
  *
8024
- * @function
8025
- * @param {SidebarBodyProps} props - Props for the SidebarBody component.
8026
- * @returns {JSX.Element} The rendered sidebar body.
8027
- */
8028
- const SidebarBody = ({ className, children, ...props }) => {
8029
- const bodyClass = usePrefixedClassNames('sidebar-body');
8030
- return (jsxRuntime.jsx("div", { className: classNames(bodyClass, className), ...props, children: children }));
8031
- };
8032
- /**
8033
- * 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.
8034
7892
  *
8035
- * @function
8036
- * @param {SidebarFooterProps} props - Props for the SidebarFooter component.
8037
- * @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.
8038
7900
  */
8039
- const SidebarFooter = ({ className, children, ...props }) => {
8040
- const footerClass = usePrefixedClassNames('sidebar-footer');
8041
- return (jsxRuntime.jsx("div", { className: classNames(footerClass, className), ...props, children: children }));
8042
- };
8043
- // Attach static subcomponents
8044
- const Sidebar = withSubComponents(SidebarComponent, {
8045
- Header: SidebarHeader,
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
8465
+ const Sidebar = withSubComponents(SidebarComponent, {
8466
+ Header: SidebarHeader,
8046
8467
  Title: SidebarTitle,
8047
8468
  Close: SidebarClose,
8048
8469
  Body: SidebarBody,
@@ -9180,208 +9601,25 @@ const Box = ({ className, textColor, color, bgColor, hasShadow = true, children,
9180
9601
  const Code = ({ className, textColor, bgColor, children, ...props }) => {
9181
9602
  const { bulmaHelperClasses, rest } = useBulmaClasses({
9182
9603
  color: textColor,
9183
- backgroundColor: bgColor,
9184
- ...props,
9185
- });
9186
- const bulmaClasses = usePrefixedClassNames();
9187
- const codeClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9188
- return (jsxRuntime.jsx("code", { className: codeClasses || undefined, ...rest, children: children }));
9189
- };
9190
-
9191
- // Valid size modifiers for the content class
9192
- const validSizes = ['small', 'medium', 'large'];
9193
- /**
9194
- * The `Content` component applies Bulma’s typographic styles to its children, enhancing the appearance of HTML elements like paragraphs, headings, lists, and tables.
9195
- *
9196
- * @function
9197
- * @param {ContentProps} props - Props for the Content component.
9198
- * @returns {JSX.Element} The rendered content block.
9199
- * @see {@link https://bulma.io/documentation/elements/content/ | Bulma Content documentation}
9200
- */
9201
- const Content = ({ className, textColor, color, bgColor, size, children, ...props }) => {
9202
- /**
9203
- * Generates Bulma helper classes and separates out remaining props.
9204
- */
9205
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9206
- color: textColor ?? color,
9207
- backgroundColor: bgColor,
9208
- ...props,
9209
- });
9210
- const bulmaClasses = usePrefixedClassNames('content', {
9211
- [`is-${size}`]: size && size !== 'normal' && validSizes.includes(size),
9212
- });
9213
- const contentClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9214
- return (jsxRuntime.jsx("div", { className: contentClasses, ...rest, children: children }));
9215
- };
9216
-
9217
- /**
9218
- * The `Delete` component provides a Bulma-styled close/delete button for dismissing modals, notifications, tags, messages, and more.
9219
- *
9220
- * It renders `type="button"`, so a delete button inside a form does not submit it.
9221
- *
9222
- * @function
9223
- * @param {DeleteProps} props - Props for the Delete component.
9224
- * @returns {JSX.Element} The rendered delete button.
9225
- * @see {@link https://bulma.io/documentation/elements/delete/ | Bulma Delete documentation}
9226
- */
9227
- const Delete = ({ className, textColor, color, bgColor, onClick, size, ariaLabel, disabled = false, ...props }) => {
9228
- /**
9229
- * Generates Bulma helper classes and separates out remaining props.
9230
- */
9231
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9232
- color: textColor ?? color,
9233
- backgroundColor: bgColor,
9234
- ...props,
9235
- });
9236
- const bulmaClasses = usePrefixedClassNames('delete', {
9237
- [`is-${size}`]: size,
9238
- 'is-disabled': disabled,
9239
- });
9240
- const classes = classNames(bulmaClasses, bulmaHelperClasses, className);
9241
- return (jsxRuntime.jsx("button", { className: classes, onClick: onClick, disabled: disabled, ...rest, "aria-label": rest['aria-label'] || ariaLabel || 'Close', type: buttonType(rest.type) }));
9242
- };
9243
-
9244
- /**
9245
- * The `Divider` component renders a styled horizontal rule (`<hr>`) element with Bulma helper class integration.
9246
- *
9247
- * @function
9248
- * @param {DividerProps} props - Props for the Divider component.
9249
- * @returns {JSX.Element} The rendered hr element.
9250
- */
9251
- const Divider = ({ className, bgColor, ...props }) => {
9252
- /**
9253
- * Generates Bulma helper classes and separates out remaining props.
9254
- */
9255
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9256
- backgroundColor: bgColor,
9257
- ...props,
9258
- });
9259
- const bulmaClasses = usePrefixedClassNames();
9260
- const dividerClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9261
- return jsxRuntime.jsx("hr", { className: dividerClasses || undefined, ...rest });
9262
- };
9263
-
9264
- /**
9265
- * The `Emphasis` component renders a styled `<em>` element with Bulma helper class integration.
9266
- *
9267
- * @function
9268
- * @param {EmphasisProps} props - Props for the Emphasis component.
9269
- * @returns {JSX.Element} The rendered em element.
9270
- */
9271
- const Emphasis = ({ className, textColor, bgColor, children, ...props }) => {
9272
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9273
- color: textColor,
9274
- backgroundColor: bgColor,
9275
- ...props,
9276
- });
9277
- const bulmaClasses = usePrefixedClassNames();
9278
- const emphasisClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9279
- return (jsxRuntime.jsx("em", { className: emphasisClasses || undefined, ...rest, children: children }));
9280
- };
9281
-
9282
- /**
9283
- * FigureCaption component for rendering a styled figcaption element.
9284
- *
9285
- * A FigureCaption wraps the HTML `<figcaption>` element with Bulma helper class integration.
9286
- * Use it inside Figure components to provide captions for images or other media.
9287
- *
9288
- * @function
9289
- * @param {FigureCaptionProps} props - Props for the FigureCaption component.
9290
- * @returns {JSX.Element} The rendered figcaption element.
9291
- */
9292
- const FigureCaption = ({ className, textColor, bgColor, children, ...props }) => {
9293
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9294
- color: textColor,
9295
- backgroundColor: bgColor,
9296
- ...props,
9297
- });
9298
- const bulmaClasses = usePrefixedClassNames();
9299
- const captionClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9300
- return (jsxRuntime.jsx("figcaption", { className: captionClasses || undefined, ...rest, children: children }));
9301
- };
9302
- /**
9303
- * The `Figure` component renders a styled `<figure>` element with Bulma helper class integration.
9304
- *
9305
- * @function
9306
- * @param {FigureProps} props - Props for the Figure component.
9307
- * @returns {JSX.Element} The rendered figure element.
9308
- */
9309
- const FigureComponent = ({ className, textColor, bgColor, children, ...props }) => {
9310
- const { bulmaHelperClasses, rest } = useBulmaClasses({
9311
- color: textColor,
9312
- backgroundColor: bgColor,
9313
- ...props,
9314
- });
9315
- const bulmaClasses = usePrefixedClassNames();
9316
- const figureClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9317
- return (jsxRuntime.jsx("figure", { className: figureClasses || undefined, ...rest, children: children }));
9318
- };
9319
- /**
9320
- * Figure component with Caption subcomponent.
9321
- *
9322
- * @example
9323
- * <Figure>
9324
- * <img src="image.jpg" alt="Description" />
9325
- * <Figure.Caption>Image caption text</Figure.Caption>
9326
- * </Figure>
9327
- */
9328
- const Figure = withSubComponents(FigureComponent, {
9329
- Caption: FigureCaption,
9330
- }, 'Figure');
9331
-
9332
- /**
9333
- * Distinguishes an `IconProps` object from a plain custom node (an inline SVG, a
9334
- * `react-icons` component, …) in a slot that accepts either — `Control`'s
9335
- * `iconLeft`/`iconRight`, `IconText`'s `iconProps`.
9336
- *
9337
- * One copy, imported by both consumers: two hand-maintained copies, one per
9338
- * consumer, is how the deprecated `icon` path came to crash both (#663).
9339
- *
9340
- * Its own module rather than `Icon.tsx` because `src/index.ts` wildcard-exports
9341
- * that file, and everything published there is public forever. `polymorphic.ts`
9342
- * makes the same call for `isCustomElement`: an internal discriminator is not
9343
- * something to support for the life of the package.
9344
- *
9345
- * It tests each member's VALUE, not merely its key, and only after everything
9346
- * React owns is out of the way. `'icon' in value` was enough
9347
- * to claim a Font Awesome `IconDefinition` — `{ prefix, iconName, icon: [w, h, …,
9348
- * path] }` — whose `icon` is a path array rather than a class string. That object
9349
- * is not a renderable node either, so the honest outcome is the one this slot has
9350
- * always had for it: the node branch, and React's own "Objects are not valid as a
9351
- * React child". Claiming it here instead rendered a blank container that still
9352
- * reserved layout.
9353
- *
9354
- * A member added to `IconProps` needs a line here, and a missed one is a crash
9355
- * rather than a fallthrough. That is the cost of a slot taking either a props
9356
- * object or a node; keeping the guard in one place is what keeps the cost to a
9357
- * single line.
9358
- */
9359
- function isIconProps(value) {
9360
- if (typeof value !== 'object' || value === null)
9361
- return false;
9362
- // Anything React owns. `isValidElement` covers elements only; a PORTAL is a
9363
- // valid `ReactNode` that fails it and carries a `children` key, so the member
9364
- // test below claimed it and `Control` spread it into `<Icon>` as props —
9365
- // rendering the portal's child locally instead of in its container. Every
9366
- // React internal carries `$$typeof`, so testing for it covers lazy, context
9367
- // and whatever React adds next, rather than portals alone.
9368
- if (React.isValidElement(value) || '$$typeof' in value)
9369
- return false;
9370
- const candidate = value;
9371
- return (typeof candidate.name === 'string' ||
9372
- typeof candidate.icon === 'string' ||
9373
- 'children' in candidate);
9374
- }
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
+ };
9375
9611
 
9612
+ // Valid size modifiers for the content class
9613
+ const validSizes = ['small', 'medium', 'large'];
9376
9614
  /**
9377
- * 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.
9378
9616
  *
9379
9617
  * @function
9380
- * @param {IconTextProps} props - Props for the IconText component.
9381
- * @returns {JSX.Element} The rendered icon text element.
9382
- * @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}
9383
9621
  */
9384
- const IconTextComponent = ({ className, textColor, color, bgColor, iconProps, children, items, ...props }) => {
9622
+ const Content = ({ className, textColor, color, bgColor, size, children, ...props }) => {
9385
9623
  /**
9386
9624
  * Generates Bulma helper classes and separates out remaining props.
9387
9625
  */
@@ -9390,23 +9628,24 @@ const IconTextComponent = ({ className, textColor, color, bgColor, iconProps, ch
9390
9628
  backgroundColor: bgColor,
9391
9629
  ...props,
9392
9630
  });
9393
- const bulmaClasses = usePrefixedClassNames('icon-text');
9394
- const iconTextClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9395
- return (jsxRuntime.jsx("span", { className: iconTextClasses, ...rest, children: items ? (items.map((item, index) => (jsxRuntime.jsxs(React.Fragment, { children: [item.iconProps &&
9396
- (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 &&
9397
- (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 }));
9398
9636
  };
9399
- const IconText = withSubComponents(IconTextComponent, { Icon }, 'IconText');
9400
9637
 
9401
9638
  /**
9402
- * 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.
9403
9642
  *
9404
9643
  * @function
9405
- * @param {ImageProps} props - Props for the Image component.
9406
- * @returns {JSX.Element} The rendered image element.
9407
- * @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}
9408
9647
  */
9409
- 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 }) => {
9410
9649
  /**
9411
9650
  * Generates Bulma helper classes and separates out remaining props.
9412
9651
  */
@@ -9415,310 +9654,209 @@ const Image = ({ as, className, textColor, color, bgColor, size, isRounded, isRe
9415
9654
  backgroundColor: bgColor,
9416
9655
  ...props,
9417
9656
  });
9418
- const bulmaClasses = usePrefixedClassNames('image', {
9657
+ const bulmaClasses = usePrefixedClassNames('delete', {
9419
9658
  [`is-${size}`]: size,
9420
- 'has-ratio': size && typeof size === 'string' && size.includes('by'),
9659
+ 'is-disabled': disabled,
9421
9660
  });
9422
- const imageClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
9423
- // Default tag logic: if "as" is provided, use it.
9424
- // If not, use <figure> for aspect ratios or children, <div> otherwise.
9425
- let Tag;
9426
- if (as) {
9427
- Tag = as;
9428
- }
9429
- else if (size && typeof size === 'string' && size.includes('by')) {
9430
- Tag = 'figure';
9431
- }
9432
- else {
9433
- Tag = 'div';
9434
- }
9435
- const roundedClass = usePrefixedClassNames('is-rounded');
9436
- const content = children ? (children) : (jsxRuntime.jsx("img", { className: classNames({ [roundedClass]: isRounded }) || undefined, src: src, alt: alt, ...(isRetina && src ? { srcSet: `${src} 2x` } : {}) }));
9437
- 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) }));
9438
9663
  };
9439
9664
 
9440
9665
  /**
9441
- * How long after an item appears its announcement is written, in ms. The wait
9442
- * lets a screen reader register a region the container has only just mounted,
9443
- * 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.
9444
9671
  */
9445
- 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
+
9446
9685
  /**
9447
- * How long an announcement stays in the region once written, in ms. That's
9448
- * long enough for a screen reader to pick it up. Clearing it after keeps a
9449
- * second copy of the item's text from sitting in the page, where someone
9450
- * reading through the page, or a test looking the text up, would find it
9451
- * 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.
9452
9691
  */
9453
- const announcementLifetime = 1000;
9454
- const noAnnouncements = [];
9455
- const nothingWritten = [];
9456
- const noAnnouncementsYet = {
9457
- key: 0,
9458
- waiting: noAnnouncements,
9459
- written: nothingWritten,
9460
- };
9461
- // Hides the region with inline styles rather than a class, so it needs no
9462
- // stylesheet, including the helper classes a modular Bulma build can leave
9463
- // out. Clipping it, rather than using `display` or `visibility`, keeps it in
9464
- // the accessibility tree. The declarations match the `extras-sr-only` mixin,
9465
- // plus `clip-path`, which replaces the deprecated `clip`. The negative margin
9466
- // pulls the 1px box back inside the page, so it doesn't add scrollable
9467
- // overflow. Exported for other live regions that must work without a
9468
- // stylesheet, such as a range calendar's.
9469
- const visuallyHidden = {
9470
- position: 'absolute',
9471
- width: '1px',
9472
- height: '1px',
9473
- padding: 0,
9474
- margin: '-1px',
9475
- overflow: 'hidden',
9476
- clip: 'rect(0, 0, 0, 0)',
9477
- clipPath: 'inset(50%)',
9478
- whiteSpace: 'nowrap',
9479
- 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 }));
9480
9701
  };
9702
+
9481
9703
  /**
9482
- * Works out what a status region says. Each item is announced once each time
9483
- * it appears, so one that goes and comes back is announced again. Its
9484
- * announcement is written `announceDelay` after it appears, or after the last
9485
- * item to join it when several appear in quick succession, alongside whatever
9486
- * the region still says. Once written, it stays for `announcementLifetime`,
9487
- * whatever is written after it and whether or not its item goes in the
9488
- * meantime. An item that goes before its announcement is written isn't
9489
- * announced.
9704
+ * FigureCaption component for rendering a styled figcaption element.
9490
9705
  *
9491
- * Nothing is written in the commit that mounts the region or the item. A
9492
- * screen reader reliably announces a polite live region whose content changes
9493
- * after the region is already in the page, and not always one that arrives
9494
- * with its content. By the time `describe` runs, an item's rendered text can
9495
- * 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.
9496
9708
  *
9497
- * @function useAnnouncements
9498
- * @param items - The items to announce, in the order they were shown.
9499
- * @param describe - The text to announce for an item, or nothing to skip it.
9500
- * It runs once the item is on screen, and it must keep its identity between
9501
- * renders (a module-level function or a `useCallback`), or every render
9502
- * re-checks the items.
9503
- * @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.
9504
9712
  */
9505
- function useAnnouncements(items, describe) {
9506
- // The ids of the items on screen as of the last change, every one of which
9507
- // has been looked at for an announcement already.
9508
- const onScreenIdsRef = React.useRef(new Set());
9509
- // Clears that haven't run yet. They outlive the batch that started them, so
9510
- // they're only stopped when the region unmounts.
9511
- const clearTimersRef = React.useRef(new Set());
9512
- const [announcements, setAnnouncements] = React.useState(noAnnouncementsYet);
9513
- // Runs after the commit that puts an item on screen, which is the first
9514
- // point its rendered text can be read.
9515
- React.useEffect(() => {
9516
- const announced = onScreenIdsRef.current;
9517
- const appeared = items.filter(item => !announced.has(item.id));
9518
- // Items that have gone drop out here too, so one that comes back has
9519
- // appeared again.
9520
- onScreenIdsRef.current = new Set(items.map(item => item.id));
9521
- const next = [];
9522
- for (const item of appeared) {
9523
- const text = describe(item);
9524
- if (text)
9525
- next.push({ id: item.id, text });
9526
- }
9527
- if (next.length > 0) {
9528
- // eslint-disable-next-line react-hooks/set-state-in-effect -- an item's rendered text can only be read once it's on screen
9529
- setAnnouncements(current => ({
9530
- ...current,
9531
- key: current.key + 1,
9532
- // Announcements still waiting go out with the new ones, apart from
9533
- // one whose item came back before it was written, which the new one
9534
- // replaces. What's already written stays put.
9535
- waiting: [
9536
- ...current.waiting.filter(waiting => !next.some(({ id }) => id === waiting.id)),
9537
- ...next,
9538
- ],
9539
- }));
9540
- }
9541
- }, [items, describe]);
9542
- // The write only touches the batch it was started for: a newer batch can
9543
- // be set before the render that cancels this timer. Its clear is started
9544
- // by the write itself, so it's timed from the write without waiting on a
9545
- // render, and a newer batch arriving doesn't cut short what's written.
9546
- const { key } = announcements;
9547
- React.useEffect(() => {
9548
- if (key === noAnnouncementsYet.key)
9549
- return undefined;
9550
- const clearTimers = clearTimersRef.current;
9551
- const write = setTimeout(() => {
9552
- // Whether an item is on screen is decided here, once. An item that
9553
- // went before its announcement was due isn't announced, and one that
9554
- // goes after keeps its announcement for the rest of its lifetime, when
9555
- // the region holds the only copy of its text rather than a second one.
9556
- const onScreen = onScreenIdsRef.current;
9557
- setAnnouncements(current => current.key === key
9558
- ? {
9559
- ...current,
9560
- waiting: noAnnouncements,
9561
- written: [
9562
- ...current.written,
9563
- ...current.waiting
9564
- .filter(announcement => onScreen.has(announcement.id))
9565
- .map(announcement => ({ ...announcement, batch: key })),
9566
- ],
9567
- }
9568
- : current);
9569
- // 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
9570
- const clear = setTimeout(() => {
9571
- clearTimers.delete(clear);
9572
- setAnnouncements(current => {
9573
- const written = current.written.filter(announcement => announcement.batch !== key);
9574
- return written.length === current.written.length
9575
- ? current
9576
- : { ...current, written };
9577
- });
9578
- }, announcementLifetime);
9579
- clearTimers.add(clear);
9580
- }, announceDelay);
9581
- return () => clearTimeout(write);
9582
- }, [key]);
9583
- React.useEffect(() => {
9584
- const clearTimers = clearTimersRef.current;
9585
- return () => {
9586
- clearTimers.forEach(clearTimeout);
9587
- clearTimers.clear();
9588
- };
9589
- }, []);
9590
- return announcements.written;
9591
- }
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
+ };
9592
9723
  /**
9593
- * Reads an element out roughly the way a screen reader would: its text, with
9594
- * an element's `aria-label`, or an image's `alt`, standing in for what's
9595
- * inside it, a break between block-level elements, and parts that are
9596
- * `aria-hidden`, `hidden` or inline-styled `display: none` left out. It isn't
9597
- * the full accessible name computation, so `aria-labelledby`, CSS-generated
9598
- * content, content hidden by a stylesheet and other ways of naming or hiding
9599
- * content aren't followed.
9724
+ * The `Figure` component renders a styled `<figure>` element with Bulma helper class integration.
9600
9725
  *
9601
- * @function spokenText
9602
- * @param element - The element to read.
9603
- * @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.
9604
9729
  */
9605
- function spokenText(element) {
9606
- return readAloud(element).replace(/\s+/g, ' ').trim();
9607
- }
9608
- // Elements a browser displays as blocks by default, and `br`. A screen reader
9609
- // pauses between them, so their text shouldn't run into the text beside them.
9610
- // They're known by tag rather than by computed `display`, which would depend
9611
- // on a stylesheet and on layout.
9612
- const blockLevelTags = new Set([
9613
- 'ADDRESS',
9614
- 'ARTICLE',
9615
- 'ASIDE',
9616
- 'BLOCKQUOTE',
9617
- 'BR',
9618
- 'CAPTION',
9619
- 'DD',
9620
- 'DETAILS',
9621
- 'DIALOG',
9622
- 'DIV',
9623
- 'DL',
9624
- 'DT',
9625
- 'FIELDSET',
9626
- 'FIGCAPTION',
9627
- 'FIGURE',
9628
- 'FOOTER',
9629
- 'FORM',
9630
- 'H1',
9631
- 'H2',
9632
- 'H3',
9633
- 'H4',
9634
- 'H5',
9635
- 'H6',
9636
- 'HEADER',
9637
- 'HGROUP',
9638
- 'HR',
9639
- 'LEGEND',
9640
- 'LI',
9641
- 'MAIN',
9642
- 'MENU',
9643
- 'NAV',
9644
- 'OL',
9645
- 'P',
9646
- 'PRE',
9647
- 'SEARCH',
9648
- 'SECTION',
9649
- 'SUMMARY',
9650
- 'TABLE',
9651
- 'TBODY',
9652
- 'TD',
9653
- 'TFOOT',
9654
- 'TH',
9655
- 'THEAD',
9656
- 'TR',
9657
- 'UL',
9658
- ]);
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
+ };
9659
9740
  /**
9660
- * Whether an element is left out of what a screen reader reads.
9741
+ * Figure component with Caption subcomponent.
9661
9742
  *
9662
- * @function isHidden
9663
- * @param element - The element to check.
9664
- * @returns True for `aria-hidden="true"`, the `hidden` attribute, and an
9665
- * inline `display: none`, on an HTML, SVG or any other element with an
9666
- * inline style.
9743
+ * @example
9744
+ * <Figure>
9745
+ * <img src="image.jpg" alt="Description" />
9746
+ * <Figure.Caption>Image caption text</Figure.Caption>
9747
+ * </Figure>
9667
9748
  */
9668
- const isHidden = (element) => element.getAttribute('aria-hidden') === 'true' ||
9669
- element.hasAttribute('hidden') ||
9670
- element.style?.display === 'none';
9749
+ const Figure = withSubComponents(FigureComponent, {
9750
+ Caption: FigureCaption,
9751
+ }, 'Figure');
9752
+
9671
9753
  /**
9672
- * The text `spokenText` reads for an element, before its whitespace is
9673
- * 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`.
9674
9757
  *
9675
- * @function readAloud
9676
- * @param element - The element to read.
9677
- * @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.
9678
9779
  */
9679
- function readAloud(element) {
9680
- if (isHidden(element))
9681
- return '';
9682
- const name = element.getAttribute('aria-label') ||
9683
- (element.tagName === 'IMG' ? element.getAttribute('alt') : null);
9684
- const text = name ||
9685
- Array.from(element.childNodes, child => {
9686
- if (child instanceof Element)
9687
- return readAloud(child);
9688
- return child.nodeType === Node.TEXT_NODE ? child.textContent : '';
9689
- }).join('');
9690
- // A name stands in for the element's content, and a block's content stands
9691
- // apart from what's around it, so both are padded to keep them from running
9692
- // into the neighboring text. An inline run, like `<span>Up</span>` then
9693
- // `<span>loaded</span>`, still reads as one word.
9694
- 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);
9695
9795
  }
9796
+
9696
9797
  /**
9697
- * A visually hidden, polite `status` live region that a container announces
9698
- * its items through. The container renders it from the moment it mounts,
9699
- * whether or not it has anything to show, so the region is already in the
9700
- * 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.
9701
9799
  *
9702
- * The announcements are this component's own state, so writing and clearing
9703
- * them re-renders the region and leaves the container's items alone. Each one
9704
- * is its own keyed node, so a re-render that leaves the announcements alone
9705
- * 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.
9706
9824
  *
9707
- * @function StatusRegion
9708
- * @param props - The items to announce, and the text to announce for each.
9709
- * @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}
9710
9829
  */
9711
- function StatusRegion({ items, describe, }) {
9712
- const announcements = useAnnouncements(items, describe);
9713
- // `role="status"` makes a region atomic unless it says otherwise, which
9714
- // reads the whole region on every change. Each announcement is its own
9715
- // node, and only additions are relevant by default, so with atomic off a
9716
- // screen reader reads just what was written, and removing one announcement
9717
- // doesn't read out the ones still there. A node is keyed by its batch as
9718
- // well as its item, since an item that comes back can be written again
9719
- // while what it said before is still there.
9720
- 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}`))) }));
9721
- }
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
+ };
9722
9860
 
9723
9861
  /**
9724
9862
  * Keys pressed together or one after another, in the outer `<kbd>` HTML gives a combination, with the separator between each pair as text.
@@ -11332,9 +11470,9 @@ function useAutoLabelId({ label, id, labelProps, rendersLabel, hasInput = true,
11332
11470
  ? (labelProps?.id ?? `${generatedId}-label`)
11333
11471
  : undefined;
11334
11472
  // Inactive with a label still means an own Field may render it (pickers'
11335
- // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`
11336
- // tells Field the association is owned here, so it must not generate one
11337
- // 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).
11338
11476
  const fieldLabelProps = active
11339
11477
  ? ownLabelId
11340
11478
  ? { ...ownLabelProps, id: ownLabelId }
@@ -12781,7 +12919,7 @@ const keepFocus = (e) => e.preventDefault();
12781
12919
  // An IME that's composing owns every key: Enter confirms what it composed,
12782
12920
  // and the arrows move through its candidates. Safari can say so with
12783
12921
  // `keyCode` 229 rather than `isComposing`.
12784
- const isComposing = (e) => e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229;
12922
+ const isComposing$1 = (e) => e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229;
12785
12923
  // Whether an option can be picked. The arrows still reach a disabled one, so
12786
12924
  // a screen reader user finds it.
12787
12925
  const isEnabled = (item) => typeof item === 'string' || !item.disabled;
@@ -12944,7 +13082,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
12944
13082
  }, [keepFirst, isActive, filteredData.length, inputValue]);
12945
13083
  // Handle keyboard navigation
12946
13084
  const handleKeyDown = React.useCallback((e) => {
12947
- if (disabled || isComposing(e))
13085
+ if (disabled || isComposing$1(e))
12948
13086
  return;
12949
13087
  switch (e.key) {
12950
13088
  case 'ArrowDown':
@@ -13129,6 +13267,10 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13129
13267
  });
13130
13268
  Autocomplete.displayName = 'Autocomplete';
13131
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;
13132
13274
  /**
13133
13275
  * The `Taginput` component provides a tag/chip input field for managing multiple tags.
13134
13276
  *
@@ -13177,7 +13319,9 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13177
13319
  const [inputValue, setInputValue] = React.useState('');
13178
13320
  const [isActive, setIsActive] = React.useState(false);
13179
13321
  const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
13180
- const listboxId = `${React.useId()}-listbox`;
13322
+ const baseId = React.useId();
13323
+ const listboxId = `${baseId}-listbox`;
13324
+ const counterId = `${baseId}-counter`;
13181
13325
  // With `data` the input offers suggestions, so it's a combobox, even
13182
13326
  // while none match. Without it there's never a list, so it stays a plain
13183
13327
  // text field.
@@ -13186,16 +13330,13 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13186
13330
  // Use controlled or internal tags
13187
13331
  const tags = controlledValue !== undefined ? controlledValue : internalTags;
13188
13332
  const isMaxReached = maxTags !== undefined && tags.length >= maxTags;
13189
- // At the tag limit the text input is not rendered, so there is nothing to
13190
- // wire a label to, its own or a surrounding Field's. The suggestion list
13191
- // points `aria-labelledby` at the label that names the input, so with
13192
- // 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.
13193
13335
  const { controlId, fieldLabelProps, labelled, labelId: listLabelId, } = useAutoLabelId({
13194
13336
  label,
13195
13337
  id,
13196
13338
  labelProps,
13197
- rendersLabel: ownField && !isMaxReached,
13198
- hasInput: !isMaxReached,
13339
+ rendersLabel: ownField,
13199
13340
  needsLabelId: hasSuggestions,
13200
13341
  });
13201
13342
  // Get display value from tag
@@ -13216,9 +13357,8 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13216
13357
  const hasInput = inputValue || (openOnFocus && isActive);
13217
13358
  return matchesInput && notAlreadyAdded && hasInput;
13218
13359
  });
13219
- // At the tag limit the input isn't rendered, so the list isn't either.
13220
- // Left open it would be a listbox with no combobox, offering tags past
13221
- // the limit (#1087).
13360
+ // At the tag limit the list stays closed, since it could only offer tags
13361
+ // past the limit (#1087).
13222
13362
  const listOpen = isActive && !isMaxReached && filteredData.length > 0;
13223
13363
  // keepFirst: auto-highlight first item when dropdown opens
13224
13364
  React.useEffect(() => {
@@ -13233,36 +13373,42 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13233
13373
  }
13234
13374
  onChange?.(newTags);
13235
13375
  }, [controlledValue, onChange]);
13236
- // Add a tag from text typed, pasted or picked from the list. Only
13237
- // `createTag` turns it into an object.
13238
- 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) => {
13239
13381
  if (disabled || readonly)
13240
13382
  return;
13241
- const displayValue = value.trim();
13242
- if (!displayValue)
13243
- return;
13244
- // No maxTags check: only the input and the list call this, neither
13245
- // renders at the limit, and each call adds at most one tag.
13246
- // Check duplicates
13247
- if (!allowDuplicates) {
13248
- const exists = tags.some(tag => getDisplayValue(tag).toLowerCase() === displayValue.toLowerCase());
13249
- if (exists)
13250
- 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);
13251
13407
  }
13252
- // Check if allowed (must be in data if allowNew is false). Compare
13253
- // case-insensitively, consistent with the dropdown filter and the
13254
- // duplicate check above.
13255
- if (!allowNew &&
13256
- !data.some(d => d.toLowerCase() === displayValue.toLowerCase()))
13257
- return;
13258
- // beforeAdding validation
13259
- if (beforeAdding && !beforeAdding(displayValue))
13408
+ if (added.length === 0)
13260
13409
  return;
13261
- // createTag transformation
13262
- const finalTag = createTag ? createTag(displayValue) : displayValue;
13263
- const newTags = [...tags, finalTag];
13264
- updateTags(newTags);
13265
- onAdd?.(finalTag);
13410
+ updateTags(next);
13411
+ added.forEach(tag => onAdd?.(tag));
13266
13412
  setInputValue('');
13267
13413
  if (!keepOpen) {
13268
13414
  setIsActive(false);
@@ -13272,6 +13418,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13272
13418
  disabled,
13273
13419
  readonly,
13274
13420
  tags,
13421
+ maxTags,
13275
13422
  allowDuplicates,
13276
13423
  allowNew,
13277
13424
  data,
@@ -13300,7 +13447,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13300
13447
  if (confirmKeys.includes(lastChar) && lastChar !== 'Enter') {
13301
13448
  const valueWithoutKey = newValue.slice(0, -1).trim();
13302
13449
  if (valueWithoutKey) {
13303
- addTag(valueWithoutKey);
13450
+ addTags([valueWithoutKey]);
13304
13451
  }
13305
13452
  return;
13306
13453
  }
@@ -13309,7 +13456,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13309
13456
  if (newValue && data.length > 0) {
13310
13457
  setIsActive(true);
13311
13458
  }
13312
- }, [confirmKeys, addTag, onTyping, data.length]);
13459
+ }, [confirmKeys, addTags, onTyping, data.length]);
13313
13460
  // Handle paste
13314
13461
  const handlePaste = React.useCallback((e) => {
13315
13462
  if (onPasteSeparators.length === 0)
@@ -13320,17 +13467,12 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13320
13467
  if (!hasSeparator)
13321
13468
  return;
13322
13469
  e.preventDefault();
13323
- // Build a regex from all separators
13324
- 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, '\\$&'));
13325
13473
  const regex = new RegExp(`[${escapedSeparators.join('')}]`);
13326
- const parts = pastedText
13327
- .split(regex)
13328
- .map(s => s.trim())
13329
- .filter(Boolean);
13330
- for (const part of parts) {
13331
- addTag(part);
13332
- }
13333
- }, [onPasteSeparators, addTag]);
13474
+ addTags(pastedText.split(regex));
13475
+ }, [onPasteSeparators, addTags]);
13334
13476
  // Handle focus
13335
13477
  const handleFocus = React.useCallback(() => {
13336
13478
  if (openOnFocus && data.length > 0 && !disabled) {
@@ -13363,16 +13505,16 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13363
13505
  }, [isActive]);
13364
13506
  // Handle keyboard
13365
13507
  const handleKeyDown = React.useCallback((e) => {
13366
- if (disabled || readonly)
13508
+ if (disabled || readonly || isComposing(e))
13367
13509
  return;
13368
13510
  // Handle confirm keys
13369
13511
  if (confirmKeys.includes(e.key)) {
13370
13512
  e.preventDefault();
13371
13513
  if (highlightedIndex >= 0 && filteredData[highlightedIndex]) {
13372
- addTag(filteredData[highlightedIndex]);
13514
+ addTags([filteredData[highlightedIndex]]);
13373
13515
  }
13374
13516
  else if (inputValue.trim()) {
13375
- addTag(inputValue);
13517
+ addTags([inputValue]);
13376
13518
  }
13377
13519
  return;
13378
13520
  }
@@ -13399,7 +13541,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13399
13541
  case 'Escape':
13400
13542
  // Closing the suggestions claims the key, so a Modal around the
13401
13543
  // field stays open. With none showing, the key goes on to it.
13402
- if (isActive && filteredData.length > 0)
13544
+ if (listOpen)
13403
13545
  e.preventDefault();
13404
13546
  setIsActive(false);
13405
13547
  setHighlightedIndex(-1);
@@ -13411,9 +13553,10 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13411
13553
  confirmKeys,
13412
13554
  highlightedIndex,
13413
13555
  filteredData,
13414
- addTag,
13556
+ addTags,
13415
13557
  inputValue,
13416
13558
  isActive,
13559
+ listOpen,
13417
13560
  data.length,
13418
13561
  removeOnKeys,
13419
13562
  tags.length,
@@ -13499,9 +13642,13 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
13499
13642
  e.stopPropagation();
13500
13643
  removeTag(index);
13501
13644
  }, "aria-label": ariaCloseLabel || `Remove ${displayVal}` }))] }, index));
13502
- }), !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', {
13503
13650
  'is-active': index === highlightedIndex,
13504
- }), 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 &&
13505
13652
  tags.map((tag, i) => (jsxRuntime.jsx("input", { type: "hidden", name: name, value: getDisplayValue(tag), form: form }, `tag-input-${i}`)))] }));
13506
13653
  if (ownField) {
13507
13654
  return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [taginputElement, messageEl] }));
@@ -19964,6 +20111,8 @@ exports.Thead = Thead;
19964
20111
  exports.Theme = Theme;
19965
20112
  exports.TimeInput = TimeInput;
19966
20113
  exports.TimeInputBase = TimeInputBase;
20114
+ exports.Timeline = Timeline;
20115
+ exports.TimelineItem = TimelineItem;
19967
20116
  exports.Title = Title;
19968
20117
  exports.Toast = Toast;
19969
20118
  exports.ToastContainer = ToastContainer;