@allxsmith/bestax-bulma 5.18.1 → 5.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1237,6 +1237,27 @@ const ColumnsComponent = ({ className, textColor, color: _fieldColor, bgColor, i
1237
1237
  };
1238
1238
  const Columns = withSubComponents(ColumnsComponent, { Column }, 'Columns');
1239
1239
 
1240
+ /**
1241
+ * The `type` to render on a `<button>` a component defaults to `button`: the
1242
+ * caller's own value when it is `button`, `submit` or `reset`, and `button`
1243
+ * otherwise.
1244
+ *
1245
+ * A missing `type` is not the only way a button ends up submitting the form
1246
+ * around it. HTML reads an invalid value, such as the anchor's MIME-type
1247
+ * `type="text/html"`, as submit too, and a props spread carrying
1248
+ * `type: undefined` removes the attribute outright. Both can arrive through a
1249
+ * spread or an untyped caller the component's own types never see, so this
1250
+ * checks the value rather than trusting where it came from.
1251
+ *
1252
+ * Call it AFTER the forwarded props are spread and pass it the forwarded
1253
+ * `type`, so its result is the attribute that renders.
1254
+ */
1255
+ function buttonType(given) {
1256
+ return given === 'submit' || given === 'reset' || given === 'button'
1257
+ ? given
1258
+ : 'button';
1259
+ }
1260
+
1240
1261
  /**
1241
1262
  * Whether an `as` target is a custom element rather than a built-in tag.
1242
1263
  *
@@ -1534,7 +1555,7 @@ const Avatar = React.forwardRef(function Avatar(avatarProps, ref) {
1534
1555
  // The decorative opt-out never applies to an interactive avatar — a link or
1535
1556
  // button must always expose an accessible name.
1536
1557
  const isDecorative = alt === '' && !isInteractive;
1537
- const a11yProps = showImage
1558
+ const a11yDefaults = showImage
1538
1559
  ? isInteractive && !accessibleName
1539
1560
  ? // The img alt normally names the control; with no alt/name (an API
1540
1561
  // returning only a photo URL) the link/button would be nameless.
@@ -1546,10 +1567,18 @@ const Avatar = React.forwardRef(function Avatar(avatarProps, ref) {
1546
1567
  ...(isInteractive ? {} : { role: 'img' }),
1547
1568
  'aria-label': accessibleName || name || 'Avatar',
1548
1569
  };
1549
- // A clickable avatar inside a form must not submit it; default the native
1550
- // button type (an explicit type passed through rest still wins).
1551
- const buttonTypeProps = Tag === 'button' ? { type: 'button' } : {};
1552
- return (jsxRuntime.jsxs(Tag, { ref: ref, className: combinedClasses, style: { ...sizeStyle, ...style }, ...buttonTypeProps, ...linkProps, ...a11yProps, ...rest, children: [showImage && (jsxRuntime.jsx("img", { ...imageProps, ref: imgRef, src: src, alt: accessibleName ?? '', onError: e => {
1570
+ // Each default yields to a value the caller passed, and is applied after
1571
+ // `rest` reading through it rather than spread before it. React treats an
1572
+ // `undefined` attribute as "remove it", and a spread carrying the key with no
1573
+ // value is how that arrives, so a default spread first was erased by it: an
1574
+ // `aria-label: undefined` left a button avatar nameless, and `role` and
1575
+ // `aria-hidden` went the same way. The button `type` below has the same
1576
+ // shape (#690).
1577
+ const a11yProps = Object.fromEntries(Object.entries(a11yDefaults).map(([key, fallback]) => [
1578
+ key,
1579
+ rest[key] ?? fallback,
1580
+ ]));
1581
+ return (jsxRuntime.jsxs(Tag, { ref: ref, className: combinedClasses, style: { ...sizeStyle, ...style }, ...linkProps, ...rest, ...a11yProps, ...(Tag === 'button' ? { type: buttonType(rest.type) } : {}), children: [showImage && (jsxRuntime.jsx("img", { ...imageProps, ref: imgRef, src: src, alt: accessibleName ?? '', onError: e => {
1553
1582
  imageProps?.onError?.(e);
1554
1583
  setErroredSrc(src);
1555
1584
  } }, src)), showInitials && (jsxRuntime.jsx("span", { className: initialsClass, children: resolvedInitials })), showIcon && icon, showDefaultIcon && jsxRuntime.jsx(DefaultAvatarIcon, {})] }));
@@ -2049,7 +2078,6 @@ const CardFooterItem = ({ as = 'span', className, children, color, bgColor, text
2049
2078
  }
2050
2079
  const forwarded = omitAttrs(rest, STRIP_FROM_NON_ANCHOR$1);
2051
2080
  if (as === 'button') {
2052
- const forwardedType = forwarded.type;
2053
2081
  return (jsxRuntime.jsx("button", { className: itemClasses, ...forwarded,
2054
2082
  // A footer item button must not submit an enclosing form by default —
2055
2083
  // `<button>` defaults to `type="submit"`, and a Save/Cancel action row
@@ -2059,13 +2087,9 @@ const CardFooterItem = ({ as = 'span', className, children, color, bgColor, text
2059
2087
  // typed here as the `<a>` MIME string (`STRIP_FROM_NON_ANCHOR` above
2060
2088
  // says why it is not withheld), so `as="button" type="text/html"`
2061
2089
  // compiles, and HTML's INVALID-value default for a button's `type` is
2062
- // submit too. So anything that is not one of the three native button
2063
- // types falls back to `button` rather than being forwarded.
2064
- type: forwardedType === 'submit' ||
2065
- forwardedType === 'reset' ||
2066
- forwardedType === 'button'
2067
- ? forwardedType
2068
- : 'button', children: children }));
2090
+ // submit too. `buttonType` keeps `button`, `submit` and `reset` and
2091
+ // turns anything else into `button`.
2092
+ type: buttonType(forwarded.type), children: children }));
2069
2093
  }
2070
2094
  return (jsxRuntime.jsx("span", { className: itemClasses, ...omitAttrs(forwarded, STRIP_FROM_NON_BUTTON), children: children }));
2071
2095
  };
@@ -2402,9 +2426,7 @@ const DropdownItem = ((itemProps) => {
2402
2426
  // carrying `role: undefined` would otherwise erase the default, and an
2403
2427
  // item with no role drops out of the menu and its arrow-key order.
2404
2428
  role: forwarded.role ?? 'menuitem', ...(Component === 'button'
2405
- ? {
2406
- type: forwarded.type ?? 'button',
2407
- }
2429
+ ? { type: buttonType(forwarded.type) }
2408
2430
  : {}), children: children }));
2409
2431
  });
2410
2432
  /**
@@ -2513,7 +2535,9 @@ const MenuItem = React.forwardRef(function MenuItem(itemProps, ref) {
2513
2535
  labelChildren.push(child);
2514
2536
  }
2515
2537
  });
2516
- return (jsxRuntime.jsxs("li", { className: className, "data-testid": testId, style: style, id: id, title: title, role: role, tabIndex: tabIndex, children: [jsxRuntime.jsx(Component, { ref: ref, className: itemClass || undefined, ...linkProps, children: labelChildren }), nestedMenuLists] }));
2538
+ return (jsxRuntime.jsxs("li", { className: className, "data-testid": testId, style: style, id: id, title: title, role: role, tabIndex: tabIndex, children: [jsxRuntime.jsx(Component, { ref: ref, className: itemClass || undefined, ...linkProps, ...(Component === 'button'
2539
+ ? { type: buttonType(linkProps.type) }
2540
+ : {}), children: labelChildren }), nestedMenuLists] }));
2517
2541
  });
2518
2542
  MenuItem.displayName = 'MenuItem';
2519
2543
  // Attach static subcomponents
@@ -4713,6 +4737,48 @@ const Sidebar = withSubComponents(SidebarComponent, {
4713
4737
  Footer: SidebarFooter,
4714
4738
  }, 'Sidebar');
4715
4739
 
4740
+ /**
4741
+ * Splits the items a container shows into one stack per position in use. An
4742
+ * item goes to its own position when it has one and to the container's
4743
+ * otherwise. A position with no items gets no stack.
4744
+ *
4745
+ * Stacks come back in `order`, so their order in the page follows the screen
4746
+ * rather than which position happened to be used first, and a stack keeps its
4747
+ * place while the others come and go. A position missing from `order` sorts
4748
+ * after the known ones.
4749
+ *
4750
+ * @function groupIntoPositionStacks
4751
+ * @param items - The items the container shows, in the order they were shown.
4752
+ * @param positionOf - Reads an item's own position, if it has one.
4753
+ * @param containerPosition - Where an item without a position of its own goes.
4754
+ * @param order - Every known position, in the order stacks should render.
4755
+ * @returns One stack per position in use.
4756
+ */
4757
+ function groupIntoPositionStacks(items, positionOf, containerPosition, order) {
4758
+ const byPosition = new Map();
4759
+ for (const item of items) {
4760
+ const position = positionOf(item) ?? containerPosition;
4761
+ const stack = byPosition.get(position);
4762
+ if (stack) {
4763
+ stack.push(item);
4764
+ }
4765
+ else {
4766
+ byPosition.set(position, [item]);
4767
+ }
4768
+ }
4769
+ const rank = (position) => {
4770
+ const index = order.indexOf(position);
4771
+ return index === -1 ? order.length : index;
4772
+ };
4773
+ // Array.prototype.sort is stable, so unknown positions keep the order they
4774
+ // were first used in.
4775
+ return Array.from(byPosition, ([position, stackItems]) => ({
4776
+ key: position === containerPosition ? 'container' : `at-${position}`,
4777
+ position,
4778
+ items: stackItems,
4779
+ })).sort((a, b) => rank(a.position) - rank(b.position));
4780
+ }
4781
+
4716
4782
  /**
4717
4783
  * The `Toast` component provides brief notification messages with optional action and cancel buttons.
4718
4784
  *
@@ -4989,25 +5055,38 @@ const toast = {
4989
5055
  return () => toastListeners.delete(listener);
4990
5056
  },
4991
5057
  };
5058
+ // The order ToastContainer renders its stacks in: across the top of the
5059
+ // screen, then across the bottom.
5060
+ const toastStackOrder = [
5061
+ 'top-left',
5062
+ 'top-center',
5063
+ 'top-right',
5064
+ 'bottom-left',
5065
+ 'bottom-center',
5066
+ 'bottom-right',
5067
+ ];
4992
5068
  /**
4993
5069
  * Container component for rendering programmatic toasts.
4994
- * Place once at your app root to enable the toast API.
5070
+ * Place once at your app root to enable the toast API. A toast shown with a
5071
+ * `position` appears there, and one shown without goes to the container's
5072
+ * `position`.
4995
5073
  *
4996
5074
  * @function
4997
5075
  * @param {{ position?: ToastPosition }} props - Container props.
4998
5076
  * @returns {JSX.Element | null} The rendered toast container, or null if empty.
4999
5077
  */
5000
- const ToastContainer = ({ position = 'top-right', }) => {
5078
+ const ToastContainer = ({ position = 'top-right' }) => {
5001
5079
  // Starts from the toasts already showing instead of an empty list, then
5002
5080
  // follows changes.
5003
5081
  const toastList = React.useSyncExternalStore(toast.subscribe, getVisibleToasts, getServerToasts);
5004
5082
  if (typeof document === 'undefined' || toastList.length === 0) {
5005
5083
  return null;
5006
5084
  }
5007
- return reactDom.createPortal(jsxRuntime.jsx("div", { className: `toast-container is-${position}`, children: toastList.map(t => {
5085
+ const stacks = groupIntoPositionStacks(toastList, t => t.props.position, position, toastStackOrder);
5086
+ return reactDom.createPortal(stacks.map(stack => (jsxRuntime.jsx("div", { className: `toast-container is-${stack.position}`, children: stack.items.map(t => {
5008
5087
  const { queue: _queue, ...toastProps } = t.props;
5009
- return (jsxRuntime.jsx(Toast, { ...toastProps, position: position, onClose: () => toast.close(t.id) }, t.id));
5010
- }) }), document.body);
5088
+ return (jsxRuntime.jsx(Toast, { ...toastProps, position: stack.position, onClose: () => toast.close(t.id) }, t.id));
5089
+ }) }, stack.key))), document.body);
5011
5090
  };
5012
5091
 
5013
5092
  /**
@@ -6451,25 +6530,28 @@ const NotificationItem = ({ instance, onClose }) => {
6451
6530
  }, [pauseOnHover]);
6452
6531
  return (jsxRuntime.jsx(Notification, { color: color, isLight: isLight, hasDelete: hasDelete, onDelete: handleClose, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: { pointerEvents: 'auto' }, children: urgent ? (jsxRuntime.jsx("span", { role: "alert", "aria-live": "assertive", children: message })) : (jsxRuntime.jsx("span", { role: "status", "aria-live": "polite", children: message })) }));
6453
6532
  };
6533
+ // The order NotificationContainer renders its stacks in: across the top of the
6534
+ // screen, then across the bottom.
6535
+ const notificationStackOrder = [
6536
+ 'top-left',
6537
+ 'top',
6538
+ 'top-right',
6539
+ 'bottom-left',
6540
+ 'bottom',
6541
+ 'bottom-right',
6542
+ ];
6454
6543
  /**
6455
- * Container component for rendering programmatic notifications.
6456
- * Place once at your app root to enable the notification API.
6544
+ * Inline style that fixes a stack of notifications to its place on the screen.
6457
6545
  *
6458
6546
  * @function
6459
- * @param {{ position?: NotificationPosition }} props - Container props.
6460
- * @returns {JSX.Element | null} The rendered notification container, or null if empty.
6547
+ * @param {NotificationPosition} position - Where the stack sits.
6548
+ * @returns {React.CSSProperties} The stack's style.
6461
6549
  */
6462
- const NotificationContainer = ({ position = 'top-right' }) => {
6463
- // Starts from the notifications already showing instead of an empty list,
6464
- // then follows changes.
6465
- const items = React.useSyncExternalStore(notification.subscribe, getVisibleNotifications, getServerNotifications);
6466
- if (typeof document === 'undefined' || items.length === 0) {
6467
- return null;
6468
- }
6550
+ const notificationStackStyle = (position) => {
6469
6551
  const isBottom = position.startsWith('bottom');
6470
6552
  const isCenter = position === 'top' || position === 'bottom';
6471
6553
  const isRight = position.endsWith('right');
6472
- const containerStyle = {
6554
+ return {
6473
6555
  position: 'fixed',
6474
6556
  zIndex: 100,
6475
6557
  display: 'flex',
@@ -6485,7 +6567,27 @@ const NotificationContainer = ({ position = 'top-right' }) => {
6485
6567
  ? { right: 0, alignItems: 'flex-end' }
6486
6568
  : { left: 0, alignItems: 'flex-start' }),
6487
6569
  };
6488
- return reactDom.createPortal(jsxRuntime.jsx("div", { style: containerStyle, children: items.map(item => (jsxRuntime.jsx(NotificationItem, { instance: item, onClose: notification.close }, item.id))) }), document.body);
6570
+ };
6571
+ /**
6572
+ * Container component for rendering programmatic notifications.
6573
+ * Place once at your app root to enable the notification API. A notification
6574
+ * shown with a `position` appears there, and one shown without goes to the
6575
+ * container's `position`, so the container renders a stack for each position
6576
+ * in use.
6577
+ *
6578
+ * @function
6579
+ * @param {{ position?: NotificationPosition }} props - Container props.
6580
+ * @returns {JSX.Element | null} The rendered notification container, or null if empty.
6581
+ */
6582
+ const NotificationContainer = ({ position = 'top-right' }) => {
6583
+ // Starts from the notifications already showing instead of an empty list,
6584
+ // then follows changes.
6585
+ const items = React.useSyncExternalStore(notification.subscribe, getVisibleNotifications, getServerNotifications);
6586
+ if (typeof document === 'undefined' || items.length === 0) {
6587
+ return null;
6588
+ }
6589
+ const stacks = groupIntoPositionStacks(items, item => item.options.position, position, notificationStackOrder);
6590
+ return reactDom.createPortal(stacks.map(stack => (jsxRuntime.jsx("div", { style: notificationStackStyle(stack.position), children: stack.items.map(item => (jsxRuntime.jsx(NotificationItem, { instance: item, onClose: notification.close }, item.id))) }, stack.key))), document.body);
6489
6591
  };
6490
6592
 
6491
6593
  /**