@allxsmith/bestax-bulma 5.52.0 → 5.54.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.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/Avatars.js +7 -2
- package/dist/esm/components/Avatars.js.map +1 -1
- package/dist/esm/components/Badge.js +7 -2
- package/dist/esm/components/Badge.js.map +1 -1
- package/dist/esm/components/Command.js +19 -9
- package/dist/esm/components/Command.js.map +1 -1
- package/dist/esm/components/Modal.js +2 -1
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/components/Pagination.js +9 -1
- package/dist/esm/components/Pagination.js.map +1 -1
- package/dist/esm/components/Sidebar.js +3 -1
- package/dist/esm/components/Sidebar.js.map +1 -1
- package/dist/esm/components/Steps.js +5 -1
- package/dist/esm/components/Steps.js.map +1 -1
- package/dist/esm/components/Toast.js +8 -5
- package/dist/esm/components/Toast.js.map +1 -1
- package/dist/esm/elements/Notification.js +4 -2
- package/dist/esm/elements/Notification.js.map +1 -1
- package/dist/esm/elements/RingProgress.js +6 -1
- package/dist/esm/elements/RingProgress.js.map +1 -1
- package/dist/esm/form/Autocomplete.js +7 -2
- package/dist/esm/form/Autocomplete.js.map +1 -1
- package/dist/esm/form/Checkbox.js +3 -2
- package/dist/esm/form/Checkbox.js.map +1 -1
- package/dist/esm/form/CheckboxCard.js +3 -2
- package/dist/esm/form/CheckboxCard.js.map +1 -1
- package/dist/esm/form/Combobox.js +10 -4
- package/dist/esm/form/Combobox.js.map +1 -1
- package/dist/esm/form/DateInputBase.js +15 -6
- package/dist/esm/form/DateInputBase.js.map +1 -1
- package/dist/esm/form/DateRangeInputBase.js +15 -5
- package/dist/esm/form/DateRangeInputBase.js.map +1 -1
- package/dist/esm/form/DateTimeInputBase.js +15 -6
- package/dist/esm/form/DateTimeInputBase.js.map +1 -1
- package/dist/esm/form/Fieldset.js +64 -0
- package/dist/esm/form/Fieldset.js.map +1 -0
- package/dist/esm/form/File.js +4 -1
- package/dist/esm/form/File.js.map +1 -1
- package/dist/esm/form/FormContext.js +17 -1
- package/dist/esm/form/FormContext.js.map +1 -1
- package/dist/esm/form/InputBase.js +3 -2
- package/dist/esm/form/InputBase.js.map +1 -1
- package/dist/esm/form/Numberinput.js +3 -2
- package/dist/esm/form/Numberinput.js.map +1 -1
- package/dist/esm/form/PinInput.js +3 -2
- package/dist/esm/form/PinInput.js.map +1 -1
- package/dist/esm/form/Radio.js +3 -2
- package/dist/esm/form/Radio.js.map +1 -1
- package/dist/esm/form/RadioCard.js +3 -2
- package/dist/esm/form/RadioCard.js.map +1 -1
- package/dist/esm/form/Rate.js +12 -3
- package/dist/esm/form/Rate.js.map +1 -1
- package/dist/esm/form/SelectBase.js +3 -2
- package/dist/esm/form/SelectBase.js.map +1 -1
- package/dist/esm/form/Slider.js +10 -6
- package/dist/esm/form/Slider.js.map +1 -1
- package/dist/esm/form/Switch.js +3 -1
- package/dist/esm/form/Switch.js.map +1 -1
- package/dist/esm/form/Taginput.js +12 -5
- package/dist/esm/form/Taginput.js.map +1 -1
- package/dist/esm/form/TextAreaBase.js +3 -2
- package/dist/esm/form/TextAreaBase.js.map +1 -1
- package/dist/esm/form/TimeInputBase.js +13 -6
- package/dist/esm/form/TimeInputBase.js.map +1 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js +4 -2
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
- package/dist/esm/form/_pickerInternals/useLocaleDefaults.js +80 -0
- package/dist/esm/form/_pickerInternals/useLocaleDefaults.js.map +1 -0
- package/dist/esm/helpers/LocaleProvider.js +156 -0
- package/dist/esm/helpers/LocaleProvider.js.map +1 -0
- package/dist/esm/helpers/localeFormat.js +108 -0
- package/dist/esm/helpers/localeFormat.js.map +1 -0
- package/dist/esm/helpers/localeInfo.js +109 -0
- package/dist/esm/helpers/localeInfo.js.map +1 -0
- package/dist/esm/helpers/portal.js +14 -1
- package/dist/esm/helpers/portal.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +690 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/Avatars.d.ts +1 -1
- package/dist/types/components/Badge.d.ts +1 -1
- package/dist/types/components/Command.d.ts +4 -2
- package/dist/types/components/Pagination.d.ts +3 -0
- package/dist/types/components/Steps.d.ts +4 -1
- package/dist/types/elements/RingProgress.d.ts +3 -2
- package/dist/types/form/Autocomplete.d.ts +6 -1
- package/dist/types/form/Combobox.d.ts +3 -2
- package/dist/types/form/DateInputBase.d.ts +25 -3
- package/dist/types/form/DateRangeInputBase.d.ts +25 -3
- package/dist/types/form/DateTimeInputBase.d.ts +25 -3
- package/dist/types/form/Fieldset.d.ts +86 -0
- package/dist/types/form/File.d.ts +2 -1
- package/dist/types/form/FormContext.d.ts +12 -0
- package/dist/types/form/Rate.d.ts +4 -1
- package/dist/types/form/Slider.d.ts +9 -2
- package/dist/types/form/Taginput.d.ts +8 -2
- package/dist/types/form/TimeInputBase.d.ts +5 -1
- package/dist/types/form/_pickerInternals/PickerPopover.d.ts +6 -0
- package/dist/types/form/_pickerInternals/useLocaleDefaults.d.ts +49 -0
- package/dist/types/helpers/LocaleProvider.d.ts +131 -0
- package/dist/types/helpers/localeFormat.d.ts +26 -0
- package/dist/types/helpers/localeInfo.d.ts +23 -0
- package/dist/types/helpers/portal.d.ts +11 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types-cjs/components/Avatars.d.ts +1 -1
- package/dist/types-cjs/components/Badge.d.ts +1 -1
- package/dist/types-cjs/components/Command.d.ts +4 -2
- package/dist/types-cjs/components/Pagination.d.ts +3 -0
- package/dist/types-cjs/components/Steps.d.ts +4 -1
- package/dist/types-cjs/elements/RingProgress.d.ts +3 -2
- package/dist/types-cjs/form/Autocomplete.d.ts +6 -1
- package/dist/types-cjs/form/Combobox.d.ts +3 -2
- package/dist/types-cjs/form/DateInputBase.d.ts +25 -3
- package/dist/types-cjs/form/DateRangeInputBase.d.ts +25 -3
- package/dist/types-cjs/form/DateTimeInputBase.d.ts +25 -3
- package/dist/types-cjs/form/Fieldset.d.ts +86 -0
- package/dist/types-cjs/form/File.d.ts +2 -1
- package/dist/types-cjs/form/FormContext.d.ts +12 -0
- package/dist/types-cjs/form/Rate.d.ts +4 -1
- package/dist/types-cjs/form/Slider.d.ts +9 -2
- package/dist/types-cjs/form/Taginput.d.ts +8 -2
- package/dist/types-cjs/form/TimeInputBase.d.ts +5 -1
- package/dist/types-cjs/form/_pickerInternals/PickerPopover.d.ts +6 -0
- package/dist/types-cjs/form/_pickerInternals/useLocaleDefaults.d.ts +49 -0
- package/dist/types-cjs/helpers/LocaleProvider.d.ts +131 -0
- package/dist/types-cjs/helpers/localeFormat.d.ts +26 -0
- package/dist/types-cjs/helpers/localeInfo.d.ts +23 -0
- package/dist/types-cjs/helpers/portal.d.ts +11 -0
- package/dist/types-cjs/index.d.ts +3 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/form/_fieldset.scss +108 -0
- package/src/scss/form/_index.scss +1 -0
|
@@ -6,6 +6,7 @@ import { isEventInside } from '../helpers/shadowDom.js';
|
|
|
6
6
|
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
7
7
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
8
8
|
import { usePortalContainer } from '../helpers/portal.js';
|
|
9
|
+
import { LocalePortalScope } from '../helpers/LocaleProvider.js';
|
|
9
10
|
import { groupIntoPositionStacks } from '../helpers/positionStacks.js';
|
|
10
11
|
import { useLatestRef } from '../helpers/useLatestRef.js';
|
|
11
12
|
import { useOverlayStack } from '../helpers/overlayStack.js';
|
|
@@ -165,7 +166,8 @@ const Toast = forwardRef(({ message, type = 'default', actionType, position = 't
|
|
|
165
166
|
}
|
|
166
167
|
const toastContent = jsx("div", { className: containerClasses, children: toastElement });
|
|
167
168
|
if (typeof document !== 'undefined') {
|
|
168
|
-
|
|
169
|
+
const target = resolveContainer(container);
|
|
170
|
+
return createPortal(jsx(LocalePortalScope, { target: target, children: toastContent }), target);
|
|
169
171
|
}
|
|
170
172
|
return null;
|
|
171
173
|
});
|
|
@@ -327,10 +329,11 @@ const ToastContainer = ({ position = 'top-right' }) => {
|
|
|
327
329
|
return null;
|
|
328
330
|
}
|
|
329
331
|
const stacks = groupIntoPositionStacks(toastList, t => t.props.position, position, toastStackOrder);
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
const target = resolveContainer();
|
|
333
|
+
return createPortal(jsx(LocalePortalScope, { target: target, children: stacks.map(stack => (jsx("div", { className: `toast-container is-${stack.position}`, children: stack.items.map(t => {
|
|
334
|
+
const { queue: _queue, ...toastProps } = t.props;
|
|
335
|
+
return (jsx(Toast, { ...toastProps, position: stack.position, onClose: () => toast.close(t.id) }, t.id));
|
|
336
|
+
}) }, stack.key))) }), target);
|
|
334
337
|
};
|
|
335
338
|
|
|
336
339
|
export { Toast, ToastContainer, toast };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.js","sources":["../../../src/components/Toast.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useEffect,\n useRef,\n useState,\n useCallback,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { isEventInside } from '../helpers/shadowDom';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { usePortalContainer } from '../helpers/portal';\nimport { groupIntoPositionStacks } from '../helpers/positionStacks';\nimport { useLatestRef } from '../helpers/useLatestRef';\nimport { useOverlayStack } from '../helpers/overlayStack';\n\n/** Color/style type presets for toast messages. */\nexport type ToastType =\n 'default' | 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n\n/** Screen positions where toasts can be displayed. */\nexport type ToastPosition =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right';\n\n/**\n * Props for the Toast component.\n * @extraProp {string} [className] - Additional CSS classes.\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the toast element.\n */\nexport interface ToastProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color'> {\n /** The message to display (required). */\n message: string;\n /** Color variant — colors the toast **background**. */\n type?: ToastType;\n /** Color variant — colors the **action button** text. */\n actionType?: ToastType;\n /**\n * Position on the screen. Default: 'top-right'. On a standalone `<Toast>`\n * this places it. Through `toast.show()` it places that toast, and a toast\n * shown without one goes to `ToastContainer`'s `position` instead.\n */\n position?: ToastPosition;\n /** Duration in ms before auto-close. `0` disables auto-close. */\n duration?: number;\n /** Keeps the toast visible until dismissed. */\n indefinite?: boolean;\n /** Whether clicking the toast (or outside it) dismisses it. */\n dismissible?: boolean;\n /** Show an explicit close (X) button. Default: false. */\n closable?: boolean;\n /** Pill-shaped (rounded corners). */\n rounded?: boolean;\n /** Pause auto-close timer on hover. Default: false. */\n pauseOnHover?: boolean;\n /**\n * Whether the toast can be dismissed with Escape. While a Modal, Dialog,\n * Sidebar or full-page Loading is open, Escape goes to that overlay and the\n * toast stays up, so Escape dismisses the toast once none is open. An\n * Escape a popup on the page closed on, by calling `preventDefault()`,\n * leaves it up too. Default: true.\n */\n cancelable?: boolean;\n /** Text for an action button (e.g. \"Undo\"). */\n actionText?: string;\n /** Text for a cancel button. */\n cancelText?: string;\n /** Callback when the action button is clicked. */\n onAction?: () => void;\n /** Callback when toast closes. */\n onClose?: () => void;\n /** CSS selector or DOM node to mount the toast into. */\n container?: string | HTMLElement;\n /** Renders inline instead of using a portal. */\n inline?: boolean;\n}\n\n/**\n * The `Toast` component provides brief notification messages with optional action and cancel buttons.\n *\n * @function\n * @param {ToastProps} props - Props for the Toast component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the toast element.\n * @returns {JSX.Element | null} The rendered toast component.\n *\n * @example\n * // Basic toast\n * <Toast message=\"Operation successful!\" type=\"success\" />\n *\n * @example\n * // Toast with action button\n * <Toast message=\"Item deleted\" actionText=\"Undo\" onAction={handleUndo} />\n */\nexport const Toast = forwardRef<HTMLDivElement, ToastProps>(\n (\n {\n message,\n type = 'default',\n actionType,\n position = 'top-right',\n duration = 2000,\n indefinite = false,\n dismissible = true,\n closable = false,\n rounded = false,\n pauseOnHover = false,\n cancelable = true,\n actionText,\n cancelText,\n onAction,\n onClose,\n container,\n inline = false,\n className,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const resolveContainer = usePortalContainer();\n const [isVisible, setIsVisible] = useState(true);\n const [isPaused, setIsPaused] = useState(false);\n const toastRef = useRef<HTMLDivElement | null>(null);\n\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n const handleClose = useCallback(() => {\n setIsVisible(false);\n onClose?.();\n }, [onClose]);\n\n const handleAction = useCallback(() => {\n onAction?.();\n handleClose();\n }, [onAction, handleClose]);\n\n // Read when the timer runs out, a click lands outside or Escape arrives,\n // so the timer and the listeners stay put while `onClose` changes\n // identity, as an inline one does on every render of the parent. An\n // inline one from `ToastContainer` changes whenever another toast shows.\n const handleCloseRef = useLatestRef(handleClose);\n\n useEffect(() => {\n if (indefinite || duration === 0 || isPaused) return undefined;\n\n const timer = setTimeout(() => {\n handleCloseRef.current();\n }, duration);\n\n return () => clearTimeout(timer);\n }, [duration, indefinite, isPaused, handleCloseRef]);\n\n const handleMouseEnter = useCallback(() => {\n if (pauseOnHover) {\n setIsPaused(true);\n }\n }, [pauseOnHover]);\n\n const handleMouseLeave = useCallback(() => {\n if (pauseOnHover) {\n setIsPaused(false);\n }\n }, [pauseOnHover]);\n\n // Click-outside dismiss — deferred so the click that spawned the toast\n // doesn't immediately dismiss it before it's even visible.\n useEffect(() => {\n if (!dismissible || !isVisible) return undefined;\n\n const handleDocumentClick = (e: MouseEvent) => {\n if (toastRef.current && !isEventInside(e, toastRef.current)) {\n handleCloseRef.current();\n }\n };\n\n const raf = requestAnimationFrame(() => {\n document.addEventListener('click', handleDocumentClick);\n });\n\n return () => {\n cancelAnimationFrame(raf);\n document.removeEventListener('click', handleDocumentClick);\n };\n }, [dismissible, isVisible, handleCloseRef]);\n\n // A toast is a notification rather than a layer, so it never joins the\n // stack of open overlays. Asking the stack tells it whether one is open:\n // an Escape while one is belongs to the overlay, and the toast waits.\n const isTopmost = useOverlayStack(false);\n\n // On `window`, as in `Modal`, so a popup on the page has had the key\n // first, and an Escape it closed on doesn't dismiss the toast as well.\n useEffect(() => {\n if (!cancelable || !isVisible) return undefined;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !e.defaultPrevented && isTopmost()) {\n handleCloseRef.current();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [cancelable, isVisible, handleCloseRef, isTopmost]);\n\n const containerClasses = usePrefixedClassNames('toast-container', {\n [`is-${position}`]: true,\n });\n\n const toastClasses = usePrefixedClassNames('toast', {\n [`is-${type}`]: type !== 'default',\n [`is-action-${actionType}`]: !!actionType && actionType !== 'default',\n 'is-rounded': rounded,\n });\n\n const combinedClasses = classNames(\n toastClasses,\n bulmaHelperClasses,\n className\n );\n const toastMessageClass = usePrefixedClassNames('toast-message');\n const toastActionsClass = usePrefixedClassNames('toast-actions');\n const toastCancelClass = usePrefixedClassNames('toast-cancel');\n const toastActionClass = usePrefixedClassNames('toast-action');\n const toastButtonClass = usePrefixedClassNames('button');\n const toastCloseClass = usePrefixedClassNames('delete', 'is-small');\n\n const setRef = useCallback(\n (node: HTMLDivElement | null) => {\n toastRef.current = node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches by\n // running it instead of calling the ref with `null`; React 18 discards the\n // return value entirely. Returning it from here would be a React-19-only\n // contract, so we hold the cleanup and run it ourselves on detach — the\n // same shape as `Dropdown`, `Modal`, `Dialog`, `Sidebar` and `Carousel`.\n // The form controls that merge a forwarded ref still discard the cleanup\n // and detach with `ref(null)`, so this is not yet library-wide — grep\n // `typeof ref === 'function'` for the current split. Memoized because an\n // unstable ref identity makes React detach and re-attach on every render,\n // which would run that cleanup each time.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n },\n [ref]\n );\n\n if (!isVisible) {\n return null;\n }\n\n const toastElement = (\n <div\n ref={setRef}\n className={combinedClasses}\n role=\"alert\"\n aria-live={\n type === 'danger' || type === 'warning' ? 'assertive' : 'polite'\n }\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onClick={dismissible ? handleClose : undefined}\n {...rest}\n >\n <span className={toastMessageClass}>{message}</span>\n {(cancelText || actionText) && (\n <div className={toastActionsClass}>\n {cancelText && (\n <span className={toastCancelClass}>\n <button\n type=\"button\"\n className={toastButtonClass}\n onClick={e => {\n e.stopPropagation();\n handleClose();\n }}\n >\n {cancelText}\n </button>\n </span>\n )}\n {actionText && (\n <span className={toastActionClass}>\n <button\n type=\"button\"\n className={toastButtonClass}\n onClick={e => {\n e.stopPropagation();\n handleAction();\n }}\n >\n {actionText}\n </button>\n </span>\n )}\n </div>\n )}\n {closable && (\n <button\n type=\"button\"\n className={toastCloseClass}\n onClick={e => {\n e.stopPropagation();\n handleClose();\n }}\n aria-label=\"Close\"\n />\n )}\n </div>\n );\n\n if (inline) {\n return toastElement;\n }\n\n const toastContent = <div className={containerClasses}>{toastElement}</div>;\n\n if (typeof document !== 'undefined') {\n return createPortal(toastContent, resolveContainer(container));\n }\n\n return null;\n }\n);\n\nToast.displayName = 'Toast';\n\n// Toast Manager for programmatic toasts\n\n/**\n * Options for showing a programmatic toast. Extends ToastProps with a required message.\n */\nexport interface ToastOptions extends Omit<ToastProps, 'message'> {\n /** The message to display. */\n message: string;\n /**\n * When true, toasts enter a FIFO queue and display one at a time, one queue\n * across every `position`. Default false.\n */\n queue?: boolean;\n}\n\n/**\n * Internal representation of a toast instance.\n */\nexport interface ToastInstance {\n /** Unique identifier for this toast. */\n id: string;\n /** Configuration options for the toast. */\n props: ToastOptions;\n}\n\nlet toastId = 0;\nconst toastListeners: Set<(toasts: ToastInstance[]) => void> = new Set();\nlet toasts: ToastInstance[] = [];\n\n// Queued toasts: FIFO queue, one at a time\nlet queuedToasts: ToastInstance[] = [];\nlet currentQueuedToast: ToastInstance | null = null;\n\n// The server has nowhere to portal a toast to, and its copy of this module is\n// shared by every request, so server rendering reads an empty list. Hydration\n// reads it too, which keeps the first client render matching the server's.\nconst noToasts: ToastInstance[] = [];\nconst getServerToasts = () => noToasts;\n\n// What a container shows right now: the stacked toasts, then the queued one on\n// screen. ToastContainer renders from this rather than from updates alone, so\n// toasts raised before it mounted still appear. It is replaced rather than\n// mutated, and only when listeners are notified, because useSyncExternalStore\n// needs the same array back between changes. An empty list is `noToasts`\n// itself, so a container that hydrates with nothing to show reads the same\n// snapshot the server did and has no reason to render again.\nlet visibleToasts: ToastInstance[] = noToasts;\nconst getVisibleToasts = () => visibleToasts;\n\nconst notifyListeners = () => {\n const allVisible = [...toasts];\n if (currentQueuedToast) {\n allVisible.push(currentQueuedToast);\n }\n visibleToasts = allVisible.length > 0 ? allVisible : noToasts;\n toastListeners.forEach(listener => listener([...allVisible]));\n};\n\nconst processQueuedToast = () => {\n if (currentQueuedToast || queuedToasts.length === 0) return;\n currentQueuedToast = queuedToasts.shift()!;\n notifyListeners();\n};\n\n/**\n * Programmatic toast API for showing, closing, and managing toasts.\n *\n * @example\n * toast.success('Saved!');\n * toast.show({ message: 'Item deleted', actionText: 'Undo', onAction: handleUndo });\n */\nexport const toast = {\n /**\n * Show a toast with the given options.\n * @param {ToastOptions} options - Toast configuration.\n * @returns {string} The unique ID of the created toast.\n */\n show: (options: ToastOptions): string => {\n const id = `toast-${++toastId}`;\n const instance = { id, props: options };\n\n if (options.queue) {\n queuedToasts.push(instance);\n processQueuedToast();\n } else {\n toasts.push(instance);\n notifyListeners();\n }\n\n return id;\n },\n\n /**\n * Show a success toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n success: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'success', ...options });\n },\n\n /**\n * Show a danger toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n danger: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'danger', ...options });\n },\n\n /**\n * Show a warning toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n warning: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'warning', ...options });\n },\n\n /**\n * Show an info toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n info: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'info', ...options });\n },\n\n /**\n * Close a specific toast by ID.\n * @param {string} id - The toast ID to close.\n */\n close: (id: string): void => {\n if (currentQueuedToast && currentQueuedToast.id === id) {\n currentQueuedToast = null;\n processQueuedToast();\n } else {\n queuedToasts = queuedToasts.filter(t => t.id !== id);\n toasts = toasts.filter(t => t.id !== id);\n }\n notifyListeners();\n },\n\n /** Close all toasts and clear the queue. */\n closeAll: (): void => {\n toasts = [];\n queuedToasts = [];\n currentQueuedToast = null;\n notifyListeners();\n },\n\n /**\n * Subscribe to toast state changes.\n * @param {(toasts: ToastInstance[]) => void} listener - Callback invoked on changes.\n * @returns {() => void} Unsubscribe function.\n */\n subscribe: (listener: (toasts: ToastInstance[]) => void): (() => void) => {\n toastListeners.add(listener);\n return () => toastListeners.delete(listener);\n },\n};\n\n// The order ToastContainer renders its stacks in: across the top of the\n// screen, then across the bottom.\nconst toastStackOrder: readonly ToastPosition[] = [\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n];\n\n/**\n * Container component for rendering programmatic toasts.\n * Place once at your app root to enable the toast API. A toast shown with a\n * `position` appears there, and one shown without goes to the container's\n * `position`.\n *\n * @function\n * @param {{ position?: ToastPosition }} props - Container props.\n * @returns {JSX.Element | null} The rendered toast container, or null if empty.\n */\nexport const ToastContainer: React.FC<{\n /**\n * Where a toast shown without a `position` of its own appears. Default:\n * 'top-right'. When it changes, those toasts move without remounting, but\n * one shown with its own `position` equal to the old or new value\n * remounts and starts over as if it had just been shown.\n */\n position?: ToastPosition;\n}> = ({ position = 'top-right' }) => {\n // Starts from the toasts already showing instead of an empty list, then\n // follows changes.\n const toastList = useSyncExternalStore(\n toast.subscribe,\n getVisibleToasts,\n getServerToasts\n );\n const resolveContainer = usePortalContainer();\n\n if (typeof document === 'undefined' || toastList.length === 0) {\n return null;\n }\n\n const stacks = groupIntoPositionStacks(\n toastList,\n t => t.props.position,\n position,\n toastStackOrder\n );\n\n return createPortal(\n stacks.map(stack => (\n <div key={stack.key} className={`toast-container is-${stack.position}`}>\n {stack.items.map(t => {\n const { queue: _queue, ...toastProps } = t.props;\n return (\n <Toast\n key={t.id}\n {...toastProps}\n position={stack.position}\n onClose={() => toast.close(t.id)}\n />\n );\n })}\n </div>\n )),\n resolveContainer()\n );\n};\n\nexport default Toast;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Toast.js","sources":["../../../src/components/Toast.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useEffect,\n useRef,\n useState,\n useCallback,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { isEventInside } from '../helpers/shadowDom';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { usePortalContainer } from '../helpers/portal';\nimport { LocalePortalScope } from '../helpers/LocaleProvider';\nimport { groupIntoPositionStacks } from '../helpers/positionStacks';\nimport { useLatestRef } from '../helpers/useLatestRef';\nimport { useOverlayStack } from '../helpers/overlayStack';\n\n/** Color/style type presets for toast messages. */\nexport type ToastType =\n 'default' | 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n\n/** Screen positions where toasts can be displayed. */\nexport type ToastPosition =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right';\n\n/**\n * Props for the Toast component.\n * @extraProp {string} [className] - Additional CSS classes.\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the toast element.\n */\nexport interface ToastProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color'> {\n /** The message to display (required). */\n message: string;\n /** Color variant — colors the toast **background**. */\n type?: ToastType;\n /** Color variant — colors the **action button** text. */\n actionType?: ToastType;\n /**\n * Position on the screen. Default: 'top-right'. On a standalone `<Toast>`\n * this places it. Through `toast.show()` it places that toast, and a toast\n * shown without one goes to `ToastContainer`'s `position` instead.\n */\n position?: ToastPosition;\n /** Duration in ms before auto-close. `0` disables auto-close. */\n duration?: number;\n /** Keeps the toast visible until dismissed. */\n indefinite?: boolean;\n /** Whether clicking the toast (or outside it) dismisses it. */\n dismissible?: boolean;\n /** Show an explicit close (X) button. Default: false. */\n closable?: boolean;\n /** Pill-shaped (rounded corners). */\n rounded?: boolean;\n /** Pause auto-close timer on hover. Default: false. */\n pauseOnHover?: boolean;\n /**\n * Whether the toast can be dismissed with Escape. While a Modal, Dialog,\n * Sidebar or full-page Loading is open, Escape goes to that overlay and the\n * toast stays up, so Escape dismisses the toast once none is open. An\n * Escape a popup on the page closed on, by calling `preventDefault()`,\n * leaves it up too. Default: true.\n */\n cancelable?: boolean;\n /** Text for an action button (e.g. \"Undo\"). */\n actionText?: string;\n /** Text for a cancel button. */\n cancelText?: string;\n /** Callback when the action button is clicked. */\n onAction?: () => void;\n /** Callback when toast closes. */\n onClose?: () => void;\n /** CSS selector or DOM node to mount the toast into. */\n container?: string | HTMLElement;\n /** Renders inline instead of using a portal. */\n inline?: boolean;\n}\n\n/**\n * The `Toast` component provides brief notification messages with optional action and cancel buttons.\n *\n * @function\n * @param {ToastProps} props - Props for the Toast component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the toast element.\n * @returns {JSX.Element | null} The rendered toast component.\n *\n * @example\n * // Basic toast\n * <Toast message=\"Operation successful!\" type=\"success\" />\n *\n * @example\n * // Toast with action button\n * <Toast message=\"Item deleted\" actionText=\"Undo\" onAction={handleUndo} />\n */\nexport const Toast = forwardRef<HTMLDivElement, ToastProps>(\n (\n {\n message,\n type = 'default',\n actionType,\n position = 'top-right',\n duration = 2000,\n indefinite = false,\n dismissible = true,\n closable = false,\n rounded = false,\n pauseOnHover = false,\n cancelable = true,\n actionText,\n cancelText,\n onAction,\n onClose,\n container,\n inline = false,\n className,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const resolveContainer = usePortalContainer();\n const [isVisible, setIsVisible] = useState(true);\n const [isPaused, setIsPaused] = useState(false);\n const toastRef = useRef<HTMLDivElement | null>(null);\n\n // Cleanup returned by a consumer's callback ref on attach, held until detach.\n const consumerCleanupRef = useRef<(() => void) | null>(null);\n const handleClose = useCallback(() => {\n setIsVisible(false);\n onClose?.();\n }, [onClose]);\n\n const handleAction = useCallback(() => {\n onAction?.();\n handleClose();\n }, [onAction, handleClose]);\n\n // Read when the timer runs out, a click lands outside or Escape arrives,\n // so the timer and the listeners stay put while `onClose` changes\n // identity, as an inline one does on every render of the parent. An\n // inline one from `ToastContainer` changes whenever another toast shows.\n const handleCloseRef = useLatestRef(handleClose);\n\n useEffect(() => {\n if (indefinite || duration === 0 || isPaused) return undefined;\n\n const timer = setTimeout(() => {\n handleCloseRef.current();\n }, duration);\n\n return () => clearTimeout(timer);\n }, [duration, indefinite, isPaused, handleCloseRef]);\n\n const handleMouseEnter = useCallback(() => {\n if (pauseOnHover) {\n setIsPaused(true);\n }\n }, [pauseOnHover]);\n\n const handleMouseLeave = useCallback(() => {\n if (pauseOnHover) {\n setIsPaused(false);\n }\n }, [pauseOnHover]);\n\n // Click-outside dismiss — deferred so the click that spawned the toast\n // doesn't immediately dismiss it before it's even visible.\n useEffect(() => {\n if (!dismissible || !isVisible) return undefined;\n\n const handleDocumentClick = (e: MouseEvent) => {\n if (toastRef.current && !isEventInside(e, toastRef.current)) {\n handleCloseRef.current();\n }\n };\n\n const raf = requestAnimationFrame(() => {\n document.addEventListener('click', handleDocumentClick);\n });\n\n return () => {\n cancelAnimationFrame(raf);\n document.removeEventListener('click', handleDocumentClick);\n };\n }, [dismissible, isVisible, handleCloseRef]);\n\n // A toast is a notification rather than a layer, so it never joins the\n // stack of open overlays. Asking the stack tells it whether one is open:\n // an Escape while one is belongs to the overlay, and the toast waits.\n const isTopmost = useOverlayStack(false);\n\n // On `window`, as in `Modal`, so a popup on the page has had the key\n // first, and an Escape it closed on doesn't dismiss the toast as well.\n useEffect(() => {\n if (!cancelable || !isVisible) return undefined;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !e.defaultPrevented && isTopmost()) {\n handleCloseRef.current();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [cancelable, isVisible, handleCloseRef, isTopmost]);\n\n const containerClasses = usePrefixedClassNames('toast-container', {\n [`is-${position}`]: true,\n });\n\n const toastClasses = usePrefixedClassNames('toast', {\n [`is-${type}`]: type !== 'default',\n [`is-action-${actionType}`]: !!actionType && actionType !== 'default',\n 'is-rounded': rounded,\n });\n\n const combinedClasses = classNames(\n toastClasses,\n bulmaHelperClasses,\n className\n );\n const toastMessageClass = usePrefixedClassNames('toast-message');\n const toastActionsClass = usePrefixedClassNames('toast-actions');\n const toastCancelClass = usePrefixedClassNames('toast-cancel');\n const toastActionClass = usePrefixedClassNames('toast-action');\n const toastButtonClass = usePrefixedClassNames('button');\n const toastCloseClass = usePrefixedClassNames('delete', 'is-small');\n\n const setRef = useCallback(\n (node: HTMLDivElement | null) => {\n toastRef.current = node;\n if (typeof ref === 'function') {\n // React 19 lets a callback ref return a cleanup function and detaches by\n // running it instead of calling the ref with `null`; React 18 discards the\n // return value entirely. Returning it from here would be a React-19-only\n // contract, so we hold the cleanup and run it ourselves on detach — the\n // same shape as `Dropdown`, `Modal`, `Dialog`, `Sidebar` and `Carousel`.\n // The form controls that merge a forwarded ref still discard the cleanup\n // and detach with `ref(null)`, so this is not yet library-wide — grep\n // `typeof ref === 'function'` for the current split. Memoized because an\n // unstable ref identity makes React detach and re-attach on every render,\n // which would run that cleanup each time.\n if (node === null) {\n const consumerCleanup = consumerCleanupRef.current;\n consumerCleanupRef.current = null;\n if (consumerCleanup) {\n consumerCleanup();\n } else {\n ref(null);\n }\n return;\n }\n const cleanup: unknown = ref(node);\n consumerCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n },\n [ref]\n );\n\n if (!isVisible) {\n return null;\n }\n\n const toastElement = (\n <div\n ref={setRef}\n className={combinedClasses}\n role=\"alert\"\n aria-live={\n type === 'danger' || type === 'warning' ? 'assertive' : 'polite'\n }\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onClick={dismissible ? handleClose : undefined}\n {...rest}\n >\n <span className={toastMessageClass}>{message}</span>\n {(cancelText || actionText) && (\n <div className={toastActionsClass}>\n {cancelText && (\n <span className={toastCancelClass}>\n <button\n type=\"button\"\n className={toastButtonClass}\n onClick={e => {\n e.stopPropagation();\n handleClose();\n }}\n >\n {cancelText}\n </button>\n </span>\n )}\n {actionText && (\n <span className={toastActionClass}>\n <button\n type=\"button\"\n className={toastButtonClass}\n onClick={e => {\n e.stopPropagation();\n handleAction();\n }}\n >\n {actionText}\n </button>\n </span>\n )}\n </div>\n )}\n {closable && (\n <button\n type=\"button\"\n className={toastCloseClass}\n onClick={e => {\n e.stopPropagation();\n handleClose();\n }}\n aria-label=\"Close\"\n />\n )}\n </div>\n );\n\n if (inline) {\n return toastElement;\n }\n\n const toastContent = <div className={containerClasses}>{toastElement}</div>;\n\n if (typeof document !== 'undefined') {\n const target = resolveContainer(container);\n return createPortal(\n <LocalePortalScope target={target}>{toastContent}</LocalePortalScope>,\n target\n );\n }\n\n return null;\n }\n);\n\nToast.displayName = 'Toast';\n\n// Toast Manager for programmatic toasts\n\n/**\n * Options for showing a programmatic toast. Extends ToastProps with a required message.\n */\nexport interface ToastOptions extends Omit<ToastProps, 'message'> {\n /** The message to display. */\n message: string;\n /**\n * When true, toasts enter a FIFO queue and display one at a time, one queue\n * across every `position`. Default false.\n */\n queue?: boolean;\n}\n\n/**\n * Internal representation of a toast instance.\n */\nexport interface ToastInstance {\n /** Unique identifier for this toast. */\n id: string;\n /** Configuration options for the toast. */\n props: ToastOptions;\n}\n\nlet toastId = 0;\nconst toastListeners: Set<(toasts: ToastInstance[]) => void> = new Set();\nlet toasts: ToastInstance[] = [];\n\n// Queued toasts: FIFO queue, one at a time\nlet queuedToasts: ToastInstance[] = [];\nlet currentQueuedToast: ToastInstance | null = null;\n\n// The server has nowhere to portal a toast to, and its copy of this module is\n// shared by every request, so server rendering reads an empty list. Hydration\n// reads it too, which keeps the first client render matching the server's.\nconst noToasts: ToastInstance[] = [];\nconst getServerToasts = () => noToasts;\n\n// What a container shows right now: the stacked toasts, then the queued one on\n// screen. ToastContainer renders from this rather than from updates alone, so\n// toasts raised before it mounted still appear. It is replaced rather than\n// mutated, and only when listeners are notified, because useSyncExternalStore\n// needs the same array back between changes. An empty list is `noToasts`\n// itself, so a container that hydrates with nothing to show reads the same\n// snapshot the server did and has no reason to render again.\nlet visibleToasts: ToastInstance[] = noToasts;\nconst getVisibleToasts = () => visibleToasts;\n\nconst notifyListeners = () => {\n const allVisible = [...toasts];\n if (currentQueuedToast) {\n allVisible.push(currentQueuedToast);\n }\n visibleToasts = allVisible.length > 0 ? allVisible : noToasts;\n toastListeners.forEach(listener => listener([...allVisible]));\n};\n\nconst processQueuedToast = () => {\n if (currentQueuedToast || queuedToasts.length === 0) return;\n currentQueuedToast = queuedToasts.shift()!;\n notifyListeners();\n};\n\n/**\n * Programmatic toast API for showing, closing, and managing toasts.\n *\n * @example\n * toast.success('Saved!');\n * toast.show({ message: 'Item deleted', actionText: 'Undo', onAction: handleUndo });\n */\nexport const toast = {\n /**\n * Show a toast with the given options.\n * @param {ToastOptions} options - Toast configuration.\n * @returns {string} The unique ID of the created toast.\n */\n show: (options: ToastOptions): string => {\n const id = `toast-${++toastId}`;\n const instance = { id, props: options };\n\n if (options.queue) {\n queuedToasts.push(instance);\n processQueuedToast();\n } else {\n toasts.push(instance);\n notifyListeners();\n }\n\n return id;\n },\n\n /**\n * Show a success toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n success: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'success', ...options });\n },\n\n /**\n * Show a danger toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n danger: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'danger', ...options });\n },\n\n /**\n * Show a warning toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n warning: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'warning', ...options });\n },\n\n /**\n * Show an info toast.\n * @param {string} message - The message to display.\n * @param {Partial<ToastOptions>} [options] - Additional options.\n * @returns {string} The toast ID.\n */\n info: (message: string, options?: Partial<ToastOptions>): string => {\n return toast.show({ message, type: 'info', ...options });\n },\n\n /**\n * Close a specific toast by ID.\n * @param {string} id - The toast ID to close.\n */\n close: (id: string): void => {\n if (currentQueuedToast && currentQueuedToast.id === id) {\n currentQueuedToast = null;\n processQueuedToast();\n } else {\n queuedToasts = queuedToasts.filter(t => t.id !== id);\n toasts = toasts.filter(t => t.id !== id);\n }\n notifyListeners();\n },\n\n /** Close all toasts and clear the queue. */\n closeAll: (): void => {\n toasts = [];\n queuedToasts = [];\n currentQueuedToast = null;\n notifyListeners();\n },\n\n /**\n * Subscribe to toast state changes.\n * @param {(toasts: ToastInstance[]) => void} listener - Callback invoked on changes.\n * @returns {() => void} Unsubscribe function.\n */\n subscribe: (listener: (toasts: ToastInstance[]) => void): (() => void) => {\n toastListeners.add(listener);\n return () => toastListeners.delete(listener);\n },\n};\n\n// The order ToastContainer renders its stacks in: across the top of the\n// screen, then across the bottom.\nconst toastStackOrder: readonly ToastPosition[] = [\n 'top-left',\n 'top-center',\n 'top-right',\n 'bottom-left',\n 'bottom-center',\n 'bottom-right',\n];\n\n/**\n * Container component for rendering programmatic toasts.\n * Place once at your app root to enable the toast API. A toast shown with a\n * `position` appears there, and one shown without goes to the container's\n * `position`.\n *\n * @function\n * @param {{ position?: ToastPosition }} props - Container props.\n * @returns {JSX.Element | null} The rendered toast container, or null if empty.\n */\nexport const ToastContainer: React.FC<{\n /**\n * Where a toast shown without a `position` of its own appears. Default:\n * 'top-right'. When it changes, those toasts move without remounting, but\n * one shown with its own `position` equal to the old or new value\n * remounts and starts over as if it had just been shown.\n */\n position?: ToastPosition;\n}> = ({ position = 'top-right' }) => {\n // Starts from the toasts already showing instead of an empty list, then\n // follows changes.\n const toastList = useSyncExternalStore(\n toast.subscribe,\n getVisibleToasts,\n getServerToasts\n );\n const resolveContainer = usePortalContainer();\n\n if (typeof document === 'undefined' || toastList.length === 0) {\n return null;\n }\n\n const stacks = groupIntoPositionStacks(\n toastList,\n t => t.props.position,\n position,\n toastStackOrder\n );\n\n const target = resolveContainer();\n return createPortal(\n <LocalePortalScope target={target}>\n {stacks.map(stack => (\n <div key={stack.key} className={`toast-container is-${stack.position}`}>\n {stack.items.map(t => {\n const { queue: _queue, ...toastProps } = t.props;\n return (\n <Toast\n key={t.id}\n {...toastProps}\n position={stack.position}\n onClose={() => toast.close(t.id)}\n />\n );\n })}\n </div>\n ))}\n </LocalePortalScope>,\n target\n );\n};\n\nexport default Toast;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
-
import { jsxs, jsx
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { useSyncExternalStore, useMemo, useRef, useCallback, useState, useEffect } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
@@ -8,6 +8,7 @@ import { warnUnstyledColor } from '../helpers/colorDeprecations.js';
|
|
|
8
8
|
import { groupIntoPositionStacks } from '../helpers/positionStacks.js';
|
|
9
9
|
import { useIsHydrated } from '../helpers/useIsHydrated.js';
|
|
10
10
|
import { usePortalContainer } from '../helpers/portal.js';
|
|
11
|
+
import { LocalePortalScope } from '../helpers/LocaleProvider.js';
|
|
11
12
|
import { spokenText, StatusRegion } from '../helpers/statusRegion.js';
|
|
12
13
|
import { validColors } from '../helpers/bulmaClassHelpers.js';
|
|
13
14
|
|
|
@@ -312,7 +313,8 @@ const NotificationContainer = ({ position = 'top-right' }) => {
|
|
|
312
313
|
return null;
|
|
313
314
|
}
|
|
314
315
|
const stacks = groupIntoPositionStacks(items, item => item.options.position, position, notificationStackOrder);
|
|
315
|
-
|
|
316
|
+
const target = resolveContainer();
|
|
317
|
+
return createPortal(jsxs(LocalePortalScope, { target: target, children: [stacks.map(stack => (jsx("div", { style: notificationStackStyle(stack.position), children: stack.items.map(item => (jsx(NotificationItem, { instance: item, onClose: notification.close, registerMessage: registerMessage }, item.id))) }, stack.key))), jsx(StatusRegion, { items: politeItems, describe: describe })] }), target);
|
|
316
318
|
};
|
|
317
319
|
|
|
318
320
|
export { Notification, NotificationContainer, notification };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Notification.js","sources":["../../../src/elements/Notification.tsx"],"sourcesContent":["import React, {\n useEffect,\n useMemo,\n useRef,\n useState,\n useCallback,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { warnUnstyledColor } from '../helpers/colorDeprecations';\nimport { groupIntoPositionStacks } from '../helpers/positionStacks';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\nimport { usePortalContainer } from '../helpers/portal';\nimport { StatusRegion, spokenText } from '../helpers/statusRegion';\n\n/**\n * Props for the Notification component.\n */\nexport interface NotificationProps\n extends\n React.HTMLAttributes<HTMLDivElement>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * Bulma color modifier for the notification (renders `is-<color>`).\n *\n * Only `primary`, `link`, `info`, `success`, `warning`, `danger`, `black`,\n * `white`, `light`, and `dark` have shipped CSS for `.notification`. Every\n * other value the union accepts emits a class no CSS rule matches, so the\n * notification renders unstyled; those log a console warning in development\n * and will be removed from this union in the next major version.\n */\n color?: (typeof validColors)[number];\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Use the light color variant. */\n isLight?: boolean;\n /**\n * Shows a close (delete) button in the notification. It renders\n * `type=\"button\"`, so it does not submit a form around it.\n *\n * The notification also takes `has-delete`, which pads its end by\n * `--bulma-notification-delete-padding-inline-end` so the text clears the\n * button. That rule ships in bestax's stylesheets, not Bulma's, so an app\n * styled by Bulma's CSS alone keeps Bulma's padding.\n */\n hasDelete?: boolean;\n /** Callback fired when the delete button is clicked. */\n onDelete?: () => void;\n /** Content inside the notification. */\n children?: React.ReactNode;\n}\n\n/**\n * The `Notification` component is a Bulma-styled alert/message area for providing feedback, warnings, or information to users.\n *\n * @function\n * @param {NotificationProps} props - Props for the Notification component.\n * @returns {JSX.Element} The rendered notification element.\n * @see {@link https://bulma.io/documentation/elements/notification/ | Bulma Notification documentation}\n */\nexport const Notification: React.FC<NotificationProps> = ({\n className,\n color,\n textColor,\n isLight,\n hasDelete,\n onDelete,\n children,\n ...props\n}) => {\n warnUnstyledColor('Notification', color);\n\n /**\n * Generates Bulma helper classes and separates out remaining props.\n */\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n ...props,\n });\n\n const bulmaClasses = usePrefixedClassNames('notification', {\n [`is-${color}`]: color && validColors.includes(color),\n 'is-light': isLight,\n 'has-delete': hasDelete,\n });\n\n const deleteClasses = usePrefixedClassNames('delete');\n\n const notificationClasses = classNames(\n bulmaClasses,\n bulmaHelperClasses,\n className\n );\n\n return (\n <div className={notificationClasses} {...rest}>\n {hasDelete && (\n <button\n type=\"button\"\n className={deleteClasses}\n onClick={onDelete}\n aria-label=\"Close notification\"\n />\n )}\n {children}\n </div>\n );\n};\n\n// Programmatic Notification API\n\n/** Screen positions where programmatic notifications can be displayed. */\nexport type NotificationPosition =\n 'top-left' | 'top' | 'top-right' | 'bottom-left' | 'bottom' | 'bottom-right';\n\n/**\n * Options for showing a programmatic notification.\n */\nexport interface NotificationOptions {\n /**\n * The message to display.\n *\n * For a notification other than `danger` and `warning`, NotificationContainer\n * announces the text the message renders, with an element's `aria-label`,\n * or an image's `alt`, standing in for what's inside it, a break between\n * block-level elements, and parts that are `aria-hidden`, `hidden` or\n * inline-styled `display: none` left out. Other ways of naming or hiding\n * content, such as `aria-labelledby`, CSS-generated content or a\n * stylesheet's `display: none`, aren't followed, so a message that relies on\n * them can be announced differently from what a screen reader would read.\n */\n message: string | React.ReactNode;\n /**\n * Bulma color modifier for the notification (renders `is-<color>`).\n *\n * Only `primary`, `link`, `info`, `success`, `warning`, `danger`, `black`,\n * `white`, `light`, and `dark` have shipped CSS for `.notification`. Every\n * other value the union accepts emits a class no CSS rule matches, so the\n * notification renders unstyled; those log a console warning in development\n * and will be removed from this union in the next major version.\n */\n color?: (typeof validColors)[number];\n /** Use the light color variant. */\n isLight?: boolean;\n /** Duration in ms before auto-close. Default 3000. */\n duration?: number;\n /**\n * Position on the screen. A notification shown without one goes to\n * `NotificationContainer`'s `position`.\n */\n position?: NotificationPosition;\n /**\n * When true, notifications enter a FIFO queue and display one at a time,\n * one queue across every `position`. Default false.\n */\n queue?: boolean;\n /** Show a delete (close) button. Default true. */\n hasDelete?: boolean;\n /** Stay open until dismissed. */\n indefinite?: boolean;\n /** Pause auto-close timer on hover. Default true. */\n pauseOnHover?: boolean;\n}\n\n/**\n * Internal representation of a notification instance.\n */\ninterface NotificationInstance {\n /** Unique identifier for this notification. */\n id: string;\n /** Configuration options for the notification. */\n options: NotificationOptions;\n}\n\nlet notificationId = 0;\nconst notificationListeners: Set<(items: NotificationInstance[]) => void> =\n new Set();\nlet notifications: NotificationInstance[] = [];\n\n// Queue support\nlet queuedNotifications: NotificationInstance[] = [];\nlet currentQueuedNotification: NotificationInstance | null = null;\n\n// The server has nowhere to portal a notification to, and its copy of this\n// module is shared by every request, so server rendering reads an empty list.\n// Hydration reads it too, which keeps the first client render matching the\n// server's.\nconst noNotifications: NotificationInstance[] = [];\nconst getServerNotifications = () => noNotifications;\n\n// What a container shows right now: the stacked notifications, then the queued\n// one on screen. NotificationContainer renders from this rather than from\n// updates alone, so notifications raised before it mounted still appear. It is\n// replaced rather than mutated, and only when listeners are notified, because\n// useSyncExternalStore needs the same array back between changes. An empty\n// list is `noNotifications` itself, so an empty store reads the same snapshot\n// on the client as on the server, and a notify that leaves nothing showing\n// when nothing was showing doesn't render the container again.\nlet visibleNotifications: NotificationInstance[] = noNotifications;\nconst getVisibleNotifications = () => visibleNotifications;\n\nconst notifyNotificationListeners = () => {\n const allVisible = [...notifications];\n if (currentQueuedNotification) {\n allVisible.push(currentQueuedNotification);\n }\n visibleNotifications = allVisible.length > 0 ? allVisible : noNotifications;\n notificationListeners.forEach(listener => listener([...allVisible]));\n};\n\nconst processQueuedNotification = () => {\n if (currentQueuedNotification || queuedNotifications.length === 0) return;\n currentQueuedNotification = queuedNotifications.shift()!;\n notifyNotificationListeners();\n};\n\n/**\n * Programmatic notification API for showing, closing, and managing notifications.\n *\n * @example\n * notification.success('File saved successfully');\n * notification.danger('Something went wrong', { duration: 5000 });\n */\nexport const notification = {\n /**\n * Show a notification with the given options.\n * @param {NotificationOptions} options - Notification configuration.\n * @returns {string} The unique ID of the created notification.\n */\n show: (options: NotificationOptions): string => {\n const id = `notification-${++notificationId}`;\n const instance = { id, options };\n\n if (options.queue) {\n queuedNotifications.push(instance);\n processQueuedNotification();\n } else {\n notifications.push(instance);\n notifyNotificationListeners();\n }\n\n return id;\n },\n\n /**\n * Show a success notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n success: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'success', ...options });\n },\n\n /**\n * Show a danger notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n danger: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'danger', ...options });\n },\n\n /**\n * Show a warning notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n warning: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'warning', ...options });\n },\n\n /**\n * Show an info notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n info: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'info', ...options });\n },\n\n /**\n * Close a specific notification by ID.\n * @param {string} id - The notification ID to close.\n */\n close: (id: string): void => {\n if (currentQueuedNotification && currentQueuedNotification.id === id) {\n currentQueuedNotification = null;\n processQueuedNotification();\n } else {\n queuedNotifications = queuedNotifications.filter(n => n.id !== id);\n notifications = notifications.filter(n => n.id !== id);\n }\n notifyNotificationListeners();\n },\n\n /** Close all notifications and clear the queue. */\n closeAll: (): void => {\n notifications = [];\n queuedNotifications = [];\n currentQueuedNotification = null;\n notifyNotificationListeners();\n },\n\n /**\n * Subscribe to notification state changes.\n * @param {(items: NotificationInstance[]) => void} listener - Callback invoked on changes.\n * @returns {() => void} Unsubscribe function.\n */\n subscribe: (\n listener: (items: NotificationInstance[]) => void\n ): (() => void) => {\n notificationListeners.add(listener);\n return () => notificationListeners.delete(listener);\n },\n};\n\n/**\n * Whether a notification of this color announces itself as an alert, rather\n * than politely through NotificationContainer's status region.\n *\n * @function isUrgentColor\n * @param {NotificationOptions['color']} color - The notification's color.\n * @returns {boolean} True for `danger` and `warning`.\n */\nconst isUrgentColor = (color: NotificationOptions['color']): boolean =>\n color === 'danger' || color === 'warning';\n\n/**\n * Single auto-dismissing notification item used by NotificationContainer.\n *\n * @function\n * @param {{ instance: NotificationInstance; onClose: (id: string) => void; registerMessage: (id: string, node: HTMLElement | null) => void }} props - Component props.\n * @returns {JSX.Element} The rendered notification item.\n */\nconst NotificationItem: React.FC<{\n instance: NotificationInstance;\n onClose: (id: string) => void;\n registerMessage: (id: string, node: HTMLElement | null) => void;\n}> = ({ instance, onClose, registerMessage }) => {\n const {\n message,\n color,\n isLight,\n duration = 3000,\n hasDelete = true,\n indefinite = false,\n pauseOnHover = true,\n } = instance.options;\n\n const [isPaused, setIsPaused] = useState(false);\n const urgent = isUrgentColor(color);\n\n const handleClose = useCallback(() => {\n onClose(instance.id);\n }, [onClose, instance.id]);\n\n const messageRef = useCallback(\n (node: HTMLElement | null) => {\n registerMessage(instance.id, node);\n },\n [registerMessage, instance.id]\n );\n\n // Auto-close timer\n useEffect(() => {\n if (indefinite || duration === 0 || isPaused) return undefined;\n\n const timer = setTimeout(handleClose, duration);\n return () => clearTimeout(timer);\n }, [duration, indefinite, isPaused, handleClose]);\n\n const handleMouseEnter = useCallback(() => {\n if (pauseOnHover) setIsPaused(true);\n }, [pauseOnHover]);\n\n const handleMouseLeave = useCallback(() => {\n if (pauseOnHover) setIsPaused(false);\n }, [pauseOnHover]);\n\n return (\n <Notification\n color={color}\n isLight={isLight}\n hasDelete={hasDelete}\n onDelete={handleClose}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n style={{ pointerEvents: 'auto' }}\n >\n {/* Only the message is announced, not the close button Notification\n renders ahead of its children. Danger and warning announce\n themselves as an assertive alert, which a screen reader reliably\n reads out even when it arrives with its text. The rest are announced\n through the container's status region, which reads their text from\n this span, so it carries no role of its own that would announce them\n a second time. */}\n {urgent ? (\n <span role=\"alert\" aria-live=\"assertive\">\n {message}\n </span>\n ) : (\n <span ref={messageRef}>{message}</span>\n )}\n </Notification>\n );\n};\n\n// The order NotificationContainer renders its stacks in: across the top of the\n// screen, then across the bottom.\nconst notificationStackOrder: readonly NotificationPosition[] = [\n 'top-left',\n 'top',\n 'top-right',\n 'bottom-left',\n 'bottom',\n 'bottom-right',\n];\n\n/**\n * Inline style that fixes a stack of notifications to its place on the screen.\n *\n * @function\n * @param {NotificationPosition} position - Where the stack sits.\n * @returns {React.CSSProperties} The stack's style.\n */\nconst notificationStackStyle = (\n position: NotificationPosition\n): React.CSSProperties => {\n const isBottom = position.startsWith('bottom');\n const isCenter = position === 'top' || position === 'bottom';\n const isRight = position.endsWith('right');\n\n return {\n position: 'fixed',\n zIndex: 100,\n display: 'flex',\n flexDirection: isBottom ? 'column-reverse' : 'column',\n gap: '0.75rem',\n padding: '1rem',\n pointerEvents: 'none',\n maxWidth: '100%',\n ...(isBottom ? { bottom: 0 } : { top: 0 }),\n ...(isCenter\n ? { left: '50%', transform: 'translateX(-50%)', alignItems: 'center' }\n : isRight\n ? { right: 0, alignItems: 'flex-end' }\n : { left: 0, alignItems: 'flex-start' }),\n };\n};\n\n/**\n * Container component for rendering programmatic notifications.\n * Place once at your app root to enable the notification API. A notification\n * shown with a `position` appears there, and one shown without goes to the\n * container's `position`, so the container renders a stack for each position\n * in use.\n *\n * It keeps a visually hidden `role=\"status\"` live region in the page from the\n * moment it mounts, even while nothing is showing, and announces every\n * notification other than `danger` and `warning` through it, a moment after\n * the notification appears. Those notifications carry no `role=\"status\"` of\n * their own, so `getByRole('status')` finds the region, not the notification.\n * The text stays in the region briefly, even if the notification closes in\n * the meantime, and while both are up the page holds two copies of it. A\n * polite notification that closes before its announcement is written, a\n * moment after it appears, isn't announced at all. `danger` and `warning`\n * notifications announce themselves as assertive alerts. The region is hidden\n * with inline styles, so it needs no stylesheet.\n *\n * @function\n * @param {{ position?: NotificationPosition }} props - Container props.\n * @returns {JSX.Element | null} The rendered notification container, or null on the server and while hydrating.\n */\nexport const NotificationContainer: React.FC<{\n /**\n * Where a notification shown without a `position` of its own appears.\n * Default: 'top-right'. When it changes, those notifications move without\n * remounting, but one shown with its own `position` equal to the old or new\n * value remounts and starts over as if it had just been shown.\n */\n position?: NotificationPosition;\n}> = ({ position = 'top-right' }) => {\n // Starts from the notifications already showing instead of an empty list,\n // then follows changes.\n const items = useSyncExternalStore(\n notification.subscribe,\n getVisibleNotifications,\n getServerNotifications\n );\n // A portal needs a document, and it has no server-rendered counterpart, so\n // the server render and the hydrating one render nothing, and the container\n // portals in from the render after hydration.\n const isHydrated = useIsHydrated();\n const resolveContainer = usePortalContainer();\n\n // The notifications the status region announces. Danger and warning\n // announce themselves.\n const politeItems = useMemo(\n () => items.filter(item => !isUrgentColor(item.options.color)),\n [items]\n );\n\n // Each polite notification's message element, so the text the region\n // announces is the text on screen, whatever the message renders.\n const messageNodesRef = useRef(new Map<string, HTMLElement>());\n const registerMessage = useCallback(\n (id: string, node: HTMLElement | null) => {\n if (node) {\n messageNodesRef.current.set(id, node);\n } else {\n messageNodesRef.current.delete(id);\n }\n },\n []\n );\n // StatusRegion only describes polite notifications that are on screen, and\n // each of those registered its message element when it mounted. The\n // fallback is for a notification announced without being rendered, should\n // the container ever render fewer than it announces: a plain-text message\n // still says itself, and anything else is skipped.\n const describe = useCallback((item: NotificationInstance) => {\n const node = messageNodesRef.current.get(item.id);\n /* istanbul ignore if: every polite notification on screen registers its message element when it mounts */\n if (!node) {\n const { message } = item.options;\n return typeof message === 'string' ? message : null;\n }\n return spokenText(node);\n }, []);\n\n if (typeof document === 'undefined' || !isHydrated) {\n return null;\n }\n\n const stacks = groupIntoPositionStacks(\n items,\n item => item.options.position,\n position,\n notificationStackOrder\n );\n\n return createPortal(\n <>\n {stacks.map(stack => (\n <div key={stack.key} style={notificationStackStyle(stack.position)}>\n {stack.items.map(item => (\n <NotificationItem\n key={item.id}\n instance={item}\n onClose={notification.close}\n registerMessage={registerMessage}\n />\n ))}\n </div>\n ))}\n <StatusRegion items={politeItems} describe={describe} />\n </>,\n resolveContainer()\n );\n};\n"],"names":["_jsxs","_jsx","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Notification.js","sources":["../../../src/elements/Notification.tsx"],"sourcesContent":["import React, {\n useEffect,\n useMemo,\n useRef,\n useState,\n useCallback,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { warnUnstyledColor } from '../helpers/colorDeprecations';\nimport { groupIntoPositionStacks } from '../helpers/positionStacks';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\nimport { usePortalContainer } from '../helpers/portal';\nimport { LocalePortalScope } from '../helpers/LocaleProvider';\nimport { StatusRegion, spokenText } from '../helpers/statusRegion';\n\n/**\n * Props for the Notification component.\n */\nexport interface NotificationProps\n extends\n React.HTMLAttributes<HTMLDivElement>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * Bulma color modifier for the notification (renders `is-<color>`).\n *\n * Only `primary`, `link`, `info`, `success`, `warning`, `danger`, `black`,\n * `white`, `light`, and `dark` have shipped CSS for `.notification`. Every\n * other value the union accepts emits a class no CSS rule matches, so the\n * notification renders unstyled; those log a console warning in development\n * and will be removed from this union in the next major version.\n */\n color?: (typeof validColors)[number];\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Use the light color variant. */\n isLight?: boolean;\n /**\n * Shows a close (delete) button in the notification. It renders\n * `type=\"button\"`, so it does not submit a form around it.\n *\n * The notification also takes `has-delete`, which pads its end by\n * `--bulma-notification-delete-padding-inline-end` so the text clears the\n * button. That rule ships in bestax's stylesheets, not Bulma's, so an app\n * styled by Bulma's CSS alone keeps Bulma's padding.\n */\n hasDelete?: boolean;\n /** Callback fired when the delete button is clicked. */\n onDelete?: () => void;\n /** Content inside the notification. */\n children?: React.ReactNode;\n}\n\n/**\n * The `Notification` component is a Bulma-styled alert/message area for providing feedback, warnings, or information to users.\n *\n * @function\n * @param {NotificationProps} props - Props for the Notification component.\n * @returns {JSX.Element} The rendered notification element.\n * @see {@link https://bulma.io/documentation/elements/notification/ | Bulma Notification documentation}\n */\nexport const Notification: React.FC<NotificationProps> = ({\n className,\n color,\n textColor,\n isLight,\n hasDelete,\n onDelete,\n children,\n ...props\n}) => {\n warnUnstyledColor('Notification', color);\n\n /**\n * Generates Bulma helper classes and separates out remaining props.\n */\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n ...props,\n });\n\n const bulmaClasses = usePrefixedClassNames('notification', {\n [`is-${color}`]: color && validColors.includes(color),\n 'is-light': isLight,\n 'has-delete': hasDelete,\n });\n\n const deleteClasses = usePrefixedClassNames('delete');\n\n const notificationClasses = classNames(\n bulmaClasses,\n bulmaHelperClasses,\n className\n );\n\n return (\n <div className={notificationClasses} {...rest}>\n {hasDelete && (\n <button\n type=\"button\"\n className={deleteClasses}\n onClick={onDelete}\n aria-label=\"Close notification\"\n />\n )}\n {children}\n </div>\n );\n};\n\n// Programmatic Notification API\n\n/** Screen positions where programmatic notifications can be displayed. */\nexport type NotificationPosition =\n 'top-left' | 'top' | 'top-right' | 'bottom-left' | 'bottom' | 'bottom-right';\n\n/**\n * Options for showing a programmatic notification.\n */\nexport interface NotificationOptions {\n /**\n * The message to display.\n *\n * For a notification other than `danger` and `warning`, NotificationContainer\n * announces the text the message renders, with an element's `aria-label`,\n * or an image's `alt`, standing in for what's inside it, a break between\n * block-level elements, and parts that are `aria-hidden`, `hidden` or\n * inline-styled `display: none` left out. Other ways of naming or hiding\n * content, such as `aria-labelledby`, CSS-generated content or a\n * stylesheet's `display: none`, aren't followed, so a message that relies on\n * them can be announced differently from what a screen reader would read.\n */\n message: string | React.ReactNode;\n /**\n * Bulma color modifier for the notification (renders `is-<color>`).\n *\n * Only `primary`, `link`, `info`, `success`, `warning`, `danger`, `black`,\n * `white`, `light`, and `dark` have shipped CSS for `.notification`. Every\n * other value the union accepts emits a class no CSS rule matches, so the\n * notification renders unstyled; those log a console warning in development\n * and will be removed from this union in the next major version.\n */\n color?: (typeof validColors)[number];\n /** Use the light color variant. */\n isLight?: boolean;\n /** Duration in ms before auto-close. Default 3000. */\n duration?: number;\n /**\n * Position on the screen. A notification shown without one goes to\n * `NotificationContainer`'s `position`.\n */\n position?: NotificationPosition;\n /**\n * When true, notifications enter a FIFO queue and display one at a time,\n * one queue across every `position`. Default false.\n */\n queue?: boolean;\n /** Show a delete (close) button. Default true. */\n hasDelete?: boolean;\n /** Stay open until dismissed. */\n indefinite?: boolean;\n /** Pause auto-close timer on hover. Default true. */\n pauseOnHover?: boolean;\n}\n\n/**\n * Internal representation of a notification instance.\n */\ninterface NotificationInstance {\n /** Unique identifier for this notification. */\n id: string;\n /** Configuration options for the notification. */\n options: NotificationOptions;\n}\n\nlet notificationId = 0;\nconst notificationListeners: Set<(items: NotificationInstance[]) => void> =\n new Set();\nlet notifications: NotificationInstance[] = [];\n\n// Queue support\nlet queuedNotifications: NotificationInstance[] = [];\nlet currentQueuedNotification: NotificationInstance | null = null;\n\n// The server has nowhere to portal a notification to, and its copy of this\n// module is shared by every request, so server rendering reads an empty list.\n// Hydration reads it too, which keeps the first client render matching the\n// server's.\nconst noNotifications: NotificationInstance[] = [];\nconst getServerNotifications = () => noNotifications;\n\n// What a container shows right now: the stacked notifications, then the queued\n// one on screen. NotificationContainer renders from this rather than from\n// updates alone, so notifications raised before it mounted still appear. It is\n// replaced rather than mutated, and only when listeners are notified, because\n// useSyncExternalStore needs the same array back between changes. An empty\n// list is `noNotifications` itself, so an empty store reads the same snapshot\n// on the client as on the server, and a notify that leaves nothing showing\n// when nothing was showing doesn't render the container again.\nlet visibleNotifications: NotificationInstance[] = noNotifications;\nconst getVisibleNotifications = () => visibleNotifications;\n\nconst notifyNotificationListeners = () => {\n const allVisible = [...notifications];\n if (currentQueuedNotification) {\n allVisible.push(currentQueuedNotification);\n }\n visibleNotifications = allVisible.length > 0 ? allVisible : noNotifications;\n notificationListeners.forEach(listener => listener([...allVisible]));\n};\n\nconst processQueuedNotification = () => {\n if (currentQueuedNotification || queuedNotifications.length === 0) return;\n currentQueuedNotification = queuedNotifications.shift()!;\n notifyNotificationListeners();\n};\n\n/**\n * Programmatic notification API for showing, closing, and managing notifications.\n *\n * @example\n * notification.success('File saved successfully');\n * notification.danger('Something went wrong', { duration: 5000 });\n */\nexport const notification = {\n /**\n * Show a notification with the given options.\n * @param {NotificationOptions} options - Notification configuration.\n * @returns {string} The unique ID of the created notification.\n */\n show: (options: NotificationOptions): string => {\n const id = `notification-${++notificationId}`;\n const instance = { id, options };\n\n if (options.queue) {\n queuedNotifications.push(instance);\n processQueuedNotification();\n } else {\n notifications.push(instance);\n notifyNotificationListeners();\n }\n\n return id;\n },\n\n /**\n * Show a success notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n success: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'success', ...options });\n },\n\n /**\n * Show a danger notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n danger: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'danger', ...options });\n },\n\n /**\n * Show a warning notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n warning: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'warning', ...options });\n },\n\n /**\n * Show an info notification.\n * @param {string|React.ReactNode} message - The message to display.\n * @param {Partial<NotificationOptions>} [options] - Additional options.\n * @returns {string} The notification ID.\n */\n info: (\n message: string | React.ReactNode,\n options?: Partial<NotificationOptions>\n ): string => {\n return notification.show({ message, color: 'info', ...options });\n },\n\n /**\n * Close a specific notification by ID.\n * @param {string} id - The notification ID to close.\n */\n close: (id: string): void => {\n if (currentQueuedNotification && currentQueuedNotification.id === id) {\n currentQueuedNotification = null;\n processQueuedNotification();\n } else {\n queuedNotifications = queuedNotifications.filter(n => n.id !== id);\n notifications = notifications.filter(n => n.id !== id);\n }\n notifyNotificationListeners();\n },\n\n /** Close all notifications and clear the queue. */\n closeAll: (): void => {\n notifications = [];\n queuedNotifications = [];\n currentQueuedNotification = null;\n notifyNotificationListeners();\n },\n\n /**\n * Subscribe to notification state changes.\n * @param {(items: NotificationInstance[]) => void} listener - Callback invoked on changes.\n * @returns {() => void} Unsubscribe function.\n */\n subscribe: (\n listener: (items: NotificationInstance[]) => void\n ): (() => void) => {\n notificationListeners.add(listener);\n return () => notificationListeners.delete(listener);\n },\n};\n\n/**\n * Whether a notification of this color announces itself as an alert, rather\n * than politely through NotificationContainer's status region.\n *\n * @function isUrgentColor\n * @param {NotificationOptions['color']} color - The notification's color.\n * @returns {boolean} True for `danger` and `warning`.\n */\nconst isUrgentColor = (color: NotificationOptions['color']): boolean =>\n color === 'danger' || color === 'warning';\n\n/**\n * Single auto-dismissing notification item used by NotificationContainer.\n *\n * @function\n * @param {{ instance: NotificationInstance; onClose: (id: string) => void; registerMessage: (id: string, node: HTMLElement | null) => void }} props - Component props.\n * @returns {JSX.Element} The rendered notification item.\n */\nconst NotificationItem: React.FC<{\n instance: NotificationInstance;\n onClose: (id: string) => void;\n registerMessage: (id: string, node: HTMLElement | null) => void;\n}> = ({ instance, onClose, registerMessage }) => {\n const {\n message,\n color,\n isLight,\n duration = 3000,\n hasDelete = true,\n indefinite = false,\n pauseOnHover = true,\n } = instance.options;\n\n const [isPaused, setIsPaused] = useState(false);\n const urgent = isUrgentColor(color);\n\n const handleClose = useCallback(() => {\n onClose(instance.id);\n }, [onClose, instance.id]);\n\n const messageRef = useCallback(\n (node: HTMLElement | null) => {\n registerMessage(instance.id, node);\n },\n [registerMessage, instance.id]\n );\n\n // Auto-close timer\n useEffect(() => {\n if (indefinite || duration === 0 || isPaused) return undefined;\n\n const timer = setTimeout(handleClose, duration);\n return () => clearTimeout(timer);\n }, [duration, indefinite, isPaused, handleClose]);\n\n const handleMouseEnter = useCallback(() => {\n if (pauseOnHover) setIsPaused(true);\n }, [pauseOnHover]);\n\n const handleMouseLeave = useCallback(() => {\n if (pauseOnHover) setIsPaused(false);\n }, [pauseOnHover]);\n\n return (\n <Notification\n color={color}\n isLight={isLight}\n hasDelete={hasDelete}\n onDelete={handleClose}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n style={{ pointerEvents: 'auto' }}\n >\n {/* Only the message is announced, not the close button Notification\n renders ahead of its children. Danger and warning announce\n themselves as an assertive alert, which a screen reader reliably\n reads out even when it arrives with its text. The rest are announced\n through the container's status region, which reads their text from\n this span, so it carries no role of its own that would announce them\n a second time. */}\n {urgent ? (\n <span role=\"alert\" aria-live=\"assertive\">\n {message}\n </span>\n ) : (\n <span ref={messageRef}>{message}</span>\n )}\n </Notification>\n );\n};\n\n// The order NotificationContainer renders its stacks in: across the top of the\n// screen, then across the bottom.\nconst notificationStackOrder: readonly NotificationPosition[] = [\n 'top-left',\n 'top',\n 'top-right',\n 'bottom-left',\n 'bottom',\n 'bottom-right',\n];\n\n/**\n * Inline style that fixes a stack of notifications to its place on the screen.\n *\n * @function\n * @param {NotificationPosition} position - Where the stack sits.\n * @returns {React.CSSProperties} The stack's style.\n */\nconst notificationStackStyle = (\n position: NotificationPosition\n): React.CSSProperties => {\n const isBottom = position.startsWith('bottom');\n const isCenter = position === 'top' || position === 'bottom';\n const isRight = position.endsWith('right');\n\n return {\n position: 'fixed',\n zIndex: 100,\n display: 'flex',\n flexDirection: isBottom ? 'column-reverse' : 'column',\n gap: '0.75rem',\n padding: '1rem',\n pointerEvents: 'none',\n maxWidth: '100%',\n ...(isBottom ? { bottom: 0 } : { top: 0 }),\n ...(isCenter\n ? { left: '50%', transform: 'translateX(-50%)', alignItems: 'center' }\n : isRight\n ? { right: 0, alignItems: 'flex-end' }\n : { left: 0, alignItems: 'flex-start' }),\n };\n};\n\n/**\n * Container component for rendering programmatic notifications.\n * Place once at your app root to enable the notification API. A notification\n * shown with a `position` appears there, and one shown without goes to the\n * container's `position`, so the container renders a stack for each position\n * in use.\n *\n * It keeps a visually hidden `role=\"status\"` live region in the page from the\n * moment it mounts, even while nothing is showing, and announces every\n * notification other than `danger` and `warning` through it, a moment after\n * the notification appears. Those notifications carry no `role=\"status\"` of\n * their own, so `getByRole('status')` finds the region, not the notification.\n * The text stays in the region briefly, even if the notification closes in\n * the meantime, and while both are up the page holds two copies of it. A\n * polite notification that closes before its announcement is written, a\n * moment after it appears, isn't announced at all. `danger` and `warning`\n * notifications announce themselves as assertive alerts. The region is hidden\n * with inline styles, so it needs no stylesheet.\n *\n * @function\n * @param {{ position?: NotificationPosition }} props - Container props.\n * @returns {JSX.Element | null} The rendered notification container, or null on the server and while hydrating.\n */\nexport const NotificationContainer: React.FC<{\n /**\n * Where a notification shown without a `position` of its own appears.\n * Default: 'top-right'. When it changes, those notifications move without\n * remounting, but one shown with its own `position` equal to the old or new\n * value remounts and starts over as if it had just been shown.\n */\n position?: NotificationPosition;\n}> = ({ position = 'top-right' }) => {\n // Starts from the notifications already showing instead of an empty list,\n // then follows changes.\n const items = useSyncExternalStore(\n notification.subscribe,\n getVisibleNotifications,\n getServerNotifications\n );\n // A portal needs a document, and it has no server-rendered counterpart, so\n // the server render and the hydrating one render nothing, and the container\n // portals in from the render after hydration.\n const isHydrated = useIsHydrated();\n const resolveContainer = usePortalContainer();\n\n // The notifications the status region announces. Danger and warning\n // announce themselves.\n const politeItems = useMemo(\n () => items.filter(item => !isUrgentColor(item.options.color)),\n [items]\n );\n\n // Each polite notification's message element, so the text the region\n // announces is the text on screen, whatever the message renders.\n const messageNodesRef = useRef(new Map<string, HTMLElement>());\n const registerMessage = useCallback(\n (id: string, node: HTMLElement | null) => {\n if (node) {\n messageNodesRef.current.set(id, node);\n } else {\n messageNodesRef.current.delete(id);\n }\n },\n []\n );\n // StatusRegion only describes polite notifications that are on screen, and\n // each of those registered its message element when it mounted. The\n // fallback is for a notification announced without being rendered, should\n // the container ever render fewer than it announces: a plain-text message\n // still says itself, and anything else is skipped.\n const describe = useCallback((item: NotificationInstance) => {\n const node = messageNodesRef.current.get(item.id);\n /* istanbul ignore if: every polite notification on screen registers its message element when it mounts */\n if (!node) {\n const { message } = item.options;\n return typeof message === 'string' ? message : null;\n }\n return spokenText(node);\n }, []);\n\n if (typeof document === 'undefined' || !isHydrated) {\n return null;\n }\n\n const stacks = groupIntoPositionStacks(\n items,\n item => item.options.position,\n position,\n notificationStackOrder\n );\n\n const target = resolveContainer();\n return createPortal(\n <LocalePortalScope target={target}>\n {stacks.map(stack => (\n <div key={stack.key} style={notificationStackStyle(stack.position)}>\n {stack.items.map(item => (\n <NotificationItem\n key={item.id}\n instance={item}\n onClose={notification.close}\n registerMessage={registerMessage}\n />\n ))}\n </div>\n ))}\n <StatusRegion items={politeItems} describe={describe} />\n </LocalePortalScope>,\n target\n );\n};\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import { forwardRef, useEffect } from 'react';
|
|
4
4
|
import { usePrefixedClassNames, classNames, prefixedClassNames } from '../helpers/classNames.js';
|
|
5
5
|
import { useClassPrefix } from '../helpers/Config.js';
|
|
6
|
+
import { useNumberFormat } from '../helpers/localeFormat.js';
|
|
6
7
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
7
8
|
import { warnOnce } from '../helpers/devWarnings.js';
|
|
8
9
|
|
|
@@ -85,13 +86,17 @@ const RingProgress = forwardRef(({ className, textColor, bgColor, value, max = 1
|
|
|
85
86
|
const labelClass = usePrefixedClassNames('ring-progress-label');
|
|
86
87
|
// A section's color class, prefixed like the rest.
|
|
87
88
|
const classPrefix = useClassPrefix();
|
|
89
|
+
const formatNumber = useNumberFormat();
|
|
90
|
+
const writeShare = (share) => formatNumber
|
|
91
|
+
? formatNumber(share / 100, { style: 'percent' })
|
|
92
|
+
: `${share}%`;
|
|
88
93
|
const roleProps = sections
|
|
89
94
|
? {
|
|
90
95
|
role: 'img',
|
|
91
96
|
'aria-label': [
|
|
92
97
|
label || rest['aria-label'],
|
|
93
98
|
sections
|
|
94
|
-
.map((section, i) => `${section.label} ${Math.round(arcs[i].length)}
|
|
99
|
+
.map((section, i) => `${section.label} ${writeShare(Math.round(arcs[i].length))}`)
|
|
95
100
|
.join(', '),
|
|
96
101
|
]
|
|
97
102
|
.filter(Boolean)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RingProgress.js","sources":["../../../src/elements/RingProgress.tsx"],"sourcesContent":["import React, { forwardRef, useEffect } from 'react';\nimport {\n classNames,\n prefixedClassNames,\n usePrefixedClassNames,\n} from '../helpers/classNames';\nimport { useClassPrefix } from '../helpers/Config';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { warnOnce } from '../helpers/devWarnings';\n\nconst validRingProgressColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n 'black',\n 'dark',\n 'light',\n 'white',\n] as const;\n/** The Bulma colors an arc takes, the ones `Progress` takes. */\nexport type RingProgressColor = (typeof validRingProgressColors)[number];\n\nconst validRingProgressSizes = ['small', 'medium', 'large'] as const;\n\n/** One colored part of a ring drawn in sections. */\nexport interface RingProgressSection {\n /** Its size, in the same units as the ring's `max`. */\n value: number;\n /** Its color. Without one, it takes the ring's. */\n color?: RingProgressColor;\n /** What it is, such as \"Photos\", read in the ring's name with its share. */\n label: string;\n}\n\n/**\n * Props for the RingProgress component. The helper props, the other\n * attributes and the `ref` land on its root `<div>`.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the root `<div>`.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading that names the ring, in place of `label`. With `sections`, use `label`, since a name from here replaces the one that lists the values.\n * @extraProp {string} [aria-valuetext] - What a screen reader says for the value, such as \"7 of 10 tasks\". Without it, the value is read as a percentage.\n */\nexport interface RingProgressProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'role'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * How far along it is, from 0 to `max`. Values outside that range are\n * drawn, and read, at the nearest end. Ignored with `sections`.\n */\n value?: number;\n /** The value that fills the ring. */\n max?: number;\n /**\n * Several values around one ring, in order clockwise from the top, each\n * with its own color and label. The ring is then an image whose name\n * writes them out, such as \"Storage: Documents 40%, Photos 15%\", since a\n * screen reader can't take several values from one element. A total over\n * `max` fills the ring and stops.\n */\n sections?: RingProgressSection[];\n /**\n * What it shows, such as \"Upload\" or \"Storage\". It names the ring, so it's\n * read with the value. A development build warns when the ring has no\n * name.\n */\n label?: string;\n /**\n * A level within a range, such as storage used or a score, rather than a\n * task moving toward done: `role=\"meter\"` in place of `role=\"progressbar\"`.\n * Ignored with `sections`.\n */\n isMeter?: boolean;\n /** The arc's color, and the color of sections without one. */\n color?: RingProgressColor;\n /** The ring's size. */\n size?: (typeof validRingProgressSizes)[number];\n /** Rounds the ends of the arc. */\n isRounded?: boolean;\n /**\n * What sits in the middle of the ring, such as the value as text. It's\n * hidden from assistive tech, which reads the value from the ring itself,\n * so the value isn't heard twice. Say anything more, such as \"7 of 10\n * tasks\", in `aria-valuetext`.\n */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** An arc's place on the ring, in percent of the way round. */\ninterface Arc {\n start: number;\n length: number;\n color?: RingProgressColor;\n}\n\nconst clamp = (value: number, low: number, high: number) =>\n Math.min(Math.max(value, low), high);\n\n/**\n * The `RingProgress` component draws a value as a ring that fills as it grows, with anything you pass shown in the middle.\n *\n * It's a `progressbar` for a task moving toward done, or a `meter` with\n * `isMeter` for a level within a range, named by `label`. With `sections`\n * it draws several values around one ring and is an image whose name lists\n * them. It has no state for work with no value: that's `Loader`.\n *\n * @function\n * @param {RingProgressProps} props - Props for the RingProgress component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the root `<div>`.\n * @returns {JSX.Element} The rendered ring.\n *\n * @example\n * <RingProgress value={72} label=\"Upload\" color=\"success\">\n * 72%\n * </RingProgress>\n */\nexport const RingProgress = forwardRef<HTMLDivElement, RingProgressProps>(\n (\n {\n className,\n textColor,\n bgColor,\n value,\n max = 100,\n sections,\n label,\n isMeter,\n color,\n size,\n isRounded,\n children,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n\n const named = Boolean(\n label || rest['aria-label'] || rest['aria-labelledby']\n );\n const mixed = sections !== undefined && value !== undefined;\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'RingProgress:unnamed',\n '[bestax-bulma] <RingProgress> has no accessible name. Pass ' +\n 'label, so a screen reader can say what the ring shows.'\n );\n }\n if (mixed) {\n warnOnce(\n 'RingProgress:value-and-sections',\n '[bestax-bulma] <RingProgress> was given both value and sections. ' +\n 'It draws the sections and ignores value.'\n );\n }\n }, [named, mixed]);\n\n const total = max > 0 ? max : 100;\n const share = (part: number) => (clamp(part, 0, total) / total) * 100;\n\n // Each arc starts where the one before ended, and the last stops at the\n // top, so a total over max fills the ring and no more.\n const arcs: Arc[] = [];\n if (sections) {\n let start = 0;\n for (const section of sections) {\n const length = Math.min(share(section.value), 100 - start);\n arcs.push({ start, length, color: section.color });\n start += length;\n }\n } else {\n arcs.push({ start: 0, length: share(value ?? 0) });\n }\n\n const rootClass = usePrefixedClassNames('ring-progress', {\n [`is-${size}`]:\n !!size && (validRingProgressSizes as readonly string[]).includes(size),\n [`is-${color}`]:\n !!color &&\n (validRingProgressColors as readonly string[]).includes(color),\n 'is-rounded': !!isRounded,\n 'has-sections': !!sections,\n });\n const ringClass = usePrefixedClassNames('ring-progress-ring');\n const trackClass = usePrefixedClassNames('ring-progress-track');\n const arcClass = usePrefixedClassNames('ring-progress-arc');\n const labelClass = usePrefixedClassNames('ring-progress-label');\n // A section's color class, prefixed like the rest.\n const classPrefix = useClassPrefix();\n\n const roleProps = sections\n ? {\n role: 'img',\n 'aria-label': [\n label || rest['aria-label'],\n sections\n .map(\n (section, i) =>\n `${section.label} ${Math.round(arcs[i].length)}%`\n )\n .join(', '),\n ]\n .filter(Boolean)\n .join(': '),\n }\n : {\n role: isMeter ? 'meter' : 'progressbar',\n 'aria-valuenow': clamp(value ?? 0, 0, total),\n 'aria-valuemin': 0,\n 'aria-valuemax': total,\n 'aria-label': label || rest['aria-label'],\n };\n\n return (\n <div\n ref={ref}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n {...rest}\n {...roleProps}\n >\n <svg className={ringClass} viewBox=\"0 0 100 100\" aria-hidden=\"true\">\n <circle className={trackClass} cx=\"50\" cy=\"50\" r=\"42\" />\n {arcs.map(\n (arc, i) =>\n arc.length > 0 && (\n <circle\n key={i}\n className={classNames(\n arcClass,\n arc.color &&\n (validRingProgressColors as readonly string[]).includes(\n arc.color\n ) &&\n prefixedClassNames(classPrefix, `is-${arc.color}`)\n )}\n cx=\"50\"\n cy=\"50\"\n r=\"42\"\n pathLength={100}\n style={\n {\n '--ring-progress-length': arc.length,\n '--ring-progress-start': arc.start,\n } as React.CSSProperties\n }\n />\n )\n )}\n </svg>\n {children !== undefined && children !== null && (\n // The ring's value is already read from its role, and not every\n // browser keeps a meter's content out of the tree, so the middle\n // is for the eye only.\n <div className={labelClass} aria-hidden=\"true\">\n {children}\n </div>\n )}\n </div>\n );\n }\n);\n\nRingProgress.displayName = 'RingProgress';\n\nexport default RingProgress;\n"],"names":["clamp","_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RingProgress.js","sources":["../../../src/elements/RingProgress.tsx"],"sourcesContent":["import React, { forwardRef, useEffect } from 'react';\nimport {\n classNames,\n prefixedClassNames,\n usePrefixedClassNames,\n} from '../helpers/classNames';\nimport { useClassPrefix } from '../helpers/Config';\nimport { useNumberFormat } from '../helpers/localeFormat';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { warnOnce } from '../helpers/devWarnings';\n\nconst validRingProgressColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n 'black',\n 'dark',\n 'light',\n 'white',\n] as const;\n/** The Bulma colors an arc takes, the ones `Progress` takes. */\nexport type RingProgressColor = (typeof validRingProgressColors)[number];\n\nconst validRingProgressSizes = ['small', 'medium', 'large'] as const;\n\n/** One colored part of a ring drawn in sections. */\nexport interface RingProgressSection {\n /** Its size, in the same units as the ring's `max`. */\n value: number;\n /** Its color. Without one, it takes the ring's. */\n color?: RingProgressColor;\n /** What it is, such as \"Photos\", read in the ring's name with its share. */\n label: string;\n}\n\n/**\n * Props for the RingProgress component. The helper props, the other\n * attributes and the `ref` land on its root `<div>`.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the root `<div>`.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading that names the ring, in place of `label`. With `sections`, use `label`, since a name from here replaces the one that lists the values.\n * @extraProp {string} [aria-valuetext] - What a screen reader says for the value, such as \"7 of 10 tasks\". Without it, the value is read as a percentage.\n */\nexport interface RingProgressProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'role'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * How far along it is, from 0 to `max`. Values outside that range are\n * drawn, and read, at the nearest end. Ignored with `sections`.\n */\n value?: number;\n /** The value that fills the ring. */\n max?: number;\n /**\n * Several values around one ring, in order clockwise from the top, each\n * with its own color and label. The ring is then an image whose name\n * writes them out, such as \"Storage: Documents 40%, Photos 15%\", since a\n * screen reader can't take several values from one element. Inside a\n * `LocaleProvider` with a `locale`, the shares are written as that locale\n * writes a percentage. A total over `max` fills the ring and stops.\n */\n sections?: RingProgressSection[];\n /**\n * What it shows, such as \"Upload\" or \"Storage\". It names the ring, so it's\n * read with the value. A development build warns when the ring has no\n * name.\n */\n label?: string;\n /**\n * A level within a range, such as storage used or a score, rather than a\n * task moving toward done: `role=\"meter\"` in place of `role=\"progressbar\"`.\n * Ignored with `sections`.\n */\n isMeter?: boolean;\n /** The arc's color, and the color of sections without one. */\n color?: RingProgressColor;\n /** The ring's size. */\n size?: (typeof validRingProgressSizes)[number];\n /** Rounds the ends of the arc. */\n isRounded?: boolean;\n /**\n * What sits in the middle of the ring, such as the value as text. It's\n * hidden from assistive tech, which reads the value from the ring itself,\n * so the value isn't heard twice. Say anything more, such as \"7 of 10\n * tasks\", in `aria-valuetext`.\n */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** An arc's place on the ring, in percent of the way round. */\ninterface Arc {\n start: number;\n length: number;\n color?: RingProgressColor;\n}\n\nconst clamp = (value: number, low: number, high: number) =>\n Math.min(Math.max(value, low), high);\n\n/**\n * The `RingProgress` component draws a value as a ring that fills as it grows, with anything you pass shown in the middle.\n *\n * It's a `progressbar` for a task moving toward done, or a `meter` with\n * `isMeter` for a level within a range, named by `label`. With `sections`\n * it draws several values around one ring and is an image whose name lists\n * them. It has no state for work with no value: that's `Loader`.\n *\n * @function\n * @param {RingProgressProps} props - Props for the RingProgress component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the root `<div>`.\n * @returns {JSX.Element} The rendered ring.\n *\n * @example\n * <RingProgress value={72} label=\"Upload\" color=\"success\">\n * 72%\n * </RingProgress>\n */\nexport const RingProgress = forwardRef<HTMLDivElement, RingProgressProps>(\n (\n {\n className,\n textColor,\n bgColor,\n value,\n max = 100,\n sections,\n label,\n isMeter,\n color,\n size,\n isRounded,\n children,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n\n const named = Boolean(\n label || rest['aria-label'] || rest['aria-labelledby']\n );\n const mixed = sections !== undefined && value !== undefined;\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'RingProgress:unnamed',\n '[bestax-bulma] <RingProgress> has no accessible name. Pass ' +\n 'label, so a screen reader can say what the ring shows.'\n );\n }\n if (mixed) {\n warnOnce(\n 'RingProgress:value-and-sections',\n '[bestax-bulma] <RingProgress> was given both value and sections. ' +\n 'It draws the sections and ignores value.'\n );\n }\n }, [named, mixed]);\n\n const total = max > 0 ? max : 100;\n const share = (part: number) => (clamp(part, 0, total) / total) * 100;\n\n // Each arc starts where the one before ended, and the last stops at the\n // top, so a total over max fills the ring and no more.\n const arcs: Arc[] = [];\n if (sections) {\n let start = 0;\n for (const section of sections) {\n const length = Math.min(share(section.value), 100 - start);\n arcs.push({ start, length, color: section.color });\n start += length;\n }\n } else {\n arcs.push({ start: 0, length: share(value ?? 0) });\n }\n\n const rootClass = usePrefixedClassNames('ring-progress', {\n [`is-${size}`]:\n !!size && (validRingProgressSizes as readonly string[]).includes(size),\n [`is-${color}`]:\n !!color &&\n (validRingProgressColors as readonly string[]).includes(color),\n 'is-rounded': !!isRounded,\n 'has-sections': !!sections,\n });\n const ringClass = usePrefixedClassNames('ring-progress-ring');\n const trackClass = usePrefixedClassNames('ring-progress-track');\n const arcClass = usePrefixedClassNames('ring-progress-arc');\n const labelClass = usePrefixedClassNames('ring-progress-label');\n // A section's color class, prefixed like the rest.\n const classPrefix = useClassPrefix();\n const formatNumber = useNumberFormat();\n const writeShare = (share: number) =>\n formatNumber\n ? formatNumber(share / 100, { style: 'percent' })\n : `${share}%`;\n\n const roleProps = sections\n ? {\n role: 'img',\n 'aria-label': [\n label || rest['aria-label'],\n sections\n .map(\n (section, i) =>\n `${section.label} ${writeShare(Math.round(arcs[i].length))}`\n )\n .join(', '),\n ]\n .filter(Boolean)\n .join(': '),\n }\n : {\n role: isMeter ? 'meter' : 'progressbar',\n 'aria-valuenow': clamp(value ?? 0, 0, total),\n 'aria-valuemin': 0,\n 'aria-valuemax': total,\n 'aria-label': label || rest['aria-label'],\n };\n\n return (\n <div\n ref={ref}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n {...rest}\n {...roleProps}\n >\n <svg className={ringClass} viewBox=\"0 0 100 100\" aria-hidden=\"true\">\n <circle className={trackClass} cx=\"50\" cy=\"50\" r=\"42\" />\n {arcs.map(\n (arc, i) =>\n arc.length > 0 && (\n <circle\n key={i}\n className={classNames(\n arcClass,\n arc.color &&\n (validRingProgressColors as readonly string[]).includes(\n arc.color\n ) &&\n prefixedClassNames(classPrefix, `is-${arc.color}`)\n )}\n cx=\"50\"\n cy=\"50\"\n r=\"42\"\n pathLength={100}\n style={\n {\n '--ring-progress-length': arc.length,\n '--ring-progress-start': arc.start,\n } as React.CSSProperties\n }\n />\n )\n )}\n </svg>\n {children !== undefined && children !== null && (\n // The ring's value is already read from its role, and not every\n // browser keeps a meter's content out of the tree, so the middle\n // is for the eye only.\n <div className={labelClass} aria-hidden=\"true\">\n {children}\n </div>\n )}\n </div>\n );\n }\n);\n\nRingProgress.displayName = 'RingProgress';\n\nexport default RingProgress;\n"],"names":["clamp","_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -4,12 +4,13 @@ import { forwardRef, useRef, useState, useId, useCallback, useEffect } from 'rea
|
|
|
4
4
|
import { usePrefixedClassNames, classNames, prefixedClassNames } from '../helpers/classNames.js';
|
|
5
5
|
import { useConfig } from '../helpers/Config.js';
|
|
6
6
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
7
|
-
import { useInsideField } from './FormContext.js';
|
|
7
|
+
import { useFieldsetDisabled, useInsideField } from './FormContext.js';
|
|
8
8
|
import { Field } from './Field.js';
|
|
9
9
|
import { useAutoLabelId } from './useAutoLabelId.js';
|
|
10
10
|
import { useControlLoading } from './controlLoading.js';
|
|
11
11
|
import { spokenText, StatusRegion } from '../helpers/statusRegion.js';
|
|
12
12
|
import { useLatestRef } from '../helpers/useLatestRef.js';
|
|
13
|
+
import { useLocaleMatcher } from '../helpers/localeFormat.js';
|
|
13
14
|
|
|
14
15
|
// What the status region announces: the `empty` content, as the one item
|
|
15
16
|
// it's handed while that content is on screen because of the user's typing.
|
|
@@ -51,7 +52,7 @@ const isEnabled = (item) => typeof item === 'string' || !item.disabled;
|
|
|
51
52
|
* onSelect={(item) => console.log(item)}
|
|
52
53
|
* />
|
|
53
54
|
*/
|
|
54
|
-
const Autocomplete = forwardRef(({ data = [], value: controlledValue, placeholder, field = 'label', clearable = false, openOnFocus = false, keepFirst = false, keepOpen = false, selectOnClickOutside = false, maxHeight = 200, loading = false, disabled
|
|
55
|
+
const Autocomplete = forwardRef(({ data = [], value: controlledValue, placeholder, field = 'label', clearable = false, openOnFocus = false, keepFirst = false, keepOpen = false, selectOnClickOutside = false, maxHeight = 200, loading = false, disabled: disabledProp, checkInfiniteScroll = false, infiniteScrollDistance = 50, color, size, onInput, onSelect, onActiveChange, onInfiniteScroll, itemTemplate, header, footer, empty, name, form, required, id,
|
|
55
56
|
// Deprecated no-ops, taken here so they stay off the wrapper.
|
|
56
57
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- deprecated no-op
|
|
57
58
|
selected,
|
|
@@ -59,6 +60,7 @@ selected,
|
|
|
59
60
|
dropdown,
|
|
60
61
|
// Field props
|
|
61
62
|
label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, className, ...props }, ref) => {
|
|
63
|
+
const disabled = useFieldsetDisabled(disabledProp);
|
|
62
64
|
const insideField = useInsideField();
|
|
63
65
|
// The suggestion list points `aria-labelledby` at the label that names
|
|
64
66
|
// the input, so it needs that label's id.
|
|
@@ -70,6 +72,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
70
72
|
needsLabelId: true,
|
|
71
73
|
});
|
|
72
74
|
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
75
|
+
const matchText = useLocaleMatcher();
|
|
73
76
|
const { classPrefix } = useConfig();
|
|
74
77
|
const containerRef = useRef(null);
|
|
75
78
|
const inputRef = useRef(null);
|
|
@@ -99,6 +102,8 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
99
102
|
// Filter data based on input
|
|
100
103
|
const filteredData = data.filter(item => {
|
|
101
104
|
const displayValue = getDisplayValue(item);
|
|
105
|
+
if (matchText)
|
|
106
|
+
return matchText(displayValue, inputValue);
|
|
102
107
|
return displayValue.toLowerCase().includes(inputValue.toLowerCase());
|
|
103
108
|
});
|
|
104
109
|
// The dropdown shows the options or, when nothing matches, the `empty`
|