@allxsmith/bestax-bulma 5.34.3 → 5.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/Loading.js +19 -6
- package/dist/esm/components/Loading.js.map +1 -1
- package/dist/esm/components/Menu.js +5 -1
- package/dist/esm/components/Menu.js.map +1 -1
- package/dist/esm/components/Modal.js +22 -31
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/components/Sidebar.js +19 -6
- package/dist/esm/components/Sidebar.js.map +1 -1
- package/dist/esm/components/Steps.js +14 -13
- package/dist/esm/components/Steps.js.map +1 -1
- package/dist/esm/components/Toast.js +18 -7
- package/dist/esm/components/Toast.js.map +1 -1
- package/dist/esm/components/Tooltip.js +3 -3
- package/dist/esm/components/Tooltip.js.map +1 -1
- package/dist/esm/elements/Icon.js +2 -3
- package/dist/esm/elements/Icon.js.map +1 -1
- package/dist/esm/elements/Loader.js +2 -1
- package/dist/esm/elements/Loader.js.map +1 -1
- package/dist/esm/form/Checkbox.js +37 -4
- package/dist/esm/form/Checkbox.js.map +1 -1
- package/dist/esm/form/Slider.js +5 -4
- package/dist/esm/form/Slider.js.map +1 -1
- package/dist/esm/form/Taginput.js +21 -9
- package/dist/esm/form/Taginput.js.map +1 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js +9 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js +5 -4
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js.map +1 -1
- package/dist/esm/helpers/overlayStack.js +49 -0
- package/dist/esm/helpers/overlayStack.js.map +1 -0
- package/dist/esm/helpers/useLatestRef.js +33 -0
- package/dist/esm/helpers/useLatestRef.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +242 -95
- package/dist/index.cjs.map +1 -1
- package/dist/types/components/Loading.d.ts +13 -3
- package/dist/types/components/Menu.d.ts +14 -1
- package/dist/types/components/Modal.d.ts +7 -4
- package/dist/types/components/Sidebar.d.ts +8 -3
- package/dist/types/components/Steps.d.ts +27 -5
- package/dist/types/components/Toast.d.ts +5 -2
- package/dist/types/elements/Icon.d.ts +6 -1
- package/dist/types/elements/Loader.d.ts +2 -1
- package/dist/types/form/Checkbox.d.ts +11 -0
- package/dist/types/form/Taginput.d.ts +5 -1
- package/dist/types/helpers/overlayStack.d.ts +15 -0
- package/dist/types/helpers/useLatestRef.d.ts +21 -0
- package/dist/types-cjs/components/Loading.d.ts +13 -3
- package/dist/types-cjs/components/Menu.d.ts +14 -1
- package/dist/types-cjs/components/Modal.d.ts +7 -4
- package/dist/types-cjs/components/Sidebar.d.ts +8 -3
- package/dist/types-cjs/components/Steps.d.ts +27 -5
- package/dist/types-cjs/components/Toast.d.ts +5 -2
- package/dist/types-cjs/elements/Icon.d.ts +6 -1
- package/dist/types-cjs/elements/Loader.d.ts +2 -1
- package/dist/types-cjs/form/Checkbox.d.ts +11 -0
- package/dist/types-cjs/form/Taginput.d.ts +5 -1
- package/dist/types-cjs/helpers/overlayStack.d.ts +15 -0
- package/dist/types-cjs/helpers/useLatestRef.d.ts +21 -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/components/_steps.scss +96 -38
- package/src/scss/elements/_loader.scss +20 -0
- package/src/scss/form/_checkbox.scss +28 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Steps.js","sources":["../../../src/components/Steps.tsx"],"sourcesContent":["import React from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\n\n/** Available size modifiers for the Steps component. */\nexport type StepsSize = 'small' | 'medium' | 'large';\n/** Available color variants for the Steps component. */\nexport type StepsColor =\n 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n\n/**\n * Props for individual Step items.\n */\nexport interface StepItemProps {\n /** Step label/title */\n label?: React.ReactNode;\n /** Icon for the step marker */\n icon?: React.ReactNode;\n /**\n * Icon shown when the step is completed.\n * @defaultValue '✓'\n */\n completedIcon?: React.ReactNode;\n /** Whether this step is clickable */\n clickable?: boolean;\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for the Steps component.\n * @extraProp {React.ReactNode} [children] - Step children (alternative to items).\n * @extraProp {string} [className] - Additional CSS classes.\n */\nexport interface StepsProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor' | 'size'> {\n /** Current active step (0-indexed). */\n value?: number;\n /** Array of step items. */\n items?: StepItemProps[];\n /** Size of the steps. */\n size?: StepsSize;\n /** Color variant. */\n color?: StepsColor;\n /** Show step markers. Default: true. */\n hasMarker?: boolean;\n /** Enable animations. Default: true. */\n animated?: boolean;\n /** Use rounded markers. Default: true. */\n rounded?: boolean;\n /** Vertical layout. */\n vertical?: boolean;\n /** Position of labels. */\n labelPosition?: 'bottom' | 'right' | 'left';\n /** Mobile display mode. */\n mobileMode?: 'minimal' | 'compact' | 'right';\n /** Displays step numbers in the markers. */\n showStepNumbers?: boolean;\n /**\n * Shows previous/next navigation buttons. They render `type=\"button\"`, so a\n * multi-step form wrapped around the steps is not submitted by moving\n * between them.\n */\n hasNavigation?: boolean;\n /** Label for the previous button. */\n prevLabel?: string;\n /** Label for the next button. */\n nextLabel?: string;\n /** Callback when previous button is clicked. */\n onPrev?: () => void;\n /** Callback when next button is clicked. */\n onNext?: () => void;\n /** Callback when a step is clicked. */\n onStepClick?: (step: number) => void;\n}\n\n/**\n * Props for the Step subcomponent.\n */\nexport interface StepProps\n extends\n Omit<React.LiHTMLAttributes<HTMLLIElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Whether this step is active */\n isActive?: boolean;\n /** Whether this step is completed */\n isCompleted?: boolean;\n /** Step label/title */\n label?: React.ReactNode;\n /** Icon for the step marker */\n icon?: React.ReactNode;\n /** Whether this step is clickable */\n clickable?: boolean;\n /** Click handler */\n onClick?: () => void;\n /** Step number to display in marker (1-indexed) */\n stepNumber?: number;\n /**\n * Icon shown when the step is completed.\n * @defaultValue '✓'\n */\n completedIcon?: React.ReactNode;\n}\n\n/**\n * Individual Step component for use inside Steps.\n *\n * @function\n * @param {StepProps} props - Props for the Step component.\n * @returns {JSX.Element} The rendered step segment.\n */\nexport const Step: React.FC<StepProps> = ({\n isActive = false,\n isCompleted = false,\n label,\n icon,\n clickable = false,\n onClick,\n stepNumber,\n completedIcon,\n className,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n\n const stepClasses = classNames(\n usePrefixedClassNames('steps-segment', {\n 'is-active': isActive,\n 'is-completed': isCompleted,\n }),\n bulmaHelperClasses,\n className\n );\n const stepsLinkClass = usePrefixedClassNames('steps-link');\n const stepsMarkerClass = usePrefixedClassNames('steps-marker');\n const stepsContentClass = usePrefixedClassNames('steps-content');\n const stepsTitleClass = usePrefixedClassNames('steps-title');\n\n const handleClick = () => {\n if (clickable && onClick) {\n onClick();\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (clickable && onClick && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick();\n }\n };\n\n const resolvedCompletedIcon =\n completedIcon === undefined ? '\\u2713' : completedIcon;\n const markerContent =\n icon ||\n (isCompleted && resolvedCompletedIcon\n ? resolvedCompletedIcon\n : (stepNumber ?? null));\n\n return (\n <li\n className={stepClasses}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n tabIndex={clickable ? 0 : undefined}\n role={clickable ? 'button' : undefined}\n aria-current={isActive ? 'step' : undefined}\n {...rest}\n >\n <div className={stepsLinkClass}>\n <span className={stepsMarkerClass}>{markerContent}</span>\n {(label || children) && (\n <div className={stepsContentClass}>\n <p className={stepsTitleClass}>{label || children}</p>\n </div>\n )}\n </div>\n </li>\n );\n};\n\n/**\n * The `Steps` component provides a multi-step progress indicator for wizard flows, checkout processes, or any multi-step workflow.\n *\n * @function\n * @param {StepsProps} props - Props for the Steps component.\n * @returns {JSX.Element} The rendered steps component.\n *\n * @example\n * // Basic steps with items\n * <Steps\n * value={1}\n * items={[\n * { label: 'Account' },\n * { label: 'Profile' },\n * { label: 'Complete' },\n * ]}\n * />\n *\n * @example\n * // Clickable steps with navigation\n * <Steps\n * value={currentStep}\n * onStepClick={(step) => setCurrentStep(step)}\n * hasNavigation\n * items={[\n * { label: 'Step 1', clickable: true },\n * { label: 'Step 2', clickable: true },\n * { label: 'Step 3', clickable: true },\n * ]}\n * />\n */\nconst StepsComponent: React.FC<StepsProps> = ({\n value = 0,\n items,\n size,\n color,\n hasMarker = true,\n animated = true,\n rounded = true,\n vertical = false,\n labelPosition = 'bottom',\n mobileMode,\n showStepNumbers = true,\n hasNavigation = false,\n prevLabel,\n nextLabel,\n onPrev,\n onNext,\n onStepClick,\n className,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n\n // Generate classes for the wrapper div\n const stepsClasses = usePrefixedClassNames('steps', {\n [`is-${size}`]: size,\n [`is-${color}`]: color,\n 'has-marker': hasMarker,\n 'is-animated': animated,\n 'is-rounded': rounded,\n 'is-vertical': vertical,\n [`has-label-${labelPosition}`]:\n labelPosition && (labelPosition !== 'bottom' || vertical),\n [`is-${mobileMode}`]: mobileMode,\n });\n\n const combinedClasses = classNames(\n stepsClasses,\n bulmaHelperClasses,\n className\n );\n\n // Generate classes for the ul (list)\n const listClasses = usePrefixedClassNames('steps-list', {\n 'is-vertical': vertical,\n });\n const stepsNavigationClass = usePrefixedClassNames('steps-navigation');\n const prevButtonClass = usePrefixedClassNames('button');\n const nextButtonClass = usePrefixedClassNames('button', {\n 'is-primary': true,\n });\n\n // Count total steps for navigation\n const totalSteps = items ? items.length : React.Children.count(children);\n\n // Render items if provided\n const renderSteps = () => {\n if (items && items.length > 0) {\n return items.map((item, index) => (\n <Step\n key={index}\n isActive={index === value}\n isCompleted={index < value}\n label={item.label}\n icon={item.icon}\n completedIcon={item.completedIcon}\n clickable={item.clickable}\n stepNumber={showStepNumbers ? index + 1 : undefined}\n onClick={\n item.clickable && onStepClick ? () => onStepClick(index) : undefined\n }\n className={item.className}\n />\n ));\n }\n\n // If children are provided, clone them with active/completed states\n if (children) {\n return React.Children.map(children, (child, index) => {\n if (React.isValidElement<StepProps>(child)) {\n return React.cloneElement(child, {\n isActive: index === value,\n isCompleted: index < value,\n stepNumber: showStepNumbers ? index + 1 : undefined,\n onClick:\n child.props.clickable && onStepClick\n ? () => onStepClick(index)\n : child.props.onClick,\n });\n }\n return child;\n });\n }\n\n return null;\n };\n\n return (\n <div className={combinedClasses} {...rest}>\n <ul className={listClasses}>{renderSteps()}</ul>\n {hasNavigation && (\n <div className={stepsNavigationClass}>\n <button\n type=\"button\"\n className={prevButtonClass}\n disabled={value === 0}\n onClick={onPrev ?? (() => onStepClick?.(value - 1))}\n >\n {prevLabel ?? 'Previous'}\n </button>\n <button\n type=\"button\"\n className={nextButtonClass}\n disabled={value === totalSteps - 1}\n onClick={onNext ?? (() => onStepClick?.(value + 1))}\n >\n {nextLabel ?? 'Next'}\n </button>\n </div>\n )}\n </div>\n );\n};\n\n// Attach Step as static property\nexport const Steps = withSubComponents(StepsComponent, { Step }, 'Steps');\n\nexport default Steps;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Steps.js","sources":["../../../src/components/Steps.tsx"],"sourcesContent":["import React from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\n\n/** Available size modifiers for the Steps component. */\nexport type StepsSize = 'small' | 'medium' | 'large';\n/** Available color variants for the Steps component. */\nexport type StepsColor =\n 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n\n/**\n * Props for individual Step items.\n */\nexport interface StepItemProps {\n /**\n * Step label/title. On a `clickable` step it's rendered inside the step's\n * `<button>`, so it has to be phrasing content, like text, a `<span>` or an\n * icon, with no link, button or other control in it.\n */\n label?: React.ReactNode;\n /** Icon for the step marker */\n icon?: React.ReactNode;\n /**\n * Icon shown when the step is completed.\n * @defaultValue '✓'\n */\n completedIcon?: React.ReactNode;\n /**\n * Whether this step is clickable. A clickable step puts its marker and\n * label in a `<button type=\"button\">` inside its list item, which calls\n * `onStepClick`, and the current step's `aria-current` goes on that button.\n */\n clickable?: boolean;\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for the Steps component.\n * @extraProp {React.ReactNode} [children] - Step children (alternative to items).\n * @extraProp {string} [className] - Additional CSS classes.\n */\nexport interface StepsProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor' | 'size'> {\n /** Current active step (0-indexed). */\n value?: number;\n /** Array of step items. */\n items?: StepItemProps[];\n /** Size of the steps. */\n size?: StepsSize;\n /** Color variant. */\n color?: StepsColor;\n /** Show step markers. Default: true. */\n hasMarker?: boolean;\n /** Enable animations. Default: true. */\n animated?: boolean;\n /** Use rounded markers. Default: true. */\n rounded?: boolean;\n /** Vertical layout. */\n vertical?: boolean;\n /**\n * Position of labels. `right` and `left` mean the end and the start of the\n * line, so in a right-to-left page `right` puts each label to the left of\n * its marker, and `left` to the right.\n */\n labelPosition?: 'bottom' | 'right' | 'left';\n /** Mobile display mode. */\n mobileMode?: 'minimal' | 'compact' | 'right';\n /** Displays step numbers in the markers. */\n showStepNumbers?: boolean;\n /**\n * Shows previous/next navigation buttons. They render `type=\"button\"`, so a\n * multi-step form wrapped around the steps is not submitted by moving\n * between them.\n */\n hasNavigation?: boolean;\n /** Label for the previous button. */\n prevLabel?: string;\n /** Label for the next button. */\n nextLabel?: string;\n /** Callback when previous button is clicked. */\n onPrev?: () => void;\n /** Callback when next button is clicked. */\n onNext?: () => void;\n /** Callback when a step is clicked. */\n onStepClick?: (step: number) => void;\n}\n\n/**\n * Props for the Step subcomponent.\n */\nexport interface StepProps\n extends\n Omit<React.LiHTMLAttributes<HTMLLIElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Whether this step is active */\n isActive?: boolean;\n /** Whether this step is completed */\n isCompleted?: boolean;\n /**\n * Step label/title, or pass it as `children`. On a `clickable` step it's\n * rendered inside the step's `<button>`, so it has to be phrasing content,\n * like text, a `<span>` or an icon, with no link, button or other control\n * in it.\n */\n label?: React.ReactNode;\n /** Icon for the step marker */\n icon?: React.ReactNode;\n /**\n * Whether this step is clickable. A clickable step puts its marker and\n * label in a `<button type=\"button\">` inside the `<li>`, which calls\n * `onClick`, and `aria-current` goes on that button. The `<li>` keeps the\n * rest of the props.\n */\n clickable?: boolean;\n /** Click handler */\n onClick?: () => void;\n /** Step number to display in marker (1-indexed) */\n stepNumber?: number;\n /**\n * Icon shown when the step is completed.\n * @defaultValue '✓'\n */\n completedIcon?: React.ReactNode;\n}\n\n/**\n * Individual Step component for use inside Steps.\n *\n * @function\n * @param {StepProps} props - Props for the Step component.\n * @returns {JSX.Element} The rendered step segment.\n */\nexport const Step: React.FC<StepProps> = ({\n isActive = false,\n isCompleted = false,\n label,\n icon,\n clickable = false,\n onClick,\n stepNumber,\n completedIcon,\n className,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n\n const stepClasses = classNames(\n usePrefixedClassNames('steps-segment', {\n 'is-active': isActive,\n 'is-completed': isCompleted,\n }),\n bulmaHelperClasses,\n className\n );\n const stepsLinkClass = usePrefixedClassNames('steps-link');\n const stepsMarkerClass = usePrefixedClassNames('steps-marker');\n const stepsContentClass = usePrefixedClassNames('steps-content');\n const stepsTitleClass = usePrefixedClassNames('steps-title');\n\n const resolvedCompletedIcon =\n completedIcon === undefined ? '\\u2713' : completedIcon;\n const markerContent =\n icon ||\n (isCompleted && resolvedCompletedIcon\n ? resolvedCompletedIcon\n : (stepNumber ?? null));\n const title = label || children;\n const current = isActive ? 'step' : undefined;\n\n // The step stays a list item, and a real button inside it does the\n // clicking, so the list keeps its items and the button brings Enter, Space\n // and focus with it. A button holds only phrasing content, so its parts\n // are spans.\n if (clickable) {\n return (\n <li className={stepClasses} {...rest}>\n <button\n type=\"button\"\n className={stepsLinkClass}\n onClick={() => onClick?.()}\n aria-current={current}\n >\n <span className={stepsMarkerClass}>{markerContent}</span>\n {title && (\n <span className={stepsContentClass}>\n <span className={stepsTitleClass}>{title}</span>\n </span>\n )}\n </button>\n </li>\n );\n }\n\n return (\n <li className={stepClasses} aria-current={current} {...rest}>\n <div className={stepsLinkClass}>\n <span className={stepsMarkerClass}>{markerContent}</span>\n {title && (\n <div className={stepsContentClass}>\n <p className={stepsTitleClass}>{title}</p>\n </div>\n )}\n </div>\n </li>\n );\n};\n\n/**\n * The `Steps` component provides a multi-step progress indicator for wizard flows, checkout processes, or any multi-step workflow.\n *\n * @function\n * @param {StepsProps} props - Props for the Steps component.\n * @returns {JSX.Element} The rendered steps component.\n *\n * @example\n * // Basic steps with items\n * <Steps\n * value={1}\n * items={[\n * { label: 'Account' },\n * { label: 'Profile' },\n * { label: 'Complete' },\n * ]}\n * />\n *\n * @example\n * // Clickable steps with navigation\n * <Steps\n * value={currentStep}\n * onStepClick={(step) => setCurrentStep(step)}\n * hasNavigation\n * items={[\n * { label: 'Step 1', clickable: true },\n * { label: 'Step 2', clickable: true },\n * { label: 'Step 3', clickable: true },\n * ]}\n * />\n */\nconst StepsComponent: React.FC<StepsProps> = ({\n value = 0,\n items,\n size,\n color,\n hasMarker = true,\n animated = true,\n rounded = true,\n vertical = false,\n labelPosition = 'bottom',\n mobileMode,\n showStepNumbers = true,\n hasNavigation = false,\n prevLabel,\n nextLabel,\n onPrev,\n onNext,\n onStepClick,\n className,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n\n // Generate classes for the wrapper div\n const stepsClasses = usePrefixedClassNames('steps', {\n [`is-${size}`]: size,\n [`is-${color}`]: color,\n 'has-marker': hasMarker,\n 'is-animated': animated,\n 'is-rounded': rounded,\n 'is-vertical': vertical,\n [`has-label-${labelPosition}`]:\n labelPosition && (labelPosition !== 'bottom' || vertical),\n [`is-${mobileMode}`]: mobileMode,\n });\n\n const combinedClasses = classNames(\n stepsClasses,\n bulmaHelperClasses,\n className\n );\n\n // Generate classes for the ul (list)\n const listClasses = usePrefixedClassNames('steps-list', {\n 'is-vertical': vertical,\n });\n const stepsNavigationClass = usePrefixedClassNames('steps-navigation');\n const prevButtonClass = usePrefixedClassNames('button');\n const nextButtonClass = usePrefixedClassNames('button', {\n 'is-primary': true,\n });\n\n // Count total steps for navigation\n const totalSteps = items ? items.length : React.Children.count(children);\n\n // Render items if provided\n const renderSteps = () => {\n if (items && items.length > 0) {\n return items.map((item, index) => (\n <Step\n key={index}\n isActive={index === value}\n isCompleted={index < value}\n label={item.label}\n icon={item.icon}\n completedIcon={item.completedIcon}\n clickable={item.clickable}\n stepNumber={showStepNumbers ? index + 1 : undefined}\n onClick={\n item.clickable && onStepClick ? () => onStepClick(index) : undefined\n }\n className={item.className}\n />\n ));\n }\n\n // If children are provided, clone them with active/completed states\n if (children) {\n return React.Children.map(children, (child, index) => {\n if (React.isValidElement<StepProps>(child)) {\n return React.cloneElement(child, {\n isActive: index === value,\n isCompleted: index < value,\n stepNumber: showStepNumbers ? index + 1 : undefined,\n onClick:\n child.props.clickable && onStepClick\n ? () => onStepClick(index)\n : child.props.onClick,\n });\n }\n return child;\n });\n }\n\n return null;\n };\n\n // role=\"list\" restates the <ul>'s own role, for Safari: WebKit stops\n // exposing a list as one once its markers are hidden, as they are here,\n // unless the role is set explicitly.\n return (\n <div className={combinedClasses} {...rest}>\n <ul className={listClasses} role=\"list\">\n {renderSteps()}\n </ul>\n {hasNavigation && (\n <div className={stepsNavigationClass}>\n <button\n type=\"button\"\n className={prevButtonClass}\n disabled={value === 0}\n onClick={onPrev ?? (() => onStepClick?.(value - 1))}\n >\n {prevLabel ?? 'Previous'}\n </button>\n <button\n type=\"button\"\n className={nextButtonClass}\n disabled={value === totalSteps - 1}\n onClick={onNext ?? (() => onStepClick?.(value + 1))}\n >\n {nextLabel ?? 'Next'}\n </button>\n </div>\n )}\n </div>\n );\n};\n\n// Attach Step as static property\nexport const Steps = withSubComponents(StepsComponent, { Step }, 'Steps');\n\nexport default Steps;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
|
|
@@ -7,6 +7,8 @@ import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
|
7
7
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
8
8
|
import { usePortalContainer } from '../helpers/portal.js';
|
|
9
9
|
import { groupIntoPositionStacks } from '../helpers/positionStacks.js';
|
|
10
|
+
import { useLatestRef } from '../helpers/useLatestRef.js';
|
|
11
|
+
import { useOverlayStack } from '../helpers/overlayStack.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* The `Toast` component provides brief notification messages with optional action and cancel buttons.
|
|
@@ -40,14 +42,19 @@ const Toast = forwardRef(({ message, type = 'default', actionType, position = 't
|
|
|
40
42
|
onAction?.();
|
|
41
43
|
handleClose();
|
|
42
44
|
}, [onAction, handleClose]);
|
|
45
|
+
// Read when the timer runs out, a click lands outside or Escape arrives,
|
|
46
|
+
// so the timer and the listeners stay put while `onClose` changes
|
|
47
|
+
// identity, as an inline one does on every render of the parent. An
|
|
48
|
+
// inline one from `ToastContainer` changes whenever another toast shows.
|
|
49
|
+
const handleCloseRef = useLatestRef(handleClose);
|
|
43
50
|
useEffect(() => {
|
|
44
51
|
if (indefinite || duration === 0 || isPaused)
|
|
45
52
|
return undefined;
|
|
46
53
|
const timer = setTimeout(() => {
|
|
47
|
-
|
|
54
|
+
handleCloseRef.current();
|
|
48
55
|
}, duration);
|
|
49
56
|
return () => clearTimeout(timer);
|
|
50
|
-
}, [duration, indefinite, isPaused,
|
|
57
|
+
}, [duration, indefinite, isPaused, handleCloseRef]);
|
|
51
58
|
const handleMouseEnter = useCallback(() => {
|
|
52
59
|
if (pauseOnHover) {
|
|
53
60
|
setIsPaused(true);
|
|
@@ -65,7 +72,7 @@ const Toast = forwardRef(({ message, type = 'default', actionType, position = 't
|
|
|
65
72
|
return undefined;
|
|
66
73
|
const handleDocumentClick = (e) => {
|
|
67
74
|
if (toastRef.current && !isEventInside(e, toastRef.current)) {
|
|
68
|
-
|
|
75
|
+
handleCloseRef.current();
|
|
69
76
|
}
|
|
70
77
|
};
|
|
71
78
|
const raf = requestAnimationFrame(() => {
|
|
@@ -75,20 +82,24 @@ const Toast = forwardRef(({ message, type = 'default', actionType, position = 't
|
|
|
75
82
|
cancelAnimationFrame(raf);
|
|
76
83
|
document.removeEventListener('click', handleDocumentClick);
|
|
77
84
|
};
|
|
78
|
-
}, [dismissible, isVisible,
|
|
85
|
+
}, [dismissible, isVisible, handleCloseRef]);
|
|
86
|
+
// A toast is a notification rather than a layer, so it never joins the
|
|
87
|
+
// stack of open overlays. Asking the stack tells it whether one is open:
|
|
88
|
+
// an Escape while one is belongs to the overlay, and the toast waits.
|
|
89
|
+
const isTopmost = useOverlayStack(false);
|
|
79
90
|
// On `window`, as in `Modal`, so a popup on the page has had the key
|
|
80
91
|
// first, and an Escape it closed on doesn't dismiss the toast as well.
|
|
81
92
|
useEffect(() => {
|
|
82
93
|
if (!cancelable || !isVisible)
|
|
83
94
|
return undefined;
|
|
84
95
|
const handleKeyDown = (e) => {
|
|
85
|
-
if (e.key === 'Escape' && !e.defaultPrevented) {
|
|
86
|
-
|
|
96
|
+
if (e.key === 'Escape' && !e.defaultPrevented && isTopmost()) {
|
|
97
|
+
handleCloseRef.current();
|
|
87
98
|
}
|
|
88
99
|
};
|
|
89
100
|
window.addEventListener('keydown', handleKeyDown);
|
|
90
101
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
91
|
-
}, [cancelable, isVisible,
|
|
102
|
+
}, [cancelable, isVisible, handleCloseRef, isTopmost]);
|
|
92
103
|
const containerClasses = usePrefixedClassNames('toast-container', {
|
|
93
104
|
[`is-${position}`]: true,
|
|
94
105
|
});
|
|
@@ -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';\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. An Escape a popup on the\n * page closed on, by calling `preventDefault()`, leaves it up. 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 useEffect(() => {\n if (indefinite || duration === 0 || isPaused) return undefined;\n\n const timer = setTimeout(() => {\n handleClose();\n }, duration);\n\n return () => clearTimeout(timer);\n }, [duration, indefinite, isPaused, handleClose]);\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 handleClose();\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, handleClose]);\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) {\n handleClose();\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [cancelable, isVisible, handleClose]);\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 { 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,11 +1,12 @@
|
|
|
1
1
|
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { useState, useRef, useId, isValidElement, Fragment, useEffect,
|
|
3
|
+
import { useState, useRef, useId, isValidElement, Fragment, useEffect, cloneElement } from 'react';
|
|
4
4
|
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
5
5
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
6
6
|
import { useHoverOpen, chain } from '../helpers/useHoverOpen.js';
|
|
7
7
|
import { isTabbableKind } from '../helpers/useFocusTrap.js';
|
|
8
8
|
import { isDev, warnOnce } from '../helpers/devWarnings.js';
|
|
9
|
+
import { useClientLayoutEffect } from '../helpers/useClientLayoutEffect.js';
|
|
9
10
|
|
|
10
11
|
/** An id list with `id` added at the end, unless it's in there already. */
|
|
11
12
|
const withId = (ids, id) => [...new Set([...(ids ?? '').split(/\s+/), id])].filter(Boolean).join(' ');
|
|
@@ -109,10 +110,9 @@ const Tooltip = ({ label, content, position = 'top', color, size, active: always
|
|
|
109
110
|
openOnTap,
|
|
110
111
|
});
|
|
111
112
|
// Auto-placement and overflow correction
|
|
112
|
-
|
|
113
|
+
useClientLayoutEffect(() => {
|
|
113
114
|
const isAuto = position === 'auto';
|
|
114
115
|
if (!isAuto) {
|
|
115
|
-
// eslint-disable-next-line react-hooks/set-state-in-effect -- layout measurement effect resolves placement
|
|
116
116
|
setResolvedPosition(position);
|
|
117
117
|
}
|
|
118
118
|
const showing = isVisible || alwaysActive;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.js","sources":["../../../src/components/Tooltip.tsx"],"sourcesContent":["import React, {\n Fragment,\n cloneElement,\n isValidElement,\n useEffect,\n useId,\n useState,\n useRef,\n useLayoutEffect,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { chain, useHoverOpen } from '../helpers/useHoverOpen';\nimport { isTabbableKind } from '../helpers/useFocusTrap';\nimport { isDev, warnOnce } from '../helpers/devWarnings';\n\n/** Screen positions for the tooltip. */\nexport type TooltipPosition = 'top' | 'bottom' | 'left' | 'right' | 'auto';\n/** Color variants for the tooltip. */\nexport type TooltipColor =\n | 'primary'\n | 'link'\n | 'info'\n | 'success'\n | 'warning'\n | 'danger'\n | 'dark'\n | 'light';\n/** Size variants for the tooltip. */\nexport type TooltipSize = 'small' | 'medium' | 'large';\n\n/** An id list with `id` added at the end, unless it's in there already. */\nconst withId = (ids: string | undefined, id: string) =>\n [...new Set([...(ids ?? '').split(/\\s+/), id])].filter(Boolean).join(' ');\n\n/**\n * The first element in the trigger that Tab lands on, judged by what it is\n * rather than by its styles, which can hide it for now. The trigger is all\n * of the wrapper that comes before the tooltip.\n */\nconst triggerTabStop = (wrapper: Element, tooltip: Element) => {\n const elements = Array.from(wrapper.querySelectorAll('*'));\n return elements\n .slice(0, elements.indexOf(tooltip))\n .find(el => isTabbableKind(el) && !el.matches(':disabled'));\n};\n\n/**\n * Props for the Tooltip component.\n * @extraProp {React.ReactNode} [children] - The element that triggers the\n * tooltip. Its `aria-describedby` points at the tooltip, after any ids it\n * already had, so a screen reader reads the tooltip as its description when\n * it takes focus, even while the tooltip is hidden. That takes a single\n * element which passes `aria-describedby` on to its DOM node, as native\n * elements and bestax components do, and takes focus itself, such as a\n * button, a link or a `Span` with `tabIndex={0}`: something focusable inside\n * it doesn't get the link, and text, a fragment or several elements get none.\n * A development build warns when the first element Tab reaches in the\n * trigger doesn't carry the link, or there isn't one.\n * @extraProp {string} [className] - Additional CSS classes.\n */\nexport interface TooltipProps\n extends\n Omit<React.HTMLAttributes<HTMLSpanElement>, 'color' | 'content'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** The tooltip text content. */\n label?: string;\n /**\n * Rich tooltip content (alternative to `label`), to read rather than use.\n * Nothing in it should take focus or a click: it isn't in the Tab order\n * while hidden, focus there isn't counted as being in the tooltip, and\n * Escape hiding it would drop that focus. For links or buttons, use\n * `HoverCard` or `Popover`. A screen reader reads it as plain text, since\n * it's the trigger's description.\n */\n content?: React.ReactNode;\n /** Position of the tooltip. Default: 'top'. */\n position?: TooltipPosition;\n /** Color variant for the tooltip. */\n color?: TooltipColor;\n /** Size of the tooltip. */\n size?: TooltipSize;\n /**\n * Force tooltip to be always visible. Hover, focus and Escape leave it\n * shown, so Escape goes on to whatever has focus, or to a `Modal` or\n * `Popover` around it.\n */\n active?: boolean;\n /** Allow tooltip to wrap to multiple lines. */\n multiline?: boolean;\n /** Enable fade animation. Default: true. */\n animated?: boolean;\n /** Use square corners instead of rounded. */\n square?: boolean;\n /** Show dashed underline on trigger. */\n dashed?: boolean;\n /**\n * How long the pointer or keyboard focus waits on the trigger before the\n * tooltip shows, in milliseconds.\n */\n delay?: number;\n /**\n * How long the tooltip stays after the pointer and focus have left the\n * trigger and the tooltip, in milliseconds. The pointer can move from the\n * trigger onto a shown tooltip without one, across the default gap. A\n * theme that raises `--bulma-tooltip-arrow-size` or\n * `--bulma-tooltip-arrow-margin` widens the gap past what the pointer can\n * cross on its own, so give it a `closeDelay` long enough to cross the\n * rest.\n */\n closeDelay?: number;\n /**\n * Show the tooltip with a tap on a touch screen. Touch has no hover, so\n * without this a phone or tablet never shows it. With it, a tap on the\n * trigger shows it at once, and it stays until another tap on the trigger\n * or the tooltip, a tap anywhere else, or Escape. The tap still does\n * whatever the trigger does, so turn this on only for a trigger with no\n * tap action of its own. Mouse and pen work the same either way.\n * @defaultValue false\n */\n openOnTap?: boolean;\n /** Additional classes for the tooltip element. */\n tooltipClassName?: string;\n}\n\n/**\n * The `Tooltip` component displays helpful information when users hover over or focus on an element.\n *\n * Hovering the trigger or giving it keyboard focus shows it. Touch doesn't,\n * unless `openOnTap` is on, and neither does focus a click or a tap brings,\n * so tapping the trigger just does what the trigger does. The pointer can\n * move onto a shown tooltip. Escape hides it wherever focus is, without\n * moving the pointer or focus, and it stays hidden until one of them leaves\n * the trigger and comes back, or, with `openOnTap`, until the next tap on\n * the trigger. The key always goes on too, to whatever has focus or a\n * `Modal` or `Popover` around it, since a tooltip only adds to what's\n * there. The tooltip is its trigger's description, so a screen reader reads\n * it as the trigger takes focus, whether it's showing yet or not.\n *\n * @function\n * @param {TooltipProps} props - Props for the Tooltip component.\n * @returns {JSX.Element} The rendered tooltip wrapper.\n *\n * @example\n * // Basic tooltip\n * <Tooltip label=\"This is helpful info\">\n * <button>Hover me</button>\n * </Tooltip>\n *\n * @example\n * // Positioned tooltip with color, on an icon-only button with a name of its own\n * <Tooltip label=\"Moves it to the trash\" position=\"right\" color=\"danger\">\n * <Button aria-label=\"Delete\">\n * <Icon name=\"fas fa-trash\" />\n * </Button>\n * </Tooltip>\n *\n * @example\n * // Multiline tooltip\n * <Tooltip label=\"This is a longer tooltip that wraps to multiple lines\" multiline>\n * <button>More info</button>\n * </Tooltip>\n */\nexport const Tooltip: React.FC<TooltipProps> = ({\n label,\n content,\n position = 'top',\n color,\n size,\n active: alwaysActive,\n multiline = false,\n animated = true,\n square = false,\n dashed = false,\n delay = 0,\n closeDelay = 0,\n openOnTap = false,\n children,\n className,\n tooltipClassName,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const {\n onPointerEnter,\n onPointerOver,\n onPointerLeave,\n onPointerDown,\n onPointerUp,\n onFocus,\n onBlur,\n ...attrs\n } = rest;\n const [isVisible, setIsVisible] = useState(false);\n const [resolvedPosition, setResolvedPosition] = useState<\n Exclude<TooltipPosition, 'auto'>\n >(position === 'auto' ? 'top' : position);\n const [contentStyle, setContentStyle] = useState<React.CSSProperties>({});\n const wrapperRef = useRef<HTMLSpanElement | null>(null);\n const contentRef = useRef<HTMLSpanElement | null>(null);\n const tooltipId = `${useId()}-tooltip`;\n\n // Only a single element can be given the link, and a fragment takes no\n // props of its own.\n const linkable =\n isValidElement<{ 'aria-describedby'?: string }>(children) &&\n children.type !== Fragment;\n\n // A screen reader reads the tooltip only when Tab lands on an element that\n // carries the link. Only the page shows whether it does, since a component\n // may not pass the attribute on, or what takes focus may sit inside the\n // element that took it. After every commit, since the children can change,\n // and only in development.\n useEffect(() => {\n if (!isDev()) return;\n const stop = triggerTabStop(\n wrapperRef.current as HTMLSpanElement,\n contentRef.current as HTMLSpanElement\n );\n if (\n stop?.getAttribute('aria-describedby')?.split(/\\s+/).includes(tooltipId)\n ) {\n return;\n }\n warnOnce(\n 'Tooltip:unlinked-trigger',\n \"[bestax-bulma] <Tooltip> can't describe its trigger, so a screen \" +\n \"reader won't read the tooltip: Tab reaches nothing in the trigger, \" +\n \"or what it reaches doesn't carry the aria-describedby that points \" +\n 'at the tooltip. Pass one element that takes focus and passes ' +\n 'aria-describedby on to its DOM node, such as a button, a link or a ' +\n 'Span with tabIndex={0}.'\n );\n });\n\n const hover = useHoverOpen({\n // The pointer and focus as they are. `active` shows it on top of that, so\n // dropping `active` leaves it shown while the pointer or focus holds it.\n // An ask to close can come while it's already hidden, which is no change.\n open: isVisible,\n onOpenChange: setIsVisible,\n openDelay: delay,\n closeDelay,\n // The wrapper stands in for the trigger. It holds only the children and\n // the tooltip, and nothing in the tooltip takes focus, so focus anywhere\n // in it is focus on the trigger.\n triggerRef: wrapperRef,\n floatingRef: contentRef,\n // Escape hides it and carries on, so a control in the trigger, or a\n // Modal around it, still gets the key.\n consumeEscape: false,\n openOnTap,\n });\n\n // Auto-placement and overflow correction\n useLayoutEffect(() => {\n const isAuto = position === 'auto';\n\n if (!isAuto) {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- layout measurement effect resolves placement\n setResolvedPosition(position);\n }\n\n const showing = isVisible || alwaysActive;\n if (!showing) {\n setContentStyle({});\n return;\n }\n\n const wrapper = wrapperRef.current;\n const content = contentRef.current;\n if (!wrapper || !content) return;\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const margin = 40;\n const edgePadding = 8; // minimum distance from viewport edge\n\n // Step 1: determine position (auto mode only)\n if (isAuto) {\n const spaceTop = wrapperRect.top;\n const spaceBottom = window.innerHeight - wrapperRect.bottom;\n\n if (spaceTop >= margin) {\n setResolvedPosition('top');\n } else if (spaceBottom >= margin) {\n setResolvedPosition('bottom');\n } else if (window.innerWidth - wrapperRect.right >= wrapperRect.left) {\n setResolvedPosition('right');\n } else {\n setResolvedPosition('left');\n }\n }\n\n // Step 2: measure tooltip content and correct overflow.\n // Use requestAnimationFrame so the resolved position class is applied first.\n requestAnimationFrame(() => {\n const contentEl = contentRef.current;\n if (!contentEl) return;\n\n const contentRect = contentEl.getBoundingClientRect();\n const style: React.CSSProperties = {};\n\n // For top/bottom positions, tooltip is centered horizontally via CSS\n // (left: 50%; transform: translateX(-50%)).\n // Check if it overflows left or right and apply a corrective shift.\n const pos = isAuto ? undefined : position; // read from state won't be updated yet in auto\n const effectivePos = pos || 'top'; // fallback, actual is set by state\n const isVertical =\n effectivePos === 'top' ||\n effectivePos === 'bottom' ||\n resolvedPosition === 'top' ||\n resolvedPosition === 'bottom';\n\n if (isVertical) {\n if (contentRect.left < edgePadding) {\n // Overflows left — shift right\n const shift = edgePadding - contentRect.left;\n style.left = `calc(50% + ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${-shift}px`;\n } else if (contentRect.right > window.innerWidth - edgePadding) {\n // Overflows right — shift left\n const shift = contentRect.right - (window.innerWidth - edgePadding);\n style.left = `calc(50% - ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${shift}px`;\n }\n } else {\n // For left/right positions, tooltip is centered vertically via CSS\n // (top: 50%; transform: translateY(-50%)).\n if (contentRect.top < edgePadding) {\n const shift = edgePadding - contentRect.top;\n style.top = `calc(50% + ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${-shift}px`;\n } else if (contentRect.bottom > window.innerHeight - edgePadding) {\n const shift = contentRect.bottom - (window.innerHeight - edgePadding);\n style.top = `calc(50% - ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${shift}px`;\n }\n }\n\n setContentStyle(style);\n });\n }, [position, resolvedPosition, isVisible, alwaysActive, label, content]);\n\n // Generate Bulma classes\n const tooltipClasses = usePrefixedClassNames('tooltip', {\n 'is-active': alwaysActive || isVisible,\n // Open by the pointer, focus or a tap, rather than shown by `active`\n // alone: only then does the tooltip, and the gap to it, take the pointer.\n 'is-open': isVisible,\n [`is-${resolvedPosition}`]: resolvedPosition,\n [`is-${color}`]: color,\n [`is-${size}`]: size,\n 'is-multiline': multiline,\n 'is-animated': animated,\n 'is-square': square,\n 'is-dashed': dashed,\n });\n\n const combinedClasses = classNames(\n tooltipClasses,\n bulmaHelperClasses,\n className\n );\n const combinedTooltipClasses = usePrefixedClassNames(\n 'tooltip-content',\n tooltipClassName\n );\n\n return (\n // The tooltip is inside the wrapper, so the pointer on it is still on the\n // wrapper, and the stylesheet bridges the gap between them.\n <span\n ref={wrapperRef}\n className={combinedClasses}\n onPointerEnter={chain(onPointerEnter, hover.onPointerEnter)}\n onPointerOver={chain(onPointerOver, hover.onPointerOver)}\n onPointerLeave={chain(onPointerLeave, hover.onPointerLeave)}\n onFocus={chain(onFocus, hover.onFocus)}\n onBlur={chain(onBlur, hover.onBlur)}\n onPointerDown={chain(onPointerDown, hover.onPointerDown)}\n onPointerUp={chain(onPointerUp, hover.onPointerUp)}\n {...(label ? { 'data-tooltip': label } : {})}\n {...attrs}\n >\n {linkable\n ? cloneElement(children, {\n 'aria-describedby': withId(\n children.props['aria-describedby'],\n tooltipId\n ),\n })\n : children}\n <span\n ref={contentRef}\n id={tooltipId}\n className={combinedTooltipClasses}\n // Hidden by `visibility: hidden` in the stylesheet, which keeps it out\n // of the accessibility tree and still lets it describe the trigger\n // (accname step 2A), so it takes no aria-hidden.\n role=\"tooltip\"\n style={Object.keys(contentStyle).length > 0 ? contentStyle : undefined}\n >\n {content || label}\n </span>\n </span>\n );\n};\n\nexport default Tooltip;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tooltip.js","sources":["../../../src/components/Tooltip.tsx"],"sourcesContent":["import React, {\n Fragment,\n cloneElement,\n isValidElement,\n useEffect,\n useId,\n useState,\n useRef,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { chain, useHoverOpen } from '../helpers/useHoverOpen';\nimport { isTabbableKind } from '../helpers/useFocusTrap';\nimport { isDev, warnOnce } from '../helpers/devWarnings';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\n\n/** Screen positions for the tooltip. */\nexport type TooltipPosition = 'top' | 'bottom' | 'left' | 'right' | 'auto';\n/** Color variants for the tooltip. */\nexport type TooltipColor =\n | 'primary'\n | 'link'\n | 'info'\n | 'success'\n | 'warning'\n | 'danger'\n | 'dark'\n | 'light';\n/** Size variants for the tooltip. */\nexport type TooltipSize = 'small' | 'medium' | 'large';\n\n/** An id list with `id` added at the end, unless it's in there already. */\nconst withId = (ids: string | undefined, id: string) =>\n [...new Set([...(ids ?? '').split(/\\s+/), id])].filter(Boolean).join(' ');\n\n/**\n * The first element in the trigger that Tab lands on, judged by what it is\n * rather than by its styles, which can hide it for now. The trigger is all\n * of the wrapper that comes before the tooltip.\n */\nconst triggerTabStop = (wrapper: Element, tooltip: Element) => {\n const elements = Array.from(wrapper.querySelectorAll('*'));\n return elements\n .slice(0, elements.indexOf(tooltip))\n .find(el => isTabbableKind(el) && !el.matches(':disabled'));\n};\n\n/**\n * Props for the Tooltip component.\n * @extraProp {React.ReactNode} [children] - The element that triggers the\n * tooltip. Its `aria-describedby` points at the tooltip, after any ids it\n * already had, so a screen reader reads the tooltip as its description when\n * it takes focus, even while the tooltip is hidden. That takes a single\n * element which passes `aria-describedby` on to its DOM node, as native\n * elements and bestax components do, and takes focus itself, such as a\n * button, a link or a `Span` with `tabIndex={0}`: something focusable inside\n * it doesn't get the link, and text, a fragment or several elements get none.\n * A development build warns when the first element Tab reaches in the\n * trigger doesn't carry the link, or there isn't one.\n * @extraProp {string} [className] - Additional CSS classes.\n */\nexport interface TooltipProps\n extends\n Omit<React.HTMLAttributes<HTMLSpanElement>, 'color' | 'content'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** The tooltip text content. */\n label?: string;\n /**\n * Rich tooltip content (alternative to `label`), to read rather than use.\n * Nothing in it should take focus or a click: it isn't in the Tab order\n * while hidden, focus there isn't counted as being in the tooltip, and\n * Escape hiding it would drop that focus. For links or buttons, use\n * `HoverCard` or `Popover`. A screen reader reads it as plain text, since\n * it's the trigger's description.\n */\n content?: React.ReactNode;\n /** Position of the tooltip. Default: 'top'. */\n position?: TooltipPosition;\n /** Color variant for the tooltip. */\n color?: TooltipColor;\n /** Size of the tooltip. */\n size?: TooltipSize;\n /**\n * Force tooltip to be always visible. Hover, focus and Escape leave it\n * shown, so Escape goes on to whatever has focus, or to a `Modal` or\n * `Popover` around it.\n */\n active?: boolean;\n /** Allow tooltip to wrap to multiple lines. */\n multiline?: boolean;\n /** Enable fade animation. Default: true. */\n animated?: boolean;\n /** Use square corners instead of rounded. */\n square?: boolean;\n /** Show dashed underline on trigger. */\n dashed?: boolean;\n /**\n * How long the pointer or keyboard focus waits on the trigger before the\n * tooltip shows, in milliseconds.\n */\n delay?: number;\n /**\n * How long the tooltip stays after the pointer and focus have left the\n * trigger and the tooltip, in milliseconds. The pointer can move from the\n * trigger onto a shown tooltip without one, across the default gap. A\n * theme that raises `--bulma-tooltip-arrow-size` or\n * `--bulma-tooltip-arrow-margin` widens the gap past what the pointer can\n * cross on its own, so give it a `closeDelay` long enough to cross the\n * rest.\n */\n closeDelay?: number;\n /**\n * Show the tooltip with a tap on a touch screen. Touch has no hover, so\n * without this a phone or tablet never shows it. With it, a tap on the\n * trigger shows it at once, and it stays until another tap on the trigger\n * or the tooltip, a tap anywhere else, or Escape. The tap still does\n * whatever the trigger does, so turn this on only for a trigger with no\n * tap action of its own. Mouse and pen work the same either way.\n * @defaultValue false\n */\n openOnTap?: boolean;\n /** Additional classes for the tooltip element. */\n tooltipClassName?: string;\n}\n\n/**\n * The `Tooltip` component displays helpful information when users hover over or focus on an element.\n *\n * Hovering the trigger or giving it keyboard focus shows it. Touch doesn't,\n * unless `openOnTap` is on, and neither does focus a click or a tap brings,\n * so tapping the trigger just does what the trigger does. The pointer can\n * move onto a shown tooltip. Escape hides it wherever focus is, without\n * moving the pointer or focus, and it stays hidden until one of them leaves\n * the trigger and comes back, or, with `openOnTap`, until the next tap on\n * the trigger. The key always goes on too, to whatever has focus or a\n * `Modal` or `Popover` around it, since a tooltip only adds to what's\n * there. The tooltip is its trigger's description, so a screen reader reads\n * it as the trigger takes focus, whether it's showing yet or not.\n *\n * @function\n * @param {TooltipProps} props - Props for the Tooltip component.\n * @returns {JSX.Element} The rendered tooltip wrapper.\n *\n * @example\n * // Basic tooltip\n * <Tooltip label=\"This is helpful info\">\n * <button>Hover me</button>\n * </Tooltip>\n *\n * @example\n * // Positioned tooltip with color, on an icon-only button with a name of its own\n * <Tooltip label=\"Moves it to the trash\" position=\"right\" color=\"danger\">\n * <Button aria-label=\"Delete\">\n * <Icon name=\"fas fa-trash\" />\n * </Button>\n * </Tooltip>\n *\n * @example\n * // Multiline tooltip\n * <Tooltip label=\"This is a longer tooltip that wraps to multiple lines\" multiline>\n * <button>More info</button>\n * </Tooltip>\n */\nexport const Tooltip: React.FC<TooltipProps> = ({\n label,\n content,\n position = 'top',\n color,\n size,\n active: alwaysActive,\n multiline = false,\n animated = true,\n square = false,\n dashed = false,\n delay = 0,\n closeDelay = 0,\n openOnTap = false,\n children,\n className,\n tooltipClassName,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const {\n onPointerEnter,\n onPointerOver,\n onPointerLeave,\n onPointerDown,\n onPointerUp,\n onFocus,\n onBlur,\n ...attrs\n } = rest;\n const [isVisible, setIsVisible] = useState(false);\n const [resolvedPosition, setResolvedPosition] = useState<\n Exclude<TooltipPosition, 'auto'>\n >(position === 'auto' ? 'top' : position);\n const [contentStyle, setContentStyle] = useState<React.CSSProperties>({});\n const wrapperRef = useRef<HTMLSpanElement | null>(null);\n const contentRef = useRef<HTMLSpanElement | null>(null);\n const tooltipId = `${useId()}-tooltip`;\n\n // Only a single element can be given the link, and a fragment takes no\n // props of its own.\n const linkable =\n isValidElement<{ 'aria-describedby'?: string }>(children) &&\n children.type !== Fragment;\n\n // A screen reader reads the tooltip only when Tab lands on an element that\n // carries the link. Only the page shows whether it does, since a component\n // may not pass the attribute on, or what takes focus may sit inside the\n // element that took it. After every commit, since the children can change,\n // and only in development.\n useEffect(() => {\n if (!isDev()) return;\n const stop = triggerTabStop(\n wrapperRef.current as HTMLSpanElement,\n contentRef.current as HTMLSpanElement\n );\n if (\n stop?.getAttribute('aria-describedby')?.split(/\\s+/).includes(tooltipId)\n ) {\n return;\n }\n warnOnce(\n 'Tooltip:unlinked-trigger',\n \"[bestax-bulma] <Tooltip> can't describe its trigger, so a screen \" +\n \"reader won't read the tooltip: Tab reaches nothing in the trigger, \" +\n \"or what it reaches doesn't carry the aria-describedby that points \" +\n 'at the tooltip. Pass one element that takes focus and passes ' +\n 'aria-describedby on to its DOM node, such as a button, a link or a ' +\n 'Span with tabIndex={0}.'\n );\n });\n\n const hover = useHoverOpen({\n // The pointer and focus as they are. `active` shows it on top of that, so\n // dropping `active` leaves it shown while the pointer or focus holds it.\n // An ask to close can come while it's already hidden, which is no change.\n open: isVisible,\n onOpenChange: setIsVisible,\n openDelay: delay,\n closeDelay,\n // The wrapper stands in for the trigger. It holds only the children and\n // the tooltip, and nothing in the tooltip takes focus, so focus anywhere\n // in it is focus on the trigger.\n triggerRef: wrapperRef,\n floatingRef: contentRef,\n // Escape hides it and carries on, so a control in the trigger, or a\n // Modal around it, still gets the key.\n consumeEscape: false,\n openOnTap,\n });\n\n // Auto-placement and overflow correction\n useClientLayoutEffect(() => {\n const isAuto = position === 'auto';\n\n if (!isAuto) {\n setResolvedPosition(position);\n }\n\n const showing = isVisible || alwaysActive;\n if (!showing) {\n setContentStyle({});\n return;\n }\n\n const wrapper = wrapperRef.current;\n const content = contentRef.current;\n if (!wrapper || !content) return;\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const margin = 40;\n const edgePadding = 8; // minimum distance from viewport edge\n\n // Step 1: determine position (auto mode only)\n if (isAuto) {\n const spaceTop = wrapperRect.top;\n const spaceBottom = window.innerHeight - wrapperRect.bottom;\n\n if (spaceTop >= margin) {\n setResolvedPosition('top');\n } else if (spaceBottom >= margin) {\n setResolvedPosition('bottom');\n } else if (window.innerWidth - wrapperRect.right >= wrapperRect.left) {\n setResolvedPosition('right');\n } else {\n setResolvedPosition('left');\n }\n }\n\n // Step 2: measure tooltip content and correct overflow.\n // Use requestAnimationFrame so the resolved position class is applied first.\n requestAnimationFrame(() => {\n const contentEl = contentRef.current;\n if (!contentEl) return;\n\n const contentRect = contentEl.getBoundingClientRect();\n const style: React.CSSProperties = {};\n\n // For top/bottom positions, tooltip is centered horizontally via CSS\n // (left: 50%; transform: translateX(-50%)).\n // Check if it overflows left or right and apply a corrective shift.\n const pos = isAuto ? undefined : position; // read from state won't be updated yet in auto\n const effectivePos = pos || 'top'; // fallback, actual is set by state\n const isVertical =\n effectivePos === 'top' ||\n effectivePos === 'bottom' ||\n resolvedPosition === 'top' ||\n resolvedPosition === 'bottom';\n\n if (isVertical) {\n if (contentRect.left < edgePadding) {\n // Overflows left — shift right\n const shift = edgePadding - contentRect.left;\n style.left = `calc(50% + ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${-shift}px`;\n } else if (contentRect.right > window.innerWidth - edgePadding) {\n // Overflows right — shift left\n const shift = contentRect.right - (window.innerWidth - edgePadding);\n style.left = `calc(50% - ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${shift}px`;\n }\n } else {\n // For left/right positions, tooltip is centered vertically via CSS\n // (top: 50%; transform: translateY(-50%)).\n if (contentRect.top < edgePadding) {\n const shift = edgePadding - contentRect.top;\n style.top = `calc(50% + ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${-shift}px`;\n } else if (contentRect.bottom > window.innerHeight - edgePadding) {\n const shift = contentRect.bottom - (window.innerHeight - edgePadding);\n style.top = `calc(50% - ${shift}px)`;\n (style as Record<string, string>)['--tooltip-arrow-offset'] =\n `${shift}px`;\n }\n }\n\n setContentStyle(style);\n });\n }, [position, resolvedPosition, isVisible, alwaysActive, label, content]);\n\n // Generate Bulma classes\n const tooltipClasses = usePrefixedClassNames('tooltip', {\n 'is-active': alwaysActive || isVisible,\n // Open by the pointer, focus or a tap, rather than shown by `active`\n // alone: only then does the tooltip, and the gap to it, take the pointer.\n 'is-open': isVisible,\n [`is-${resolvedPosition}`]: resolvedPosition,\n [`is-${color}`]: color,\n [`is-${size}`]: size,\n 'is-multiline': multiline,\n 'is-animated': animated,\n 'is-square': square,\n 'is-dashed': dashed,\n });\n\n const combinedClasses = classNames(\n tooltipClasses,\n bulmaHelperClasses,\n className\n );\n const combinedTooltipClasses = usePrefixedClassNames(\n 'tooltip-content',\n tooltipClassName\n );\n\n return (\n // The tooltip is inside the wrapper, so the pointer on it is still on the\n // wrapper, and the stylesheet bridges the gap between them.\n <span\n ref={wrapperRef}\n className={combinedClasses}\n onPointerEnter={chain(onPointerEnter, hover.onPointerEnter)}\n onPointerOver={chain(onPointerOver, hover.onPointerOver)}\n onPointerLeave={chain(onPointerLeave, hover.onPointerLeave)}\n onFocus={chain(onFocus, hover.onFocus)}\n onBlur={chain(onBlur, hover.onBlur)}\n onPointerDown={chain(onPointerDown, hover.onPointerDown)}\n onPointerUp={chain(onPointerUp, hover.onPointerUp)}\n {...(label ? { 'data-tooltip': label } : {})}\n {...attrs}\n >\n {linkable\n ? cloneElement(children, {\n 'aria-describedby': withId(\n children.props['aria-describedby'],\n tooltipId\n ),\n })\n : children}\n <span\n ref={contentRef}\n id={tooltipId}\n className={combinedTooltipClasses}\n // Hidden by `visibility: hidden` in the stylesheet, which keeps it out\n // of the accessibility tree and still lets it describe the trigger\n // (accname step 2A), so it takes no aria-hidden.\n role=\"tooltip\"\n style={Object.keys(contentStyle).length > 0 ? contentStyle : undefined}\n >\n {content || label}\n </span>\n </span>\n );\n};\n\nexport default Tooltip;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -105,8 +105,7 @@ function getIconClasses(library, name, variant, features) {
|
|
|
105
105
|
*/
|
|
106
106
|
const Icon = ({ className, textColor, bgColor, name, library, variant, features, libraryFeatures, // Deprecated but maintained for backward compatibility
|
|
107
107
|
size, ariaLabel, style, icon, // Capture and exclude the deprecated 'icon' prop from DOM
|
|
108
|
-
color
|
|
109
|
-
containerClassName, children, ...restProps }) => {
|
|
108
|
+
color, containerClassName, children, ...restProps }) => {
|
|
110
109
|
// Get the default icon library from context, fallback to 'fa' if not set
|
|
111
110
|
const defaultLibrary = useIconLibrary();
|
|
112
111
|
/**
|
|
@@ -114,7 +113,7 @@ containerClassName, children, ...restProps }) => {
|
|
|
114
113
|
* Note: variant, features, and libraryFeatures are excluded from props spread
|
|
115
114
|
*/
|
|
116
115
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
117
|
-
color: textColor,
|
|
116
|
+
color: textColor ?? color,
|
|
118
117
|
backgroundColor: bgColor,
|
|
119
118
|
...restProps,
|
|
120
119
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Icon.js","sources":["../../../src/elements/Icon.tsx"],"sourcesContent":["import React from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useIconLibrary } from '../helpers/Config';\n\n// TypeScript declaration for Ionicons web component\ninterface IonIconProps extends React.HTMLAttributes<HTMLElement> {\n name?: string;\n src?: string;\n icon?: unknown;\n size?: string;\n lazy?: boolean;\n sanitize?: boolean;\n color?: string;\n flipRtl?: boolean;\n ariaLabel?: string;\n ariaHidden?: string;\n}\n\n// React 19 moved the global `JSX` namespace under the `react` module, so the\n// `<ion-icon>` web-component augmentation must target `React.JSX` to take effect.\ndeclare module 'react' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'ion-icon': IonIconProps;\n }\n }\n}\n\ntype IconLibrary = 'fa' | 'mdi' | 'ion' | 'material-icons' | 'material-symbols'; // 'fa' = Font Awesome, 'mdi' = Material Design Icons, 'ion' = Ionicons Web Components, 'material-icons' = Google Material Icons, 'material-symbols' = Google Material Symbols\n\n/**\n * Shared props for the Icon component, independent of whether a class-based `name` or a\n * custom `children` node supplies the glyph.\n */\ninterface IconBaseProps\n extends React.HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {\n /** Additional CSS classes to apply. */\n className?: string;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Bulma color modifier for the icon. */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /**\n * Legacy icon class string (e.g. `'fas fa-star'`). Only its last segment is read, as the\n * glyph name; the library still comes from `library` or `ConfigProvider`, so\n * `'mdi mdi-rocket'` renders an `fa` class unless the effective library is already `mdi` —\n * set it here or on the provider.\n *\n * @deprecated Use `name` instead.\n */\n icon?: string;\n /**\n * The icon library to use ('fa' = Font Awesome, 'mdi' = Material Design Icons, 'ion' = Ionicons Web Components, 'material-icons' = Google Material Icons, 'material-symbols' = Google Material Symbols). Defaults to the value set in ConfigProvider or 'fa' if not configured. Ignored when `children` supplies the glyph instead of `name`.\n * @defaultValue 'fa'\n */\n library?: IconLibrary; // defaults to ConfigProvider iconLibrary or 'fa'\n /** Icon style variant (e.g. `'solid'`, `'outlined'`, `'rounded'`). Ignored when `children` supplies the glyph instead of `name`. */\n variant?: string; // e.g., 'solid', 'outlined', 'rounded', 'sharp'\n /** Additional modifiers (e.g. `'fa-lg'`, `'fa-spin'`, `'is-size-1'`). Ignored when `children` supplies the glyph instead of `name`. */\n features?: string | string[]; // e.g., 'fa-lg', 'fa-spin', 'is-size-1'\n /**\n * Additional modifiers in the older combined form, parsed into `variant` and `features`.\n *\n * @deprecated Use `variant` and `features` instead.\n */\n libraryFeatures?: string | string[];\n /** Size modifier for the icon container. */\n size?: 'small' | 'medium' | 'large';\n /**\n * Accessible name for an icon that means something on its own. Set it and the container\n * span gets `role=\"img\"` with this `aria-label`, so the name is exactly this text: a\n * Material ligature such as `rocket_launch` inside stays out of it. Leave it unset (or\n * empty) and the icon is decorative: the span renders `aria-hidden=\"true\"` and no name,\n * so it adds nothing to the accessible name of a `Button` or text around it. For an\n * icon-only control, put the name on the control (e.g. `Button`'s `aria-label`) and leave\n * this unset. A non-empty native `aria-label` or `aria-labelledby` passed instead also\n * counts as a name, and an explicit `role` or `aria-hidden` wins over both defaults. An\n * unnamed icon given a `role` or a `tabIndex` of its own isn't hidden, since hiding\n * something focusable is a failure of its own.\n */\n ariaLabel?: string;\n /** Inline style object. */\n style?: React.CSSProperties;\n /** Override the default `'icon'` container class (e.g., `'panel-icon'`). */\n containerClassName?: string; // Override the default 'icon' container class (e.g., 'panel-icon')\n}\n\n/**\n * Props for `Icon` rendering a class-based glyph from an icon library.\n */\nexport interface IconNameProps extends IconBaseProps {\n /** The icon name, with or without its library prefix (e.g. `'star'` or `'fa-star'`). Mutually exclusive with `children`. */\n name: string; // e.g., 'star', 'account', 'home-outline'\n /** A custom node (an inline SVG, a `react-icons` component, …) rendered inside the icon container in place of a class-based glyph. Mutually exclusive with `name`. */\n children?: never;\n}\n\n/**\n * Props for `Icon` rendering a custom node (an inline SVG, a `react-icons` component, a Font\n * Awesome React `<FontAwesomeIcon>`, …) in place of a class-based glyph.\n */\nexport interface IconChildrenProps extends IconBaseProps {\n /** The icon name, with or without its library prefix (e.g. `'star'` or `'fa-star'`). Mutually exclusive with `children`. */\n name?: undefined;\n /** A custom node (an inline SVG, a `react-icons` component, …) rendered inside the icon container in place of a class-based glyph. Mutually exclusive with `name`. */\n // `undefined` is excluded deliberately: the renderer discriminates on `children !== undefined`,\n // so `children={undefined}` would satisfy the type but fall through to the `name` path with no\n // name and render an `fa-undefined` glyph. Excluding it keeps the advertised\n // exactly-one-of-`name`-or-`children` constraint matching what actually renders.\n children: Exclude<React.ReactNode, undefined>;\n}\n\n/**\n * Props for `Icon` naming its glyph through the deprecated `icon` prop instead of `name`.\n *\n * The runtime has always accepted `icon` on its own — it strips a leading library prefix off\n * the class string and falls through to the `name` path — but the type offered no member without\n * a `name` or `children`, so every caller still on the deprecated prop got an error the\n * package could not see — until #663 nothing here type-checked a test or a story, so the\n * three `Icon.test.tsx` cases exercising this path proved the runtime and said nothing about\n * the type. Declaring the path is the honest resolution: it is deprecated, not removed, and a\n * deprecation that does not type-check is a removal announced only to whoever tries it.\n *\n * @deprecated Pass `name` instead. This member goes when `icon` does.\n */\nexport interface IconDeprecatedProps extends IconBaseProps {\n /**\n * Legacy icon class string (e.g. `'fas fa-star'`). Only its last segment is read, as the\n * glyph name; the library still comes from `library` or `ConfigProvider`, so\n * `'mdi mdi-rocket'` renders an `fa` class unless the effective library is already `mdi` —\n * set it here or on the provider.\n *\n * @deprecated Use `name` instead.\n */\n // Required here, optional on `IconBaseProps`: the other two members allow it\n // alongside `name`, and this one is the member where it IS the glyph. Same\n // prose in both places on purpose — the API-docs extractor reads whichever it\n // reaches first, and the two must not drift apart.\n icon: string;\n /** The icon name. Absent on this path — `icon` supplies the glyph. */\n name?: undefined;\n /** A custom node. Mutually exclusive with `icon`. */\n children?: never;\n}\n\n/**\n * Props for the Icon component — a discriminated union of a class-based `name`, a custom\n * `children` node, and the deprecated `icon` class string.\n */\nexport type IconProps = IconNameProps | IconChildrenProps | IconDeprecatedProps;\n\n/**\n * Strips a redundant leading library prefix from an icon name (e.g. `fa-check` -> `check`\n * for the `fa` library, `mdi-account` -> `account` for `mdi`), so `name` behaves the same\n * whether or not the caller includes the prefix. Font Awesome and Material Design Icons docs\n * conventionally refer to icons with their class prefix (`fa-check`), which otherwise gets\n * doubled by `getIconClasses` (producing `fa-fa-check`, matching no glyph).\n *\n * @param {string} name - The icon name, possibly including a redundant library prefix.\n * @param {IconLibrary} library - The icon library the name will be rendered with.\n * @returns {string} The icon name with any redundant leading library prefix removed.\n */\nfunction stripRedundantLibraryPrefix(\n name: string,\n library: IconLibrary\n): string {\n if (!name) {\n return name;\n }\n if (library === 'fa' && name.startsWith('fa-')) {\n return name.substring(3);\n }\n if (library === 'mdi' && name.startsWith('mdi-')) {\n return name.substring(4);\n }\n return name;\n}\n\n/**\n * Gets the correct classes for the icon element based on the library and features.\n *\n * @param {IconLibrary} library - The icon library.\n * @param {string} name - The icon name.\n * @param {string} [variant] - Icon style variant (e.g., 'solid', 'outlined', 'rounded').\n * @param {string | string[]} [features] - Additional library-specific modifiers.\n * @returns {string} The combined class string for the icon.\n */\nfunction getIconClasses(\n library: IconLibrary,\n name: string,\n variant?: string,\n features?: string | string[]\n): string {\n let baseClass: string;\n let iconClass: string;\n const featureList = Array.isArray(features)\n ? features\n : features\n ? [features]\n : [];\n\n switch (library) {\n case 'fa': {\n // Font Awesome: use variant as style ('solid' -> 'fas', 'regular' -> 'far', etc.)\n const styleMap: Record<string, string> = {\n solid: 'fas',\n regular: 'far',\n brands: 'fab',\n light: 'fal',\n duotone: 'fad',\n thin: 'fat',\n };\n const faStyle = variant ? styleMap[variant] || variant : 'fas';\n baseClass = faStyle;\n iconClass = `fa-${name}`;\n return [baseClass, iconClass, ...featureList].join(' ');\n }\n case 'mdi':\n // Material Design Icons: no variants, just features\n baseClass = 'mdi';\n iconClass = `mdi-${name}`;\n return [baseClass, iconClass, ...featureList].join(' ');\n case 'material-icons': {\n // Google Material Icons: map variants to full class names\n const styleVariants: Record<string, string> = {\n filled: 'material-icons',\n outlined: 'material-icons-outlined',\n round: 'material-icons-round',\n sharp: 'material-icons-sharp',\n };\n baseClass = variant\n ? styleVariants[variant] || `material-icons-${variant}`\n : 'material-icons';\n return [baseClass, ...featureList].join(' ');\n }\n case 'material-symbols': {\n // Google Material Symbols: map variants to full class names\n const styleVariants: Record<string, string> = {\n outlined: 'material-symbols-outlined',\n rounded: 'material-symbols-rounded',\n sharp: 'material-symbols-sharp',\n };\n baseClass = variant\n ? styleVariants[variant] || `material-symbols-${variant}`\n : 'material-symbols-outlined';\n return [baseClass, ...featureList].join(' ');\n }\n default:\n // fallback: just icon name and features\n return [name, ...featureList].join(' ');\n }\n}\n\n/**\n * The `Icon` component is a Bulma-styled wrapper for displaying icons from various libraries (Font Awesome, Material Design Icons, Ionicons, Google Material Icons, Material Symbols, etc.).\n *\n * @function\n * @param {IconProps} props - Props for the Icon component.\n * @returns {JSX.Element} The rendered icon element.\n * @see {@link https://bulma.io/documentation/elements/icon/ | Bulma Icon documentation}\n */\nexport const Icon: React.FC<IconProps> = ({\n className,\n textColor,\n bgColor,\n name,\n library,\n variant,\n features,\n libraryFeatures, // Deprecated but maintained for backward compatibility\n size,\n ariaLabel,\n style,\n icon, // Capture and exclude the deprecated 'icon' prop from DOM\n color: _color, // Exclude 'color' prop if passed directly\n containerClassName,\n children,\n ...restProps\n}) => {\n // Get the default icon library from context, fallback to 'fa' if not set\n const defaultLibrary = useIconLibrary();\n\n /**\n * Generates Bulma helper classes and separates out remaining props.\n * Note: variant, features, and libraryFeatures are excluded from props spread\n */\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...restProps,\n });\n\n // Hoisted unconditionally to respect rules-of-hooks; the branches below\n // pick which result to consume.\n const defaultIconClasses = usePrefixedClassNames('icon', {\n [`is-${size}`]: size,\n });\n const sizeModifierClass = usePrefixedClassNames(\n size ? `is-${size}` : undefined\n );\n\n const bulmaClasses = containerClassName\n ? containerClassName\n : defaultIconClasses;\n\n const iconContainerClasses = classNames(\n bulmaClasses,\n containerClassName && size ? sizeModifierClass : undefined,\n bulmaHelperClasses,\n className\n );\n\n // A named icon is an image, whose name is its label alone, so a ligature inside\n // never leaks into it. An unnamed one is decorative and hidden outright. Spread\n // before `rest`, so a caller's own `role` or `aria-hidden` still wins.\n const named =\n !!ariaLabel || !!restProps['aria-label'] || !!restProps['aria-labelledby'];\n // A caller's own `role` or `tabIndex` on an unnamed icon means it isn't decoration (an\n // icon made a button, say), so it isn't hidden either: `aria-hidden` on something\n // focusable is a failure of its own.\n const a11yProps = named\n ? { role: 'img', 'aria-label': ariaLabel || undefined }\n : restProps.role !== undefined || restProps.tabIndex !== undefined\n ? {}\n : { 'aria-hidden': true };\n\n if (children !== undefined) {\n // `IconChildrenProps`: render the caller's node (an inline SVG, a `react-icons`\n // component, …) in place of a class-based glyph. Library/variant/features don't apply.\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n {children}\n </span>\n );\n }\n\n // `name` is guaranteed once `children` is absent UNLESS the caller is on the deprecated\n // `icon` path, which `IconDeprecatedProps` declares with `name?: undefined`.\n // (`IconChildrenProps['children']` excludes `undefined`, so `children={undefined}` can't\n // slip past into this branch.) The cast covers that third member, and the `!name && icon`\n // fallback below is what fills it in.\n let finalName = name as string;\n if (!name && icon) {\n // If icon prop is provided instead of name, try to parse it\n // e.g., \"mdi mdi-rocket-launch\" -> \"rocket-launch\"\n if (typeof icon === 'string') {\n const parts = icon.split(' ');\n const lastPart = parts[parts.length - 1];\n if (lastPart.startsWith('mdi-')) {\n finalName = lastPart.substring(4); // Remove \"mdi-\" prefix\n } else if (lastPart.startsWith('fa-')) {\n finalName = lastPart.substring(3); // Remove \"fa-\" prefix\n } else {\n finalName = lastPart;\n }\n }\n }\n\n const finalLibrary = library || defaultLibrary || 'fa';\n\n // Normalize a redundant leading library prefix (e.g. \"fa-check\" -> \"check\" when the\n // library is 'fa'), so `name` behaves identically with or without the prefix.\n finalName = stripRedundantLibraryPrefix(finalName, finalLibrary);\n\n if (!finalName) {\n // No glyph to name. Reachable through the public type only as `icon=\"\"`, and otherwise\n // by a plain-JS caller (or one that casts). Building a class off an absent name produced\n // a bogus `fa-undefined` glyph; render the bare container instead, matching the\n // `children` branch.\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n />\n );\n }\n\n // Backward compatibility: if libraryFeatures is provided, parse it for variant and features\n let finalVariant = variant;\n let finalFeatures = features;\n\n if (libraryFeatures && !variant && !features) {\n const legacyFeatures = Array.isArray(libraryFeatures)\n ? libraryFeatures\n : [libraryFeatures];\n\n // For Font Awesome, extract style from features\n if (finalLibrary === 'fa') {\n const faStyle = legacyFeatures.find(f =>\n [\n 'fas',\n 'far',\n 'fab',\n 'fal',\n 'fad',\n 'fat',\n 'solid',\n 'regular',\n 'brands',\n 'light',\n 'duotone',\n 'thin',\n ].includes(f)\n );\n if (faStyle) {\n finalVariant = faStyle;\n finalFeatures = legacyFeatures.filter(f => f !== faStyle);\n } else {\n finalFeatures = legacyFeatures;\n }\n }\n // For Material Icons/Symbols, extract style variant\n else if (\n finalLibrary === 'material-icons' ||\n finalLibrary === 'material-symbols'\n ) {\n const styleVariants =\n finalLibrary === 'material-icons'\n ? ['filled', 'outlined', 'round', 'sharp']\n : ['outlined', 'rounded', 'sharp'];\n\n const styleVariant = legacyFeatures.find(f => styleVariants.includes(f));\n if (styleVariant) {\n finalVariant = styleVariant;\n finalFeatures = legacyFeatures.filter(f => f !== styleVariant);\n } else {\n finalFeatures = legacyFeatures;\n }\n }\n // For others, all features go to finalFeatures\n else {\n finalFeatures = legacyFeatures;\n }\n }\n\n // Handle web components vs CSS-based icons\n if (finalLibrary === 'ion') {\n // For Ionicons, handle variant in the name\n let ionName = finalName;\n if (finalVariant === 'outline') {\n ionName = `${finalName}-outline`;\n } else if (finalVariant === 'sharp') {\n ionName = `${finalName}-sharp`;\n }\n\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n <ion-icon name={ionName} />\n </span>\n );\n }\n\n // Legacy CSS-based icons\n const iClasses = getIconClasses(\n finalLibrary,\n finalName,\n finalVariant,\n finalFeatures\n );\n\n // Material Icons and Material Symbols use text content, not CSS classes for the icon name\n if (\n finalLibrary === 'material-icons' ||\n finalLibrary === 'material-symbols'\n ) {\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n <i className={iClasses}>{finalName}</i>\n </span>\n );\n }\n\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n <i className={iClasses} />\n </span>\n );\n};\n"],"names":["_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Icon.js","sources":["../../../src/elements/Icon.tsx"],"sourcesContent":["import React from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useIconLibrary } from '../helpers/Config';\n\n// TypeScript declaration for Ionicons web component\ninterface IonIconProps extends React.HTMLAttributes<HTMLElement> {\n name?: string;\n src?: string;\n icon?: unknown;\n size?: string;\n lazy?: boolean;\n sanitize?: boolean;\n color?: string;\n flipRtl?: boolean;\n ariaLabel?: string;\n ariaHidden?: string;\n}\n\n// React 19 moved the global `JSX` namespace under the `react` module, so the\n// `<ion-icon>` web-component augmentation must target `React.JSX` to take effect.\ndeclare module 'react' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'ion-icon': IonIconProps;\n }\n }\n}\n\ntype IconLibrary = 'fa' | 'mdi' | 'ion' | 'material-icons' | 'material-symbols'; // 'fa' = Font Awesome, 'mdi' = Material Design Icons, 'ion' = Ionicons Web Components, 'material-icons' = Google Material Icons, 'material-symbols' = Google Material Symbols\n\n/**\n * Shared props for the Icon component, independent of whether a class-based `name` or a\n * custom `children` node supplies the glyph.\n */\ninterface IconBaseProps\n extends React.HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {\n /** Additional CSS classes to apply. */\n className?: string;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /**\n * Text color alias: renders `has-text-<color>`, exactly like `textColor`.\n * Not a filled variant (no `.icon.is-<color>` CSS exists). Prefer\n * `textColor`, which takes precedence when both are set; use `bgColor` for\n * a colored surface.\n */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /**\n * Legacy icon class string (e.g. `'fas fa-star'`). Only its last segment is read, as the\n * glyph name; the library still comes from `library` or `ConfigProvider`, so\n * `'mdi mdi-rocket'` renders an `fa` class unless the effective library is already `mdi` —\n * set it here or on the provider.\n *\n * @deprecated Use `name` instead.\n */\n icon?: string;\n /**\n * The icon library to use ('fa' = Font Awesome, 'mdi' = Material Design Icons, 'ion' = Ionicons Web Components, 'material-icons' = Google Material Icons, 'material-symbols' = Google Material Symbols). Defaults to the value set in ConfigProvider or 'fa' if not configured. Ignored when `children` supplies the glyph instead of `name`.\n * @defaultValue 'fa'\n */\n library?: IconLibrary; // defaults to ConfigProvider iconLibrary or 'fa'\n /** Icon style variant (e.g. `'solid'`, `'outlined'`, `'rounded'`). Ignored when `children` supplies the glyph instead of `name`. */\n variant?: string; // e.g., 'solid', 'outlined', 'rounded', 'sharp'\n /** Additional modifiers (e.g. `'fa-lg'`, `'fa-spin'`, `'is-size-1'`). Ignored when `children` supplies the glyph instead of `name`. */\n features?: string | string[]; // e.g., 'fa-lg', 'fa-spin', 'is-size-1'\n /**\n * Additional modifiers in the older combined form, parsed into `variant` and `features`.\n *\n * @deprecated Use `variant` and `features` instead.\n */\n libraryFeatures?: string | string[];\n /** Size modifier for the icon container. */\n size?: 'small' | 'medium' | 'large';\n /**\n * Accessible name for an icon that means something on its own. Set it and the container\n * span gets `role=\"img\"` with this `aria-label`, so the name is exactly this text: a\n * Material ligature such as `rocket_launch` inside stays out of it. Leave it unset (or\n * empty) and the icon is decorative: the span renders `aria-hidden=\"true\"` and no name,\n * so it adds nothing to the accessible name of a `Button` or text around it. For an\n * icon-only control, put the name on the control (e.g. `Button`'s `aria-label`) and leave\n * this unset. A non-empty native `aria-label` or `aria-labelledby` passed instead also\n * counts as a name, and an explicit `role` or `aria-hidden` wins over both defaults. An\n * unnamed icon given a `role` or a `tabIndex` of its own isn't hidden, since hiding\n * something focusable is a failure of its own.\n */\n ariaLabel?: string;\n /** Inline style object. */\n style?: React.CSSProperties;\n /** Override the default `'icon'` container class (e.g., `'panel-icon'`). */\n containerClassName?: string; // Override the default 'icon' container class (e.g., 'panel-icon')\n}\n\n/**\n * Props for `Icon` rendering a class-based glyph from an icon library.\n */\nexport interface IconNameProps extends IconBaseProps {\n /** The icon name, with or without its library prefix (e.g. `'star'` or `'fa-star'`). Mutually exclusive with `children`. */\n name: string; // e.g., 'star', 'account', 'home-outline'\n /** A custom node (an inline SVG, a `react-icons` component, …) rendered inside the icon container in place of a class-based glyph. Mutually exclusive with `name`. */\n children?: never;\n}\n\n/**\n * Props for `Icon` rendering a custom node (an inline SVG, a `react-icons` component, a Font\n * Awesome React `<FontAwesomeIcon>`, …) in place of a class-based glyph.\n */\nexport interface IconChildrenProps extends IconBaseProps {\n /** The icon name, with or without its library prefix (e.g. `'star'` or `'fa-star'`). Mutually exclusive with `children`. */\n name?: undefined;\n /** A custom node (an inline SVG, a `react-icons` component, …) rendered inside the icon container in place of a class-based glyph. Mutually exclusive with `name`. */\n // `undefined` is excluded deliberately: the renderer discriminates on `children !== undefined`,\n // so `children={undefined}` would satisfy the type but fall through to the `name` path with no\n // name and render an `fa-undefined` glyph. Excluding it keeps the advertised\n // exactly-one-of-`name`-or-`children` constraint matching what actually renders.\n children: Exclude<React.ReactNode, undefined>;\n}\n\n/**\n * Props for `Icon` naming its glyph through the deprecated `icon` prop instead of `name`.\n *\n * The runtime has always accepted `icon` on its own — it strips a leading library prefix off\n * the class string and falls through to the `name` path — but the type offered no member without\n * a `name` or `children`, so every caller still on the deprecated prop got an error the\n * package could not see — until #663 nothing here type-checked a test or a story, so the\n * three `Icon.test.tsx` cases exercising this path proved the runtime and said nothing about\n * the type. Declaring the path is the honest resolution: it is deprecated, not removed, and a\n * deprecation that does not type-check is a removal announced only to whoever tries it.\n *\n * @deprecated Pass `name` instead. This member goes when `icon` does.\n */\nexport interface IconDeprecatedProps extends IconBaseProps {\n /**\n * Legacy icon class string (e.g. `'fas fa-star'`). Only its last segment is read, as the\n * glyph name; the library still comes from `library` or `ConfigProvider`, so\n * `'mdi mdi-rocket'` renders an `fa` class unless the effective library is already `mdi` —\n * set it here or on the provider.\n *\n * @deprecated Use `name` instead.\n */\n // Required here, optional on `IconBaseProps`: the other two members allow it\n // alongside `name`, and this one is the member where it IS the glyph. Same\n // prose in both places on purpose — the API-docs extractor reads whichever it\n // reaches first, and the two must not drift apart.\n icon: string;\n /** The icon name. Absent on this path — `icon` supplies the glyph. */\n name?: undefined;\n /** A custom node. Mutually exclusive with `icon`. */\n children?: never;\n}\n\n/**\n * Props for the Icon component — a discriminated union of a class-based `name`, a custom\n * `children` node, and the deprecated `icon` class string.\n */\nexport type IconProps = IconNameProps | IconChildrenProps | IconDeprecatedProps;\n\n/**\n * Strips a redundant leading library prefix from an icon name (e.g. `fa-check` -> `check`\n * for the `fa` library, `mdi-account` -> `account` for `mdi`), so `name` behaves the same\n * whether or not the caller includes the prefix. Font Awesome and Material Design Icons docs\n * conventionally refer to icons with their class prefix (`fa-check`), which otherwise gets\n * doubled by `getIconClasses` (producing `fa-fa-check`, matching no glyph).\n *\n * @param {string} name - The icon name, possibly including a redundant library prefix.\n * @param {IconLibrary} library - The icon library the name will be rendered with.\n * @returns {string} The icon name with any redundant leading library prefix removed.\n */\nfunction stripRedundantLibraryPrefix(\n name: string,\n library: IconLibrary\n): string {\n if (!name) {\n return name;\n }\n if (library === 'fa' && name.startsWith('fa-')) {\n return name.substring(3);\n }\n if (library === 'mdi' && name.startsWith('mdi-')) {\n return name.substring(4);\n }\n return name;\n}\n\n/**\n * Gets the correct classes for the icon element based on the library and features.\n *\n * @param {IconLibrary} library - The icon library.\n * @param {string} name - The icon name.\n * @param {string} [variant] - Icon style variant (e.g., 'solid', 'outlined', 'rounded').\n * @param {string | string[]} [features] - Additional library-specific modifiers.\n * @returns {string} The combined class string for the icon.\n */\nfunction getIconClasses(\n library: IconLibrary,\n name: string,\n variant?: string,\n features?: string | string[]\n): string {\n let baseClass: string;\n let iconClass: string;\n const featureList = Array.isArray(features)\n ? features\n : features\n ? [features]\n : [];\n\n switch (library) {\n case 'fa': {\n // Font Awesome: use variant as style ('solid' -> 'fas', 'regular' -> 'far', etc.)\n const styleMap: Record<string, string> = {\n solid: 'fas',\n regular: 'far',\n brands: 'fab',\n light: 'fal',\n duotone: 'fad',\n thin: 'fat',\n };\n const faStyle = variant ? styleMap[variant] || variant : 'fas';\n baseClass = faStyle;\n iconClass = `fa-${name}`;\n return [baseClass, iconClass, ...featureList].join(' ');\n }\n case 'mdi':\n // Material Design Icons: no variants, just features\n baseClass = 'mdi';\n iconClass = `mdi-${name}`;\n return [baseClass, iconClass, ...featureList].join(' ');\n case 'material-icons': {\n // Google Material Icons: map variants to full class names\n const styleVariants: Record<string, string> = {\n filled: 'material-icons',\n outlined: 'material-icons-outlined',\n round: 'material-icons-round',\n sharp: 'material-icons-sharp',\n };\n baseClass = variant\n ? styleVariants[variant] || `material-icons-${variant}`\n : 'material-icons';\n return [baseClass, ...featureList].join(' ');\n }\n case 'material-symbols': {\n // Google Material Symbols: map variants to full class names\n const styleVariants: Record<string, string> = {\n outlined: 'material-symbols-outlined',\n rounded: 'material-symbols-rounded',\n sharp: 'material-symbols-sharp',\n };\n baseClass = variant\n ? styleVariants[variant] || `material-symbols-${variant}`\n : 'material-symbols-outlined';\n return [baseClass, ...featureList].join(' ');\n }\n default:\n // fallback: just icon name and features\n return [name, ...featureList].join(' ');\n }\n}\n\n/**\n * The `Icon` component is a Bulma-styled wrapper for displaying icons from various libraries (Font Awesome, Material Design Icons, Ionicons, Google Material Icons, Material Symbols, etc.).\n *\n * @function\n * @param {IconProps} props - Props for the Icon component.\n * @returns {JSX.Element} The rendered icon element.\n * @see {@link https://bulma.io/documentation/elements/icon/ | Bulma Icon documentation}\n */\nexport const Icon: React.FC<IconProps> = ({\n className,\n textColor,\n bgColor,\n name,\n library,\n variant,\n features,\n libraryFeatures, // Deprecated but maintained for backward compatibility\n size,\n ariaLabel,\n style,\n icon, // Capture and exclude the deprecated 'icon' prop from DOM\n color,\n containerClassName,\n children,\n ...restProps\n}) => {\n // Get the default icon library from context, fallback to 'fa' if not set\n const defaultLibrary = useIconLibrary();\n\n /**\n * Generates Bulma helper classes and separates out remaining props.\n * Note: variant, features, and libraryFeatures are excluded from props spread\n */\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor ?? color,\n backgroundColor: bgColor,\n ...restProps,\n });\n\n // Hoisted unconditionally to respect rules-of-hooks; the branches below\n // pick which result to consume.\n const defaultIconClasses = usePrefixedClassNames('icon', {\n [`is-${size}`]: size,\n });\n const sizeModifierClass = usePrefixedClassNames(\n size ? `is-${size}` : undefined\n );\n\n const bulmaClasses = containerClassName\n ? containerClassName\n : defaultIconClasses;\n\n const iconContainerClasses = classNames(\n bulmaClasses,\n containerClassName && size ? sizeModifierClass : undefined,\n bulmaHelperClasses,\n className\n );\n\n // A named icon is an image, whose name is its label alone, so a ligature inside\n // never leaks into it. An unnamed one is decorative and hidden outright. Spread\n // before `rest`, so a caller's own `role` or `aria-hidden` still wins.\n const named =\n !!ariaLabel || !!restProps['aria-label'] || !!restProps['aria-labelledby'];\n // A caller's own `role` or `tabIndex` on an unnamed icon means it isn't decoration (an\n // icon made a button, say), so it isn't hidden either: `aria-hidden` on something\n // focusable is a failure of its own.\n const a11yProps = named\n ? { role: 'img', 'aria-label': ariaLabel || undefined }\n : restProps.role !== undefined || restProps.tabIndex !== undefined\n ? {}\n : { 'aria-hidden': true };\n\n if (children !== undefined) {\n // `IconChildrenProps`: render the caller's node (an inline SVG, a `react-icons`\n // component, …) in place of a class-based glyph. Library/variant/features don't apply.\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n {children}\n </span>\n );\n }\n\n // `name` is guaranteed once `children` is absent UNLESS the caller is on the deprecated\n // `icon` path, which `IconDeprecatedProps` declares with `name?: undefined`.\n // (`IconChildrenProps['children']` excludes `undefined`, so `children={undefined}` can't\n // slip past into this branch.) The cast covers that third member, and the `!name && icon`\n // fallback below is what fills it in.\n let finalName = name as string;\n if (!name && icon) {\n // If icon prop is provided instead of name, try to parse it\n // e.g., \"mdi mdi-rocket-launch\" -> \"rocket-launch\"\n if (typeof icon === 'string') {\n const parts = icon.split(' ');\n const lastPart = parts[parts.length - 1];\n if (lastPart.startsWith('mdi-')) {\n finalName = lastPart.substring(4); // Remove \"mdi-\" prefix\n } else if (lastPart.startsWith('fa-')) {\n finalName = lastPart.substring(3); // Remove \"fa-\" prefix\n } else {\n finalName = lastPart;\n }\n }\n }\n\n const finalLibrary = library || defaultLibrary || 'fa';\n\n // Normalize a redundant leading library prefix (e.g. \"fa-check\" -> \"check\" when the\n // library is 'fa'), so `name` behaves identically with or without the prefix.\n finalName = stripRedundantLibraryPrefix(finalName, finalLibrary);\n\n if (!finalName) {\n // No glyph to name. Reachable through the public type only as `icon=\"\"`, and otherwise\n // by a plain-JS caller (or one that casts). Building a class off an absent name produced\n // a bogus `fa-undefined` glyph; render the bare container instead, matching the\n // `children` branch.\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n />\n );\n }\n\n // Backward compatibility: if libraryFeatures is provided, parse it for variant and features\n let finalVariant = variant;\n let finalFeatures = features;\n\n if (libraryFeatures && !variant && !features) {\n const legacyFeatures = Array.isArray(libraryFeatures)\n ? libraryFeatures\n : [libraryFeatures];\n\n // For Font Awesome, extract style from features\n if (finalLibrary === 'fa') {\n const faStyle = legacyFeatures.find(f =>\n [\n 'fas',\n 'far',\n 'fab',\n 'fal',\n 'fad',\n 'fat',\n 'solid',\n 'regular',\n 'brands',\n 'light',\n 'duotone',\n 'thin',\n ].includes(f)\n );\n if (faStyle) {\n finalVariant = faStyle;\n finalFeatures = legacyFeatures.filter(f => f !== faStyle);\n } else {\n finalFeatures = legacyFeatures;\n }\n }\n // For Material Icons/Symbols, extract style variant\n else if (\n finalLibrary === 'material-icons' ||\n finalLibrary === 'material-symbols'\n ) {\n const styleVariants =\n finalLibrary === 'material-icons'\n ? ['filled', 'outlined', 'round', 'sharp']\n : ['outlined', 'rounded', 'sharp'];\n\n const styleVariant = legacyFeatures.find(f => styleVariants.includes(f));\n if (styleVariant) {\n finalVariant = styleVariant;\n finalFeatures = legacyFeatures.filter(f => f !== styleVariant);\n } else {\n finalFeatures = legacyFeatures;\n }\n }\n // For others, all features go to finalFeatures\n else {\n finalFeatures = legacyFeatures;\n }\n }\n\n // Handle web components vs CSS-based icons\n if (finalLibrary === 'ion') {\n // For Ionicons, handle variant in the name\n let ionName = finalName;\n if (finalVariant === 'outline') {\n ionName = `${finalName}-outline`;\n } else if (finalVariant === 'sharp') {\n ionName = `${finalName}-sharp`;\n }\n\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n <ion-icon name={ionName} />\n </span>\n );\n }\n\n // Legacy CSS-based icons\n const iClasses = getIconClasses(\n finalLibrary,\n finalName,\n finalVariant,\n finalFeatures\n );\n\n // Material Icons and Material Symbols use text content, not CSS classes for the icon name\n if (\n finalLibrary === 'material-icons' ||\n finalLibrary === 'material-symbols'\n ) {\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n <i className={iClasses}>{finalName}</i>\n </span>\n );\n }\n\n return (\n <span\n className={iconContainerClasses}\n {...a11yProps}\n style={style}\n {...rest}\n >\n <i className={iClasses} />\n </span>\n );\n};\n"],"names":["_jsx"],"mappings":";;;;;;AAoKA;;;;;;;;;;AAUG;AACH,SAAS,2BAA2B,CAClC,IAAY,EACZ,OAAoB,EAAA;IAEpB,IAAI,CAAC,IAAI,EAAE;AACT,QAAA,OAAO,IAAI;IACb;IACA,IAAI,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;AAC9C,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1B;IACA,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE;AAChD,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1B;AACA,IAAA,OAAO,IAAI;AACb;AAEA;;;;;;;;AAQG;AACH,SAAS,cAAc,CACrB,OAAoB,EACpB,IAAY,EACZ,OAAgB,EAChB,QAA4B,EAAA;AAE5B,IAAA,IAAI,SAAiB;AACrB,IAAA,IAAI,SAAiB;AACrB,IAAA,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ;AACxC,UAAE;AACF,UAAE;cACE,CAAC,QAAQ;cACT,EAAE;IAER,QAAQ,OAAO;QACb,KAAK,IAAI,EAAE;;AAET,YAAA,MAAM,QAAQ,GAA2B;AACvC,gBAAA,KAAK,EAAE,KAAK;AACZ,gBAAA,OAAO,EAAE,KAAK;AACd,gBAAA,MAAM,EAAE,KAAK;AACb,gBAAA,KAAK,EAAE,KAAK;AACZ,gBAAA,OAAO,EAAE,KAAK;AACd,gBAAA,IAAI,EAAE,KAAK;aACZ;AACD,YAAA,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,KAAK;YAC9D,SAAS,GAAG,OAAO;AACnB,YAAA,SAAS,GAAG,CAAA,GAAA,EAAM,IAAI,CAAA,CAAE;AACxB,YAAA,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACzD;AACA,QAAA,KAAK,KAAK;;YAER,SAAS,GAAG,KAAK;AACjB,YAAA,SAAS,GAAG,CAAA,IAAA,EAAO,IAAI,CAAA,CAAE;AACzB,YAAA,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACzD,KAAK,gBAAgB,EAAE;;AAErB,YAAA,MAAM,aAAa,GAA2B;AAC5C,gBAAA,MAAM,EAAE,gBAAgB;AACxB,gBAAA,QAAQ,EAAE,yBAAyB;AACnC,gBAAA,KAAK,EAAE,sBAAsB;AAC7B,gBAAA,KAAK,EAAE,sBAAsB;aAC9B;AACD,YAAA,SAAS,GAAG;kBACR,aAAa,CAAC,OAAO,CAAC,IAAI,CAAA,eAAA,EAAkB,OAAO,CAAA;kBACnD,gBAAgB;YACpB,OAAO,CAAC,SAAS,EAAE,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC9C;QACA,KAAK,kBAAkB,EAAE;;AAEvB,YAAA,MAAM,aAAa,GAA2B;AAC5C,gBAAA,QAAQ,EAAE,2BAA2B;AACrC,gBAAA,OAAO,EAAE,0BAA0B;AACnC,gBAAA,KAAK,EAAE,wBAAwB;aAChC;AACD,YAAA,SAAS,GAAG;kBACR,aAAa,CAAC,OAAO,CAAC,IAAI,CAAA,iBAAA,EAAoB,OAAO,CAAA;kBACrD,2BAA2B;YAC/B,OAAO,CAAC,SAAS,EAAE,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC9C;AACA,QAAA;;YAEE,OAAO,CAAC,IAAI,EAAE,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;;AAE7C;AAEA;;;;;;;AAOG;MACU,IAAI,GAAwB,CAAC,EACxC,SAAS,EACT,SAAS,EACT,OAAO,EACP,IAAI,EACJ,OAAO,EACP,OAAO,EACP,QAAQ,EACR,eAAe;AACf,IAAI,EACJ,SAAS,EACT,KAAK,EACL,IAAI;AACJ,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,GAAG,SAAS,EACb,KAAI;;AAEH,IAAA,MAAM,cAAc,GAAG,cAAc,EAAE;AAEvC;;;AAGG;AACH,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;QACnD,KAAK,EAAE,SAAS,IAAI,KAAK;AACzB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,SAAS;AACb,KAAA,CAAC;;;AAIF,IAAA,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,MAAM,EAAE;AACvD,QAAA,CAAC,CAAA,GAAA,EAAM,IAAI,CAAA,CAAE,GAAG,IAAI;AACrB,KAAA,CAAC;AACF,IAAA,MAAM,iBAAiB,GAAG,qBAAqB,CAC7C,IAAI,GAAG,CAAA,GAAA,EAAM,IAAI,EAAE,GAAG,SAAS,CAChC;IAED,MAAM,YAAY,GAAG;AACnB,UAAE;UACA,kBAAkB;IAEtB,MAAM,oBAAoB,GAAG,UAAU,CACrC,YAAY,EACZ,kBAAkB,IAAI,IAAI,GAAG,iBAAiB,GAAG,SAAS,EAC1D,kBAAkB,EAClB,SAAS,CACV;;;;AAKD,IAAA,MAAM,KAAK,GACT,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC;;;;IAI5E,MAAM,SAAS,GAAG;UACd,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,IAAI,SAAS;UACnD,SAAS,CAAC,IAAI,KAAK,SAAS,IAAI,SAAS,CAAC,QAAQ,KAAK;AACvD,cAAE;AACF,cAAE,EAAE,aAAa,EAAE,IAAI,EAAE;AAE7B,IAAA,IAAI,QAAQ,KAAK,SAAS,EAAE;;;AAG1B,QAAA,QACEA,GAAA,CAAA,MAAA,EAAA,EACE,SAAS,EAAE,oBAAoB,KAC3B,SAAS,EACb,KAAK,EAAE,KAAK,EAAA,GACR,IAAI,YAEP,QAAQ,EAAA,CACJ;IAEX;;;;;;IAOA,IAAI,SAAS,GAAG,IAAc;AAC9B,IAAA,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE;;;AAGjB,QAAA,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;YAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,YAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE;gBAC/B,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACpC;AAAO,iBAAA,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE;gBACrC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACpC;iBAAO;gBACL,SAAS,GAAG,QAAQ;YACtB;QACF;IACF;AAEA,IAAA,MAAM,YAAY,GAAG,OAAO,IAAI,cAAc,IAAI,IAAI;;;AAItD,IAAA,SAAS,GAAG,2BAA2B,CAAC,SAAS,EAAE,YAAY,CAAC;IAEhE,IAAI,CAAC,SAAS,EAAE;;;;;AAKd,QAAA,QACEA,GAAA,CAAA,MAAA,EAAA,EACE,SAAS,EAAE,oBAAoB,EAAA,GAC3B,SAAS,EACb,KAAK,EAAE,KAAK,EAAA,GACR,IAAI,EAAA,CACR;IAEN;;IAGA,IAAI,YAAY,GAAG,OAAO;IAC1B,IAAI,aAAa,GAAG,QAAQ;IAE5B,IAAI,eAAe,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE;AAC5C,QAAA,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe;AAClD,cAAE;AACF,cAAE,CAAC,eAAe,CAAC;;AAGrB,QAAA,IAAI,YAAY,KAAK,IAAI,EAAE;YACzB,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,IACnC;gBACE,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,OAAO;gBACP,SAAS;gBACT,QAAQ;gBACR,OAAO;gBACP,SAAS;gBACT,MAAM;AACP,aAAA,CAAC,QAAQ,CAAC,CAAC,CAAC,CACd;YACD,IAAI,OAAO,EAAE;gBACX,YAAY,GAAG,OAAO;AACtB,gBAAA,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC;YAC3D;iBAAO;gBACL,aAAa,GAAG,cAAc;YAChC;QACF;;aAEK,IACH,YAAY,KAAK,gBAAgB;YACjC,YAAY,KAAK,kBAAkB,EACnC;AACA,YAAA,MAAM,aAAa,GACjB,YAAY,KAAK;kBACb,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO;kBACvC,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC;AAEtC,YAAA,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YACxE,IAAI,YAAY,EAAE;gBAChB,YAAY,GAAG,YAAY;AAC3B,gBAAA,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,YAAY,CAAC;YAChE;iBAAO;gBACL,aAAa,GAAG,cAAc;YAChC;QACF;;aAEK;YACH,aAAa,GAAG,cAAc;QAChC;IACF;;AAGA,IAAA,IAAI,YAAY,KAAK,KAAK,EAAE;;QAE1B,IAAI,OAAO,GAAG,SAAS;AACvB,QAAA,IAAI,YAAY,KAAK,SAAS,EAAE;AAC9B,YAAA,OAAO,GAAG,CAAA,EAAG,SAAS,CAAA,QAAA,CAAU;QAClC;AAAO,aAAA,IAAI,YAAY,KAAK,OAAO,EAAE;AACnC,YAAA,OAAO,GAAG,CAAA,EAAG,SAAS,CAAA,MAAA,CAAQ;QAChC;QAEA,QACEA,cACE,SAAS,EAAE,oBAAoB,EAAA,GAC3B,SAAS,EACb,KAAK,EAAE,KAAK,EAAA,GACR,IAAI,YAERA,GAAA,CAAA,UAAA,EAAA,EAAU,IAAI,EAAE,OAAO,EAAA,CAAI,EAAA,CACtB;IAEX;;AAGA,IAAA,MAAM,QAAQ,GAAG,cAAc,CAC7B,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,aAAa,CACd;;IAGD,IACE,YAAY,KAAK,gBAAgB;QACjC,YAAY,KAAK,kBAAkB,EACnC;QACA,QACEA,cACE,SAAS,EAAE,oBAAoB,EAAA,GAC3B,SAAS,EACb,KAAK,EAAE,KAAK,KACR,IAAI,EAAA,QAAA,EAERA,GAAA,CAAA,GAAA,EAAA,EAAG,SAAS,EAAE,QAAQ,YAAG,SAAS,EAAA,CAAK,EAAA,CAClC;IAEX;IAEA,QACEA,cACE,SAAS,EAAE,oBAAoB,EAAA,GAC3B,SAAS,EACb,KAAK,EAAE,KAAK,EAAA,GACR,IAAI,YAERA,GAAA,CAAA,GAAA,EAAA,EAAG,SAAS,EAAE,QAAQ,EAAA,CAAI,EAAA,CACrB;AAEX;;"}
|
|
@@ -9,7 +9,8 @@ import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
|
9
9
|
*
|
|
10
10
|
* The ring is `1em` square, so `textSize` scales it. It is exposed as an
|
|
11
11
|
* indeterminate `progressbar` named by `ariaLabel`, and it stops spinning
|
|
12
|
-
* under `prefers-reduced-motion: reduce` while staying drawn.
|
|
12
|
+
* under `prefers-reduced-motion: reduce` while staying drawn. In forced
|
|
13
|
+
* colors its two open sides take the page's color, so it stays open.
|
|
13
14
|
*
|
|
14
15
|
* @function
|
|
15
16
|
* @param {LoaderProps} props - Props for the Loader component.
|