@allxsmith/bestax-bulma 5.62.0 → 5.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/esm/components/EmptyState.js +2 -6
  4. package/dist/esm/components/EmptyState.js.map +1 -1
  5. package/dist/esm/components/ScrollArea.js +7 -7
  6. package/dist/esm/components/ScrollArea.js.map +1 -1
  7. package/dist/esm/components/TableOfContents.js +85 -0
  8. package/dist/esm/components/TableOfContents.js.map +1 -0
  9. package/dist/esm/components/Timeline.js +5 -9
  10. package/dist/esm/components/Timeline.js.map +1 -1
  11. package/dist/esm/components/TreeView.js +3 -7
  12. package/dist/esm/components/TreeView.js.map +1 -1
  13. package/dist/esm/elements/Buttons.js +2 -0
  14. package/dist/esm/elements/Buttons.js.map +1 -1
  15. package/dist/esm/elements/CopyButton.js +132 -0
  16. package/dist/esm/elements/CopyButton.js.map +1 -0
  17. package/dist/esm/elements/DescriptionList.js +1 -5
  18. package/dist/esm/elements/DescriptionList.js.map +1 -1
  19. package/dist/esm/elements/Stat.js +196 -0
  20. package/dist/esm/elements/Stat.js.map +1 -0
  21. package/dist/esm/elements/Stats.js +48 -0
  22. package/dist/esm/elements/Stats.js.map +1 -0
  23. package/dist/esm/elements/statContext.js +10 -0
  24. package/dist/esm/elements/statContext.js.map +1 -0
  25. package/dist/esm/form/Combobox.js +2 -1
  26. package/dist/esm/form/Combobox.js.map +1 -1
  27. package/dist/esm/form/PasswordInput.js +105 -0
  28. package/dist/esm/form/PasswordInput.js.map +1 -0
  29. package/dist/esm/form/PasswordInputBase.js +177 -0
  30. package/dist/esm/form/PasswordInputBase.js.map +1 -0
  31. package/dist/esm/helpers/checkIcon.js +20 -0
  32. package/dist/esm/helpers/checkIcon.js.map +1 -0
  33. package/dist/esm/helpers/isGiven.js +13 -0
  34. package/dist/esm/helpers/isGiven.js.map +1 -0
  35. package/dist/esm/helpers/statusRegion.js +3 -3
  36. package/dist/esm/helpers/useClipboard.js +176 -0
  37. package/dist/esm/helpers/useClipboard.js.map +1 -0
  38. package/dist/esm/helpers/useScrollSpy.js +286 -0
  39. package/dist/esm/helpers/useScrollSpy.js.map +1 -0
  40. package/dist/extras.css +1 -1
  41. package/dist/extras.css.map +1 -1
  42. package/dist/index.cjs +2671 -1544
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.esm.js +8 -0
  45. package/dist/index.esm.js.map +1 -1
  46. package/dist/types/components/TableOfContents.d.ts +87 -0
  47. package/dist/types/elements/Buttons.d.ts +1 -0
  48. package/dist/types/elements/CopyButton.d.ts +105 -0
  49. package/dist/types/elements/Stat.d.ts +242 -0
  50. package/dist/types/elements/Stats.d.ts +40 -0
  51. package/dist/types/elements/statContext.d.ts +18 -0
  52. package/dist/types/form/PasswordInput.d.ts +98 -0
  53. package/dist/types/form/PasswordInputBase.d.ts +91 -0
  54. package/dist/types/helpers/checkIcon.d.ts +10 -0
  55. package/dist/types/helpers/isGiven.d.ts +7 -0
  56. package/dist/types/helpers/useClipboard.d.ts +65 -0
  57. package/dist/types/helpers/useScrollSpy.d.ts +61 -0
  58. package/dist/types/index.d.ts +10 -0
  59. package/dist/types-cjs/components/TableOfContents.d.ts +87 -0
  60. package/dist/types-cjs/elements/Buttons.d.ts +1 -0
  61. package/dist/types-cjs/elements/CopyButton.d.ts +105 -0
  62. package/dist/types-cjs/elements/Stat.d.ts +242 -0
  63. package/dist/types-cjs/elements/Stats.d.ts +40 -0
  64. package/dist/types-cjs/elements/statContext.d.ts +18 -0
  65. package/dist/types-cjs/form/PasswordInput.d.ts +98 -0
  66. package/dist/types-cjs/form/PasswordInputBase.d.ts +91 -0
  67. package/dist/types-cjs/helpers/checkIcon.d.ts +10 -0
  68. package/dist/types-cjs/helpers/isGiven.d.ts +7 -0
  69. package/dist/types-cjs/helpers/useClipboard.d.ts +65 -0
  70. package/dist/types-cjs/helpers/useScrollSpy.d.ts +61 -0
  71. package/dist/types-cjs/index.d.ts +10 -0
  72. package/dist/versions/bestax-no-dark-mode.css +1 -1
  73. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  74. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  75. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  76. package/dist/versions/bestax-no-helpers.css +1 -1
  77. package/dist/versions/bestax-no-helpers.css.map +1 -1
  78. package/dist/versions/bestax-prefixed.css +1 -1
  79. package/dist/versions/bestax-prefixed.css.map +1 -1
  80. package/package.json +1 -1
  81. package/src/scss/CLAUDE.md +3 -0
  82. package/src/scss/_input-trigger.scss +76 -0
  83. package/src/scss/components/_index.scss +1 -0
  84. package/src/scss/components/_tableofcontents.scss +42 -0
  85. package/src/scss/elements/_index.scss +1 -0
  86. package/src/scss/elements/_stat.scss +153 -0
  87. package/src/scss/form/_index.scss +1 -0
  88. package/src/scss/form/_passwordinput.scss +112 -0
  89. package/src/scss/form/_picker-popover.scss +24 -63
@@ -9,6 +9,7 @@ import { Buttons } from '../elements/Buttons.js';
9
9
  import { Icon } from '../elements/Icon.js';
10
10
  import { Paragraph } from '../elements/Paragraph.js';
11
11
  import { Title } from '../elements/Title.js';
12
+ import { isGiven } from '../helpers/isGiven.js';
12
13
 
13
14
  const validEmptyStateColors = [
14
15
  'primary',
@@ -79,11 +80,6 @@ const EmptyStateActions = ({ className, ...props }) => {
79
80
  return jsx(Buttons, { className: classNames(actionsClass, className), ...props });
80
81
  };
81
82
  EmptyStateActions.displayName = 'EmptyState.Actions';
82
- /** Whether a shorthand prop was given something to render. */
83
- const isGiven$3 = (node) => node !== undefined &&
84
- node !== null &&
85
- typeof node !== 'boolean' &&
86
- node !== '';
87
83
  /**
88
84
  * The `EmptyState` component renders the block an app shows where content would be: an empty list, a search with no results, or a first-run screen.
89
85
  *
@@ -127,7 +123,7 @@ const EmptyStateRoot = forwardRef(({ className, icon, title, titleAs = 'h2', des
127
123
  });
128
124
  // The props' parts first, each only when it's given, then the children
129
125
  // exactly as they were written: nothing here looks at them.
130
- return (jsxs("div", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, children: [isGiven$3(icon) && (jsx(EmptyStateIndicator, { children: typeof icon === 'string' ? jsx(Icon, { name: icon }) : icon })), isGiven$3(title) && (jsx(EmptyStateTitle, { as: titleAs, children: title })), isGiven$3(description) && (jsx(EmptyStateDescription, { children: description })), children] }));
126
+ return (jsxs("div", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, children: [isGiven(icon) && (jsx(EmptyStateIndicator, { children: typeof icon === 'string' ? jsx(Icon, { name: icon }) : icon })), isGiven(title) && (jsx(EmptyStateTitle, { as: titleAs, children: title })), isGiven(description) && (jsx(EmptyStateDescription, { children: description })), children] }));
131
127
  });
132
128
  const EmptyState = withSubComponents(EmptyStateRoot, {
133
129
  Indicator: EmptyStateIndicator,
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.js","sources":["../../../src/components/EmptyState.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { Buttons, ButtonsProps } from '../elements/Buttons';\nimport { Icon } from '../elements/Icon';\nimport { Paragraph, ParagraphProps } from '../elements/Paragraph';\nimport { Title, TitleProps } from '../elements/Title';\n\nconst validEmptyStateColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n/** The Bulma colors the indicator takes. */\nexport type EmptyStateColor = (typeof validEmptyStateColors)[number];\n\nconst validEmptyStateHeadings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;\n/** A title's heading level. It's always a heading, so there's no `p`. */\nexport type EmptyStateHeading = (typeof validEmptyStateHeadings)[number];\n\nconst validEmptyStateSizes = ['small', 'medium', 'large'] as const;\n\n/**\n * Props for the EmptyState component. The helper props, the other attributes\n * and the `ref` land on the root `<div>`. The native `title` attribute isn't\n * one of them, since `title` here is the heading.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the root `<div>`.\n */\nexport interface EmptyStateProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'title'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * The picture in the indicator's circle. A string is an `Icon` name, drawn\n * from the icon library `ConfigProvider` sets. Anything else (an `<Icon>`\n * with its own `variant` or `library`, an SVG, an `<img>`) renders as it is.\n * It's decorative and hidden from assistive tech, so the title says what\n * it means.\n */\n icon?: React.ReactNode;\n /**\n * The heading, rendered as `EmptyState.Title`. It's always a real heading,\n * at the level `titleAs` sets.\n */\n title?: React.ReactNode;\n /**\n * The heading level for `title`: the level under the heading of the list\n * it stands in for, such as `h3` for a list under an `h2`. Only this sets\n * the level, and the size comes from `size`.\n */\n titleAs?: EmptyStateHeading;\n /** A sentence or two under the title, rendered as `EmptyState.Description`. */\n description?: React.ReactNode;\n /**\n * Rendered as written, after whatever `icon`, `title` and `description`\n * render. One button can go straight in, and it sits centered under the\n * text. Group two or more in `EmptyState.Actions`, or they stack. Without\n * the props, write the parts here in the order they're shown:\n * `EmptyState.Indicator`, `.Title`, `.Description`, then the actions.\n */\n children?: React.ReactNode;\n /**\n * The indicator's color. It colors the circle and its icon, not the text.\n * Without it the circle is neutral, since an empty list isn't a status.\n */\n color?: EmptyStateColor;\n /**\n * `light` is a soft tint of `color` behind the icon in the color. `filled`\n * is the color itself, with the icon in its invert.\n */\n variant?: 'light' | 'filled';\n /** Sizes the indicator, the title, the description and the spacing together. */\n size?: 'small' | 'normal' | 'medium' | 'large';\n /**\n * `center` stacks everything in a centered column. `left` puts the\n * indicator beside the text, at the start of the line, so on the right in\n * a right-to-left page, and keeps the block at least as tall as the circle\n * whatever block padding is in effect. Without an indicator, it only puts\n * the text at the start of the line. In `left`, the padding at the start\n * of the line is what makes room for the indicator, so pad it with `py`\n * rather than `p` or `px`.\n */\n align?: 'center' | 'left';\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** Props for EmptyState.Indicator, the circle the picture sits in. */\nexport interface EmptyStateIndicatorProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * The picture: an `Icon`, an SVG or an `<img>`. The indicator is hidden\n * from assistive tech, an `Icon` with an `ariaLabel` included, so anything\n * it means goes in the title or the description.\n */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * Props for EmptyState.Title. It renders `Title` with `as` and never `size`,\n * so the level is `as` alone and the size comes from the EmptyState.\n */\nexport interface EmptyStateTitleProps extends Omit<\n TitleProps,\n 'as' | 'size' | 'isSpaced'\n> {\n /**\n * The heading level: the level under the heading of the list the empty\n * state stands in for.\n */\n as?: EmptyStateHeading;\n}\n\n/** Props for EmptyState.Description, a `<p>` under the title. */\nexport type EmptyStateDescriptionProps = ParagraphProps;\n\n/**\n * Props for EmptyState.Actions, the row of buttons. It renders `Buttons`,\n * and the EmptyState aligns it, so `isCentered` and `isRight` are left out.\n */\nexport type EmptyStateActionsProps = Omit<\n ButtonsProps,\n 'isCentered' | 'isRight'\n>;\n\n/**\n * The circle the picture sits in, hidden from assistive tech whatever is in it.\n *\n * @function\n * @param {EmptyStateIndicatorProps} props - Props for EmptyState.Indicator.\n * @returns {JSX.Element} The rendered indicator.\n */\nexport const EmptyStateIndicator: React.FC<EmptyStateIndicatorProps> = ({\n className,\n textColor,\n bgColor,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const indicatorClass = usePrefixedClassNames('empty-state-indicator');\n\n // Hidden after the rest of the props, so nothing passed in can expose the\n // picture: the title says what it means.\n return (\n <div\n className={classNames(indicatorClass, bulmaHelperClasses, className)}\n {...rest}\n aria-hidden=\"true\"\n >\n {children}\n </div>\n );\n};\n\nEmptyStateIndicator.displayName = 'EmptyState.Indicator';\n\n/**\n * The heading, a Bulma `Title` at the level `as` sets, `h2` by default, and at the EmptyState's size.\n *\n * @function\n * @param {EmptyStateTitleProps} props - Props for EmptyState.Title.\n * @returns {JSX.Element} The rendered heading.\n */\nexport const EmptyStateTitle: React.FC<EmptyStateTitleProps> = ({\n as = 'h2',\n className,\n ...props\n}) => {\n // `size` isn't in the type, but on `Title` it picks the tag, so it's kept\n // off even when it's passed anyway. The level is `as` alone.\n const { size: _size, ...rest } = props as typeof props & { size?: unknown };\n const titleClass = usePrefixedClassNames('empty-state-title');\n const level = validEmptyStateHeadings.includes(as) ? as : 'h2';\n\n return (\n <Title as={level} className={classNames(titleClass, className)} {...rest} />\n );\n};\n\nEmptyStateTitle.displayName = 'EmptyState.Title';\n\n/**\n * A sentence or two under the title, in the color of the text around it.\n *\n * @function\n * @param {EmptyStateDescriptionProps} props - Props for EmptyState.Description.\n * @returns {JSX.Element} The rendered description.\n */\nexport const EmptyStateDescription: React.FC<EmptyStateDescriptionProps> = ({\n className,\n ...props\n}) => {\n const descriptionClass = usePrefixedClassNames('empty-state-description');\n return (\n <Paragraph className={classNames(descriptionClass, className)} {...props} />\n );\n};\n\nEmptyStateDescription.displayName = 'EmptyState.Description';\n\n/**\n * A row of buttons, centered under the text or at the start of the line beside the indicator.\n *\n * @function\n * @param {EmptyStateActionsProps} props - Props for EmptyState.Actions.\n * @returns {JSX.Element} The rendered row of buttons.\n */\nexport const EmptyStateActions: React.FC<EmptyStateActionsProps> = ({\n className,\n ...props\n}) => {\n const actionsClass = usePrefixedClassNames('empty-state-actions');\n return <Buttons className={classNames(actionsClass, className)} {...props} />;\n};\n\nEmptyStateActions.displayName = 'EmptyState.Actions';\n\n/** Whether a shorthand prop was given something to render. */\nconst isGiven = (node: React.ReactNode) =>\n node !== undefined &&\n node !== null &&\n typeof node !== 'boolean' &&\n node !== '';\n\n/**\n * The `EmptyState` component renders the block an app shows where content would be: an empty list, a search with no results, or a first-run screen.\n *\n * It's a column of a picture in a circle, a heading, a line of text and the\n * actions, with one layout whether it's written with the `icon`, `title` and\n * `description` props or with the parts. The children always render as\n * written, after the parts the props render.\n *\n * It has no role and no live region. It arrives already filled and goes away\n * when results come back, so it can't announce a search that matched\n * nothing: keep a `role=\"status\"` region beside the search field for that,\n * in the page the whole time. While the data loads, show a `Skeleton` or a\n * `Loader` instead, so \"No projects yet\" doesn't flash first, and show a\n * load that failed in a `Message` or a `Notification`.\n *\n * @function\n * @param {EmptyStateProps} props - Props for the EmptyState component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the root `<div>`.\n * @returns {JSX.Element} The rendered empty state.\n *\n * @example\n * <EmptyState\n * icon=\"folder-open\"\n * title=\"No projects yet\"\n * description=\"Projects you create or join show up here.\"\n * >\n * <Button color=\"primary\">New project</Button>\n * </EmptyState>\n */\nconst EmptyStateRoot = forwardRef<HTMLDivElement, EmptyStateProps>(\n (\n {\n className,\n icon,\n title,\n titleAs = 'h2',\n description,\n children,\n color,\n variant = 'light',\n size = 'normal',\n align = 'center',\n textColor,\n bgColor,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const rootClass = usePrefixedClassNames('empty-state', {\n [`is-${size}`]: (validEmptyStateSizes as readonly string[]).includes(\n size\n ),\n 'is-left': align === 'left',\n [`is-${color}`]:\n !!color && (validEmptyStateColors as readonly string[]).includes(color),\n 'is-filled': variant === 'filled',\n });\n\n // The props' parts first, each only when it's given, then the children\n // exactly as they were written: nothing here looks at them.\n return (\n <div\n ref={ref}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n {...rest}\n >\n {isGiven(icon) && (\n <EmptyStateIndicator>\n {typeof icon === 'string' ? <Icon name={icon} /> : icon}\n </EmptyStateIndicator>\n )}\n {isGiven(title) && (\n <EmptyStateTitle as={titleAs}>{title}</EmptyStateTitle>\n )}\n {isGiven(description) && (\n <EmptyStateDescription>{description}</EmptyStateDescription>\n )}\n {children}\n </div>\n );\n }\n);\n\nexport const EmptyState = withSubComponents(\n EmptyStateRoot,\n {\n Indicator: EmptyStateIndicator,\n Title: EmptyStateTitle,\n Description: EmptyStateDescription,\n Actions: EmptyStateActions,\n },\n 'EmptyState'\n);\n\nexport default EmptyState;\n"],"names":["_jsx","isGiven","_jsxs"],"mappings":";;;;;;;;;;;;AAaA,MAAM,qBAAqB,GAAG;IAC5B,SAAS;IACT,MAAM;IACN,MAAM;IACN,SAAS;IACT,SAAS;IACT,QAAQ;CACA;AAIV,MAAM,uBAAuB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU;AAI7E,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAU;AAsHlE;;;;;;AAMG;AACI,MAAM,mBAAmB,GAAuC,CAAC,EACtE,SAAS,EACT,SAAS,EACT,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,KAAI;AACH,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AACT,KAAA,CAAC;AACF,IAAA,MAAM,cAAc,GAAG,qBAAqB,CAAC,uBAAuB,CAAC;;;IAIrE,QACEA,aACE,SAAS,EAAE,UAAU,CAAC,cAAc,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAAA,GAChE,IAAI,EAAA,aAAA,EACI,MAAM,YAEjB,QAAQ,EAAA,CACL;AAEV;AAEA,mBAAmB,CAAC,WAAW,GAAG,sBAAsB;AAExD;;;;;;AAMG;AACI,MAAM,eAAe,GAAmC,CAAC,EAC9D,EAAE,GAAG,IAAI,EACT,SAAS,EACT,GAAG,KAAK,EACT,KAAI;;;IAGH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,KAA0C;AAC3E,IAAA,MAAM,UAAU,GAAG,qBAAqB,CAAC,mBAAmB,CAAC;AAC7D,IAAA,MAAM,KAAK,GAAG,uBAAuB,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;IAE9D,QACEA,IAAC,KAAK,EAAA,EAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,SAAS,CAAC,KAAM,IAAI,EAAA,CAAI;AAEhF;AAEA,eAAe,CAAC,WAAW,GAAG,kBAAkB;AAEhD;;;;;;AAMG;AACI,MAAM,qBAAqB,GAAyC,CAAC,EAC1E,SAAS,EACT,GAAG,KAAK,EACT,KAAI;AACH,IAAA,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,yBAAyB,CAAC;AACzE,IAAA,QACEA,GAAA,CAAC,SAAS,EAAA,EAAC,SAAS,EAAE,UAAU,CAAC,gBAAgB,EAAE,SAAS,CAAC,EAAA,GAAM,KAAK,EAAA,CAAI;AAEhF;AAEA,qBAAqB,CAAC,WAAW,GAAG,wBAAwB;AAE5D;;;;;;AAMG;AACI,MAAM,iBAAiB,GAAqC,CAAC,EAClE,SAAS,EACT,GAAG,KAAK,EACT,KAAI;AACH,IAAA,MAAM,YAAY,GAAG,qBAAqB,CAAC,qBAAqB,CAAC;AACjE,IAAA,OAAOA,GAAA,CAAC,OAAO,EAAA,EAAC,SAAS,EAAE,UAAU,CAAC,YAAY,EAAE,SAAS,CAAC,EAAA,GAAM,KAAK,GAAI;AAC/E;AAEA,iBAAiB,CAAC,WAAW,GAAG,oBAAoB;AAEpD;AACA,MAAMC,SAAO,GAAG,CAAC,IAAqB,KACpC,IAAI,KAAK,SAAS;AAClB,IAAA,IAAI,KAAK,IAAI;IACb,OAAO,IAAI,KAAK,SAAS;IACzB,IAAI,KAAK,EAAE;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BG;AACH,MAAM,cAAc,GAAG,UAAU,CAC/B,CACE,EACE,SAAS,EACT,IAAI,EACJ,KAAK,EACL,OAAO,GAAG,IAAI,EACd,WAAW,EACX,QAAQ,EACR,KAAK,EACL,OAAO,GAAG,OAAO,EACjB,IAAI,GAAG,QAAQ,EACf,KAAK,GAAG,QAAQ,EAChB,SAAS,EACT,OAAO,EACP,GAAG,KAAK,EACT,EACD,GAAG,KACD;AACF,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AACT,KAAA,CAAC;AACF,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,aAAa,EAAE;QACrD,CAAC,CAAA,GAAA,EAAM,IAAI,CAAA,CAAE,GAAI,oBAA0C,CAAC,QAAQ,CAClE,IAAI,CACL;QACD,SAAS,EAAE,KAAK,KAAK,MAAM;AAC3B,QAAA,CAAC,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,GACZ,CAAC,CAAC,KAAK,IAAK,qBAA2C,CAAC,QAAQ,CAAC,KAAK,CAAC;QACzE,WAAW,EAAE,OAAO,KAAK,QAAQ;AAClC,KAAA,CAAC;;;AAIF,IAAA,QACEC,IAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,UAAU,CAAC,SAAS,EAAE,kBAAkB,EAAE,SAAS,CAAC,KAC3D,IAAI,EAAA,QAAA,EAAA,CAEPD,SAAO,CAAC,IAAI,CAAC,KACZD,GAAA,CAAC,mBAAmB,cACjB,OAAO,IAAI,KAAK,QAAQ,GAAGA,IAAC,IAAI,EAAA,EAAC,IAAI,EAAE,IAAI,EAAA,CAAI,GAAG,IAAI,EAAA,CACnC,CACvB,EACAC,SAAO,CAAC,KAAK,CAAC,KACbD,GAAA,CAAC,eAAe,EAAA,EAAC,EAAE,EAAE,OAAO,EAAA,QAAA,EAAG,KAAK,EAAA,CAAmB,CACxD,EACAC,SAAO,CAAC,WAAW,CAAC,KACnBD,GAAA,CAAC,qBAAqB,EAAA,EAAA,QAAA,EAAE,WAAW,EAAA,CAAyB,CAC7D,EACA,QAAQ,CAAA,EAAA,CACL;AAEV,CAAC,CACF;AAEM,MAAM,UAAU,GAAG,iBAAiB,CACzC,cAAc,EACd;AACE,IAAA,SAAS,EAAE,mBAAmB;AAC9B,IAAA,KAAK,EAAE,eAAe;AACtB,IAAA,WAAW,EAAE,qBAAqB;AAClC,IAAA,OAAO,EAAE,iBAAiB;CAC3B,EACD,YAAY;;"}
1
+ {"version":3,"file":"EmptyState.js","sources":["../../../src/components/EmptyState.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { Buttons, ButtonsProps } from '../elements/Buttons';\nimport { Icon } from '../elements/Icon';\nimport { Paragraph, ParagraphProps } from '../elements/Paragraph';\nimport { Title, TitleProps } from '../elements/Title';\nimport { isGiven } from '../helpers/isGiven';\n\nconst validEmptyStateColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n/** The Bulma colors the indicator takes. */\nexport type EmptyStateColor = (typeof validEmptyStateColors)[number];\n\nconst validEmptyStateHeadings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;\n/** A title's heading level. It's always a heading, so there's no `p`. */\nexport type EmptyStateHeading = (typeof validEmptyStateHeadings)[number];\n\nconst validEmptyStateSizes = ['small', 'medium', 'large'] as const;\n\n/**\n * Props for the EmptyState component. The helper props, the other attributes\n * and the `ref` land on the root `<div>`. The native `title` attribute isn't\n * one of them, since `title` here is the heading.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the root `<div>`.\n */\nexport interface EmptyStateProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'title'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * The picture in the indicator's circle. A string is an `Icon` name, drawn\n * from the icon library `ConfigProvider` sets. Anything else (an `<Icon>`\n * with its own `variant` or `library`, an SVG, an `<img>`) renders as it is.\n * It's decorative and hidden from assistive tech, so the title says what\n * it means.\n */\n icon?: React.ReactNode;\n /**\n * The heading, rendered as `EmptyState.Title`. It's always a real heading,\n * at the level `titleAs` sets.\n */\n title?: React.ReactNode;\n /**\n * The heading level for `title`: the level under the heading of the list\n * it stands in for, such as `h3` for a list under an `h2`. Only this sets\n * the level, and the size comes from `size`.\n */\n titleAs?: EmptyStateHeading;\n /** A sentence or two under the title, rendered as `EmptyState.Description`. */\n description?: React.ReactNode;\n /**\n * Rendered as written, after whatever `icon`, `title` and `description`\n * render. One button can go straight in, and it sits centered under the\n * text. Group two or more in `EmptyState.Actions`, or they stack. Without\n * the props, write the parts here in the order they're shown:\n * `EmptyState.Indicator`, `.Title`, `.Description`, then the actions.\n */\n children?: React.ReactNode;\n /**\n * The indicator's color. It colors the circle and its icon, not the text.\n * Without it the circle is neutral, since an empty list isn't a status.\n */\n color?: EmptyStateColor;\n /**\n * `light` is a soft tint of `color` behind the icon in the color. `filled`\n * is the color itself, with the icon in its invert.\n */\n variant?: 'light' | 'filled';\n /** Sizes the indicator, the title, the description and the spacing together. */\n size?: 'small' | 'normal' | 'medium' | 'large';\n /**\n * `center` stacks everything in a centered column. `left` puts the\n * indicator beside the text, at the start of the line, so on the right in\n * a right-to-left page, and keeps the block at least as tall as the circle\n * whatever block padding is in effect. Without an indicator, it only puts\n * the text at the start of the line. In `left`, the padding at the start\n * of the line is what makes room for the indicator, so pad it with `py`\n * rather than `p` or `px`.\n */\n align?: 'center' | 'left';\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** Props for EmptyState.Indicator, the circle the picture sits in. */\nexport interface EmptyStateIndicatorProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * The picture: an `Icon`, an SVG or an `<img>`. The indicator is hidden\n * from assistive tech, an `Icon` with an `ariaLabel` included, so anything\n * it means goes in the title or the description.\n */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * Props for EmptyState.Title. It renders `Title` with `as` and never `size`,\n * so the level is `as` alone and the size comes from the EmptyState.\n */\nexport interface EmptyStateTitleProps extends Omit<\n TitleProps,\n 'as' | 'size' | 'isSpaced'\n> {\n /**\n * The heading level: the level under the heading of the list the empty\n * state stands in for.\n */\n as?: EmptyStateHeading;\n}\n\n/** Props for EmptyState.Description, a `<p>` under the title. */\nexport type EmptyStateDescriptionProps = ParagraphProps;\n\n/**\n * Props for EmptyState.Actions, the row of buttons. It renders `Buttons`,\n * and the EmptyState aligns it, so `isCentered` and `isRight` are left out.\n */\nexport type EmptyStateActionsProps = Omit<\n ButtonsProps,\n 'isCentered' | 'isRight'\n>;\n\n/**\n * The circle the picture sits in, hidden from assistive tech whatever is in it.\n *\n * @function\n * @param {EmptyStateIndicatorProps} props - Props for EmptyState.Indicator.\n * @returns {JSX.Element} The rendered indicator.\n */\nexport const EmptyStateIndicator: React.FC<EmptyStateIndicatorProps> = ({\n className,\n textColor,\n bgColor,\n children,\n ...props\n}) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const indicatorClass = usePrefixedClassNames('empty-state-indicator');\n\n // Hidden after the rest of the props, so nothing passed in can expose the\n // picture: the title says what it means.\n return (\n <div\n className={classNames(indicatorClass, bulmaHelperClasses, className)}\n {...rest}\n aria-hidden=\"true\"\n >\n {children}\n </div>\n );\n};\n\nEmptyStateIndicator.displayName = 'EmptyState.Indicator';\n\n/**\n * The heading, a Bulma `Title` at the level `as` sets, `h2` by default, and at the EmptyState's size.\n *\n * @function\n * @param {EmptyStateTitleProps} props - Props for EmptyState.Title.\n * @returns {JSX.Element} The rendered heading.\n */\nexport const EmptyStateTitle: React.FC<EmptyStateTitleProps> = ({\n as = 'h2',\n className,\n ...props\n}) => {\n // `size` isn't in the type, but on `Title` it picks the tag, so it's kept\n // off even when it's passed anyway. The level is `as` alone.\n const { size: _size, ...rest } = props as typeof props & { size?: unknown };\n const titleClass = usePrefixedClassNames('empty-state-title');\n const level = validEmptyStateHeadings.includes(as) ? as : 'h2';\n\n return (\n <Title as={level} className={classNames(titleClass, className)} {...rest} />\n );\n};\n\nEmptyStateTitle.displayName = 'EmptyState.Title';\n\n/**\n * A sentence or two under the title, in the color of the text around it.\n *\n * @function\n * @param {EmptyStateDescriptionProps} props - Props for EmptyState.Description.\n * @returns {JSX.Element} The rendered description.\n */\nexport const EmptyStateDescription: React.FC<EmptyStateDescriptionProps> = ({\n className,\n ...props\n}) => {\n const descriptionClass = usePrefixedClassNames('empty-state-description');\n return (\n <Paragraph className={classNames(descriptionClass, className)} {...props} />\n );\n};\n\nEmptyStateDescription.displayName = 'EmptyState.Description';\n\n/**\n * A row of buttons, centered under the text or at the start of the line beside the indicator.\n *\n * @function\n * @param {EmptyStateActionsProps} props - Props for EmptyState.Actions.\n * @returns {JSX.Element} The rendered row of buttons.\n */\nexport const EmptyStateActions: React.FC<EmptyStateActionsProps> = ({\n className,\n ...props\n}) => {\n const actionsClass = usePrefixedClassNames('empty-state-actions');\n return <Buttons className={classNames(actionsClass, className)} {...props} />;\n};\n\nEmptyStateActions.displayName = 'EmptyState.Actions';\n\n/**\n * The `EmptyState` component renders the block an app shows where content would be: an empty list, a search with no results, or a first-run screen.\n *\n * It's a column of a picture in a circle, a heading, a line of text and the\n * actions, with one layout whether it's written with the `icon`, `title` and\n * `description` props or with the parts. The children always render as\n * written, after the parts the props render.\n *\n * It has no role and no live region. It arrives already filled and goes away\n * when results come back, so it can't announce a search that matched\n * nothing: keep a `role=\"status\"` region beside the search field for that,\n * in the page the whole time. While the data loads, show a `Skeleton` or a\n * `Loader` instead, so \"No projects yet\" doesn't flash first, and show a\n * load that failed in a `Message` or a `Notification`.\n *\n * @function\n * @param {EmptyStateProps} props - Props for the EmptyState component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the root `<div>`.\n * @returns {JSX.Element} The rendered empty state.\n *\n * @example\n * <EmptyState\n * icon=\"folder-open\"\n * title=\"No projects yet\"\n * description=\"Projects you create or join show up here.\"\n * >\n * <Button color=\"primary\">New project</Button>\n * </EmptyState>\n */\nconst EmptyStateRoot = forwardRef<HTMLDivElement, EmptyStateProps>(\n (\n {\n className,\n icon,\n title,\n titleAs = 'h2',\n description,\n children,\n color,\n variant = 'light',\n size = 'normal',\n align = 'center',\n textColor,\n bgColor,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const rootClass = usePrefixedClassNames('empty-state', {\n [`is-${size}`]: (validEmptyStateSizes as readonly string[]).includes(\n size\n ),\n 'is-left': align === 'left',\n [`is-${color}`]:\n !!color && (validEmptyStateColors as readonly string[]).includes(color),\n 'is-filled': variant === 'filled',\n });\n\n // The props' parts first, each only when it's given, then the children\n // exactly as they were written: nothing here looks at them.\n return (\n <div\n ref={ref}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n {...rest}\n >\n {isGiven(icon) && (\n <EmptyStateIndicator>\n {typeof icon === 'string' ? <Icon name={icon} /> : icon}\n </EmptyStateIndicator>\n )}\n {isGiven(title) && (\n <EmptyStateTitle as={titleAs}>{title}</EmptyStateTitle>\n )}\n {isGiven(description) && (\n <EmptyStateDescription>{description}</EmptyStateDescription>\n )}\n {children}\n </div>\n );\n }\n);\n\nexport const EmptyState = withSubComponents(\n EmptyStateRoot,\n {\n Indicator: EmptyStateIndicator,\n Title: EmptyStateTitle,\n Description: EmptyStateDescription,\n Actions: EmptyStateActions,\n },\n 'EmptyState'\n);\n\nexport default EmptyState;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
@@ -11,7 +11,7 @@ import { warnOnce } from '../helpers/devWarnings.js';
11
11
  const EDGES = ['top', 'bottom', 'start', 'end'];
12
12
  // Layout can land a fraction of a pixel short of an edge, so anything closer
13
13
  // than this counts as there.
14
- const SLACK = 1;
14
+ const SLACK$1 = 1;
15
15
  const sameMeasure = (a, b) => !!a &&
16
16
  a.overflowing === b.overflowing &&
17
17
  a.barInline === b.barInline &&
@@ -92,8 +92,8 @@ const ScrollArea = forwardRef(({ className, style, textColor, bgColor, height, m
92
92
  const scrollsX = orientation !== 'vertical';
93
93
  const maxTop = el.scrollHeight - el.clientHeight;
94
94
  const maxLeft = el.scrollWidth - el.clientWidth;
95
- const overflowY = scrollsY && maxTop > SLACK;
96
- const overflowX = scrollsX && maxLeft > SLACK;
95
+ const overflowY = scrollsY && maxTop > SLACK$1;
96
+ const overflowX = scrollsX && maxLeft > SLACK$1;
97
97
  // Right to left, scrollLeft runs from 0 at the start to negative.
98
98
  const fromStart = layoutRef.current.rtl
99
99
  ? -el.scrollLeft
@@ -101,10 +101,10 @@ const ScrollArea = forwardRef(({ className, style, textColor, bgColor, height, m
101
101
  const next = {
102
102
  overflowing: overflowY || overflowX,
103
103
  edges: {
104
- top: overflowY && el.scrollTop > SLACK,
105
- bottom: overflowY && maxTop - el.scrollTop > SLACK,
106
- start: overflowX && fromStart > SLACK,
107
- end: overflowX && maxLeft - fromStart > SLACK,
104
+ top: overflowY && el.scrollTop > SLACK$1,
105
+ bottom: overflowY && maxTop - el.scrollTop > SLACK$1,
106
+ start: overflowX && fromStart > SLACK$1,
107
+ end: overflowX && maxLeft - fromStart > SLACK$1,
108
108
  },
109
109
  barInline: layoutRef.current.barInline,
110
110
  barBlock: layoutRef.current.barBlock,
@@ -1 +1 @@
1
- {"version":3,"file":"ScrollArea.js","sources":["../../../src/components/ScrollArea.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport { useLatestRef } from '../helpers/useLatestRef';\nimport { warnOnce } from '../helpers/devWarnings';\n\n/** Which way the content scrolls. */\nexport type ScrollAreaOrientation = 'vertical' | 'horizontal' | 'both';\n\n/** When the scrollbars show. */\nexport type ScrollAreaScrollbars = 'auto' | 'hover' | 'always';\n\n/** An edge of the content: the block's top and bottom, the line's start and end. */\nexport type ScrollAreaEdge = 'top' | 'bottom' | 'start' | 'end';\n\nconst EDGES: readonly ScrollAreaEdge[] = ['top', 'bottom', 'start', 'end'];\n\n// Layout can land a fraction of a pixel short of an edge, so anything closer\n// than this counts as there.\nconst SLACK = 1;\n\n/**\n * Props for the ScrollArea component.\n *\n * @helperProps The helper props go on the outer `<div>`, the `.scroll-area`\n * frame, with `className` and `style`. The `ref`, the name, `onScroll` and\n * the other attributes go on the `<div>` inside it, the viewport that\n * scrolls.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the viewport, the element that scrolls, for `scrollTo` and the like.\n * @extraProp {string} [aria-label] - Names the region, such as \"Release notes\". It needs a name, so pass this or `aria-labelledby`, and a development build warns when it has neither. The name is rendered with the region's role, while the content overflows, since ARIA doesn't allow naming a plain `div`.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading that names the region, in place of `aria-label`.\n * @extraProp {React.UIEventHandler<HTMLDivElement>} [onScroll] - Called as the viewport scrolls.\n */\nexport interface ScrollAreaProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'role' | 'tabIndex'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes for the outer frame. */\n className?: string;\n /** Inline styles for the outer frame. */\n style?: React.CSSProperties;\n /** What scrolls. */\n children?: React.ReactNode;\n /**\n * The area's height. A number is pixels, and a string is any CSS length\n * (`\"20rem\"`, `\"50vh\"`, `\"100%\"`).\n */\n height?: number | string;\n /**\n * The height it grows to with its content, then scrolls past. A number is\n * pixels. Use it in place of `height` for an area that's only as tall as\n * it needs to be.\n */\n maxHeight?: number | string;\n /**\n * Which way it scrolls. `vertical` and `horizontal` clip the other axis,\n * and `both` scrolls whichever overflows.\n */\n orientation?: ScrollAreaOrientation;\n /**\n * When the scrollbars show. `auto`: whenever the content overflows, the\n * way the system shows them. `hover`: colorless until the pointer is over\n * the area or focus is in it, which hides that the area scrolls until\n * then, so keep it for places where the layout makes that plain.\n * `always`: on every axis it scrolls, even when the content fits. Where the\n * system draws its bars over the content (macOS by default, phones and\n * tablets), they still show only while you scroll, and in forced colors\n * they always show.\n */\n showScrollbars?: ScrollAreaScrollbars;\n /**\n * Keeps room for the vertical scrollbar whether or not it shows, so the\n * content doesn't shift when it starts to overflow. It does nothing where\n * the system draws its bars over the content.\n */\n hasStableGutter?: boolean;\n /**\n * Fades the content out toward each edge that has more past it, without\n * fading the scrollbar. It's off in forced colors and when the user asks\n * for more contrast.\n */\n hasFade?: boolean;\n /**\n * Called with the edge when scrolling reaches it, once each time it\n * arrives, such as to load older messages at the top. It isn't called for\n * the edge the area starts at, or when the content changes size.\n */\n onEdgeReach?: (edge: ScrollAreaEdge) => void;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** What a measurement of the viewport found. */\ninterface Measured {\n /** Overflows on an axis it scrolls. */\n overflowing: boolean;\n /** Each edge that has more content past it. */\n edges: Record<ScrollAreaEdge, boolean>;\n /** The vertical scrollbar's width, and the horizontal one's height. */\n barInline: number;\n barBlock: number;\n}\n\nconst sameMeasure = (a: Measured | null, b: Measured): boolean =>\n !!a &&\n a.overflowing === b.overflowing &&\n a.barInline === b.barInline &&\n a.barBlock === b.barBlock &&\n EDGES.every(edge => a.edges[edge] === b.edges[edge]);\n\n/**\n * The `ScrollArea` component renders a box whose content scrolls inside it, on the browser's own scrollbars in Bulma's colors.\n *\n * The bars are the system's, made thin and colored from Bulma's tokens, so\n * they follow dark mode and keep the system's own settings. While the\n * content overflows, the viewport is a region you can tab to and scroll by\n * keyboard, Safari included, named by `aria-label` or `aria-labelledby`.\n * While it fits, it's a plain box. Setting the bars' colors turns off any\n * `::-webkit-scrollbar` styles inside it.\n *\n * @function\n * @param {ScrollAreaProps} props - Props for the ScrollArea component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the viewport.\n * @returns {JSX.Element} The rendered scroll area.\n *\n * @example\n * <ScrollArea height={240} aria-label=\"Release notes\">\n * <Content>…</Content>\n * </ScrollArea>\n *\n * @example\n * <ScrollArea maxHeight=\"20rem\" aria-label=\"Messages\" hasFade>\n * …\n * </ScrollArea>\n */\nexport const ScrollArea = forwardRef<HTMLDivElement, ScrollAreaProps>(\n (\n {\n className,\n style,\n textColor,\n bgColor,\n height,\n maxHeight,\n orientation = 'both',\n showScrollbars = 'auto',\n hasStableGutter,\n hasFade,\n onEdgeReach,\n onScroll,\n onFocus,\n onBlur,\n children,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n\n const frameClass = usePrefixedClassNames('scroll-area', {\n 'is-vertical': orientation === 'vertical',\n 'is-horizontal': orientation === 'horizontal',\n 'has-hover-scrollbars': showScrollbars === 'hover',\n 'has-visible-scrollbars': showScrollbars === 'always',\n 'has-stable-gutter': !!hasStableGutter,\n 'has-fade': !!hasFade,\n });\n const viewportClass = usePrefixedClassNames('scroll-area-viewport');\n\n const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'ScrollArea:unnamed',\n '[bestax-bulma] <ScrollArea> has no accessible name. Pass ' +\n 'aria-label or aria-labelledby, so a screen reader can say what ' +\n 'took focus when the area overflows.'\n );\n }\n }, [named]);\n\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLDivElement | null) => {\n viewportRef.current = node;\n if (typeof ref === 'function') return ref(node);\n if (ref) ref.current = node;\n },\n [ref]\n );\n\n // Nothing is known about the overflow until the client measures it, so\n // the server renders a plain scroller and hydrates as one.\n const [measured, setMeasured] = useState<Measured | null>(null);\n const [focused, setFocused] = useState(false);\n // The last measurement, read by the scroll handler to see which edges\n // it just reached, and the direction and bar sizes, which only change\n // with layout, so a scroll doesn't read computed styles.\n const lastRef = useRef<Measured | null>(null);\n const layoutRef = useRef({ rtl: false, barInline: 0, barBlock: 0 });\n const onEdgeReachRef = useLatestRef(onEdgeReach);\n\n const measure = useCallback(\n (fromScroll: boolean) => {\n const el = viewportRef.current;\n /* istanbul ignore next: measuring only runs while it's mounted */\n if (!el) return;\n const scrollsY = orientation !== 'horizontal';\n const scrollsX = orientation !== 'vertical';\n const maxTop = el.scrollHeight - el.clientHeight;\n const maxLeft = el.scrollWidth - el.clientWidth;\n const overflowY = scrollsY && maxTop > SLACK;\n const overflowX = scrollsX && maxLeft > SLACK;\n // Right to left, scrollLeft runs from 0 at the start to negative.\n const fromStart = layoutRef.current.rtl\n ? -el.scrollLeft\n : el.scrollLeft;\n const next: Measured = {\n overflowing: overflowY || overflowX,\n edges: {\n top: overflowY && el.scrollTop > SLACK,\n bottom: overflowY && maxTop - el.scrollTop > SLACK,\n start: overflowX && fromStart > SLACK,\n end: overflowX && maxLeft - fromStart > SLACK,\n },\n barInline: layoutRef.current.barInline,\n barBlock: layoutRef.current.barBlock,\n };\n const last = lastRef.current;\n lastRef.current = next;\n if (fromScroll && last) {\n for (const edge of EDGES) {\n if (last.edges[edge] && !next.edges[edge]) {\n onEdgeReachRef.current?.(edge);\n }\n }\n }\n setMeasured(prev => (sameMeasure(prev, next) ? prev : next));\n },\n [orientation, onEdgeReachRef]\n );\n\n // Layout: the direction and the bars' sizes, then the overflow. It runs\n // before paint, and again whenever the viewport or anything in it\n // changes size, or the children change.\n useClientLayoutEffect(() => {\n const el = viewportRef.current;\n /* istanbul ignore next: the effect only runs once it's mounted */\n if (!el) return;\n const relayout = () => {\n const cs = getComputedStyle(el);\n const px = (value: string) => parseFloat(value) || 0;\n layoutRef.current = {\n rtl: cs.direction === 'rtl',\n barInline: Math.max(\n 0,\n el.offsetWidth -\n el.clientWidth -\n px(cs.borderLeftWidth) -\n px(cs.borderRightWidth)\n ),\n barBlock: Math.max(\n 0,\n el.offsetHeight -\n el.clientHeight -\n px(cs.borderTopWidth) -\n px(cs.borderBottomWidth)\n ),\n };\n measure(false);\n };\n relayout();\n if (typeof ResizeObserver === 'undefined') return;\n const resizes = new ResizeObserver(relayout);\n const observeAll = () => {\n resizes.disconnect();\n resizes.observe(el);\n for (const child of Array.from(el.children)) resizes.observe(child);\n };\n observeAll();\n // Content that changes can overflow before any size is reported: a\n // child added or taken away, or text React rewrites in place, which\n // resizes no element when the area's height is fixed, and inline\n // children, which a ResizeObserver doesn't report at all.\n const mutations =\n typeof MutationObserver === 'undefined'\n ? null\n : new MutationObserver(records => {\n if (\n records.some(\n record => record.target === el && record.type === 'childList'\n )\n ) {\n observeAll();\n }\n relayout();\n });\n mutations?.observe(el, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n return () => {\n resizes.disconnect();\n mutations?.disconnect();\n };\n }, [measure]);\n\n const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {\n onScroll?.(event);\n measure(true);\n };\n const handleFocus = (event: React.FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n if (event.target === event.currentTarget) setFocused(true);\n };\n const handleBlur = (event: React.FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n if (event.target === event.currentTarget) setFocused(false);\n };\n\n // A tab stop while it overflows. Taking tabIndex off the focused element\n // would drop focus to the page, so a viewport that stops overflowing\n // keeps it until focus leaves.\n const isRegion = !!measured?.overflowing || focused;\n // The name goes with the role: ARIA prohibits naming a plain div.\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n ...viewportRest\n } = rest;\n const edgeAttributes: Record<string, string> = {};\n if (measured) {\n for (const edge of EDGES) {\n if (measured.edges[edge]) edgeAttributes[`data-overflow-${edge}`] = '';\n }\n }\n const viewportStyle = measured\n ? ({\n '--scroll-area-bar-inline-size': `${measured.barInline}px`,\n '--scroll-area-bar-block-size': `${measured.barBlock}px`,\n } as React.CSSProperties)\n : undefined;\n\n return (\n <div\n className={classNames(frameClass, bulmaHelperClasses, className)}\n style={\n height !== undefined || maxHeight !== undefined\n ? {\n ...style,\n // Only what's given, so `maxHeight` alone keeps a height\n // the style sets.\n ...(height !== undefined && { height }),\n ...(maxHeight !== undefined && { maxHeight }),\n }\n : style\n }\n >\n <div\n ref={setRefs}\n className={viewportClass}\n style={viewportStyle}\n {...viewportRest}\n {...edgeAttributes}\n role={isRegion ? 'region' : undefined}\n aria-label={isRegion ? ariaLabel : undefined}\n aria-labelledby={isRegion ? ariaLabelledby : undefined}\n tabIndex={isRegion ? 0 : undefined}\n onScroll={handleScroll}\n onFocus={handleFocus}\n onBlur={handleBlur}\n >\n {children}\n </div>\n </div>\n );\n }\n);\n\nScrollArea.displayName = 'ScrollArea';\n\nexport default ScrollArea;\n"],"names":["_jsx"],"mappings":";;;;;;;;;;AA0BA,MAAM,KAAK,GAA8B,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;AAE1E;AACA;AACA,MAAM,KAAK,GAAG,CAAC;AAuFf,MAAM,WAAW,GAAG,CAAC,CAAkB,EAAE,CAAW,KAClD,CAAC,CAAC,CAAC;AACH,IAAA,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;AAC/B,IAAA,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS;AAC3B,IAAA,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;IACzB,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;MACU,UAAU,GAAG,UAAU,CAClC,CACE,EACE,SAAS,EACT,KAAK,EACL,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,WAAW,GAAG,MAAM,EACpB,cAAc,GAAG,MAAM,EACvB,eAAe,EACf,OAAO,EACP,WAAW,EACX,QAAQ,EACR,OAAO,EACP,MAAM,EACN,QAAQ,EACR,GAAG,KAAK,EACT,EACD,GAAG,KACD;AACF,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AACT,KAAA,CAAC;AAEF,IAAA,MAAM,UAAU,GAAG,qBAAqB,CAAC,aAAa,EAAE;QACtD,aAAa,EAAE,WAAW,KAAK,UAAU;QACzC,eAAe,EAAE,WAAW,KAAK,YAAY;QAC7C,sBAAsB,EAAE,cAAc,KAAK,OAAO;QAClD,wBAAwB,EAAE,cAAc,KAAK,QAAQ;QACrD,mBAAmB,EAAE,CAAC,CAAC,eAAe;QACtC,UAAU,EAAE,CAAC,CAAC,OAAO;AACtB,KAAA,CAAC;AACF,IAAA,MAAM,aAAa,GAAG,qBAAqB,CAAC,sBAAsB,CAAC;AAEnE,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACpE,SAAS,CAAC,MAAK;QACb,IAAI,CAAC,KAAK,EAAE;YACV,QAAQ,CACN,oBAAoB,EACpB,2DAA2D;gBACzD,iEAAiE;AACjE,gBAAA,qCAAqC,CACxC;QACH;AACF,IAAA,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAEX,IAAA,MAAM,WAAW,GAAG,MAAM,CAAwB,IAAI,CAAC;AACvD,IAAA,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,IAA2B,KAAI;AAC9B,QAAA,WAAW,CAAC,OAAO,GAAG,IAAI;QAC1B,IAAI,OAAO,GAAG,KAAK,UAAU;AAAE,YAAA,OAAO,GAAG,CAAC,IAAI,CAAC;AAC/C,QAAA,IAAI,GAAG;AAAE,YAAA,GAAG,CAAC,OAAO,GAAG,IAAI;AAC7B,IAAA,CAAC,EACD,CAAC,GAAG,CAAC,CACN;;;IAID,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC;IAC/D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;;;;AAI7C,IAAA,MAAM,OAAO,GAAG,MAAM,CAAkB,IAAI,CAAC;AAC7C,IAAA,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AACnE,IAAA,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,CAAC;AAEhD,IAAA,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,UAAmB,KAAI;AACtB,QAAA,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO;;AAE9B,QAAA,IAAI,CAAC,EAAE;YAAE;AACT,QAAA,MAAM,QAAQ,GAAG,WAAW,KAAK,YAAY;AAC7C,QAAA,MAAM,QAAQ,GAAG,WAAW,KAAK,UAAU;QAC3C,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY;QAChD,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW;AAC/C,QAAA,MAAM,SAAS,GAAG,QAAQ,IAAI,MAAM,GAAG,KAAK;AAC5C,QAAA,MAAM,SAAS,GAAG,QAAQ,IAAI,OAAO,GAAG,KAAK;;AAE7C,QAAA,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC;AAClC,cAAE,CAAC,EAAE,CAAC;AACN,cAAE,EAAE,CAAC,UAAU;AACjB,QAAA,MAAM,IAAI,GAAa;YACrB,WAAW,EAAE,SAAS,IAAI,SAAS;AACnC,YAAA,KAAK,EAAE;AACL,gBAAA,GAAG,EAAE,SAAS,IAAI,EAAE,CAAC,SAAS,GAAG,KAAK;gBACtC,MAAM,EAAE,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,SAAS,GAAG,KAAK;AAClD,gBAAA,KAAK,EAAE,SAAS,IAAI,SAAS,GAAG,KAAK;AACrC,gBAAA,GAAG,EAAE,SAAS,IAAI,OAAO,GAAG,SAAS,GAAG,KAAK;AAC9C,aAAA;AACD,YAAA,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,SAAS;AACtC,YAAA,QAAQ,EAAE,SAAS,CAAC,OAAO,CAAC,QAAQ;SACrC;AACD,QAAA,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO;AAC5B,QAAA,OAAO,CAAC,OAAO,GAAG,IAAI;AACtB,QAAA,IAAI,UAAU,IAAI,IAAI,EAAE;AACtB,YAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,gBAAA,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;AACzC,oBAAA,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC;gBAChC;YACF;QACF;QACA,WAAW,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;AAC9D,IAAA,CAAC,EACD,CAAC,WAAW,EAAE,cAAc,CAAC,CAC9B;;;;IAKD,qBAAqB,CAAC,MAAK;AACzB,QAAA,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO;;AAE9B,QAAA,IAAI,CAAC,EAAE;YAAE;QACT,MAAM,QAAQ,GAAG,MAAK;AACpB,YAAA,MAAM,EAAE,GAAG,gBAAgB,CAAC,EAAE,CAAC;AAC/B,YAAA,MAAM,EAAE,GAAG,CAAC,KAAa,KAAK,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;YACpD,SAAS,CAAC,OAAO,GAAG;AAClB,gBAAA,GAAG,EAAE,EAAE,CAAC,SAAS,KAAK,KAAK;gBAC3B,SAAS,EAAE,IAAI,CAAC,GAAG,CACjB,CAAC,EACD,EAAE,CAAC,WAAW;AACZ,oBAAA,EAAE,CAAC,WAAW;AACd,oBAAA,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC;AACtB,oBAAA,EAAE,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAC1B;gBACD,QAAQ,EAAE,IAAI,CAAC,GAAG,CAChB,CAAC,EACD,EAAE,CAAC,YAAY;AACb,oBAAA,EAAE,CAAC,YAAY;AACf,oBAAA,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC;AACrB,oBAAA,EAAE,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAC3B;aACF;YACD,OAAO,CAAC,KAAK,CAAC;AAChB,QAAA,CAAC;AACD,QAAA,QAAQ,EAAE;QACV,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE;AAC3C,QAAA,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC;QAC5C,MAAM,UAAU,GAAG,MAAK;YACtB,OAAO,CAAC,UAAU,EAAE;AACpB,YAAA,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC;AAAE,gBAAA,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;AACrE,QAAA,CAAC;AACD,QAAA,UAAU,EAAE;;;;;AAKZ,QAAA,MAAM,SAAS,GACb,OAAO,gBAAgB,KAAK;AAC1B,cAAE;AACF,cAAE,IAAI,gBAAgB,CAAC,OAAO,IAAG;gBAC7B,IACE,OAAO,CAAC,IAAI,CACV,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,CAC9D,EACD;AACA,oBAAA,UAAU,EAAE;gBACd;AACA,gBAAA,QAAQ,EAAE;AACZ,YAAA,CAAC,CAAC;AACR,QAAA,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE;AACrB,YAAA,SAAS,EAAE,IAAI;AACf,YAAA,aAAa,EAAE,IAAI;AACnB,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC;AACF,QAAA,OAAO,MAAK;YACV,OAAO,CAAC,UAAU,EAAE;YACpB,SAAS,EAAE,UAAU,EAAE;AACzB,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AAEb,IAAA,MAAM,YAAY,GAAG,CAAC,KAAoC,KAAI;AAC5D,QAAA,QAAQ,GAAG,KAAK,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC;AACf,IAAA,CAAC;AACD,IAAA,MAAM,WAAW,GAAG,CAAC,KAAuC,KAAI;AAC9D,QAAA,OAAO,GAAG,KAAK,CAAC;AAChB,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa;YAAE,UAAU,CAAC,IAAI,CAAC;AAC5D,IAAA,CAAC;AACD,IAAA,MAAM,UAAU,GAAG,CAAC,KAAuC,KAAI;AAC7D,QAAA,MAAM,GAAG,KAAK,CAAC;AACf,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa;YAAE,UAAU,CAAC,KAAK,CAAC;AAC7D,IAAA,CAAC;;;;IAKD,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,EAAE,WAAW,IAAI,OAAO;;AAEnD,IAAA,MAAM,EACJ,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EACjC,GAAG,YAAY,EAChB,GAAG,IAAI;IACR,MAAM,cAAc,GAA2B,EAAE;IACjD,IAAI,QAAQ,EAAE;AACZ,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,YAAA,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;AAAE,gBAAA,cAAc,CAAC,CAAA,cAAA,EAAiB,IAAI,EAAE,CAAC,GAAG,EAAE;QACxE;IACF;IACA,MAAM,aAAa,GAAG;AACpB,UAAG;AACC,YAAA,+BAA+B,EAAE,CAAA,EAAG,QAAQ,CAAC,SAAS,CAAA,EAAA,CAAI;AAC1D,YAAA,8BAA8B,EAAE,CAAA,EAAG,QAAQ,CAAC,QAAQ,CAAA,EAAA,CAAI;AACjC;UACzB,SAAS;IAEb,QACEA,aACE,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAChE,KAAK,EACH,MAAM,KAAK,SAAS,IAAI,SAAS,KAAK;AACpC,cAAE;AACE,gBAAA,GAAG,KAAK;;;gBAGR,IAAI,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;gBACvC,IAAI,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC;AAC9C;AACH,cAAE,KAAK,EAAA,QAAA,EAGXA,GAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,EAAA,GAChB,YAAY,EAAA,GACZ,cAAc,EAClB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,gBACzB,QAAQ,GAAG,SAAS,GAAG,SAAS,EAAA,iBAAA,EAC3B,QAAQ,GAAG,cAAc,GAAG,SAAS,EACtD,QAAQ,EAAE,QAAQ,GAAG,CAAC,GAAG,SAAS,EAClC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,EAAA,QAAA,EAEjB,QAAQ,EAAA,CACL,EAAA,CACF;AAEV,CAAC;AAGH,UAAU,CAAC,WAAW,GAAG,YAAY;;"}
1
+ {"version":3,"file":"ScrollArea.js","sources":["../../../src/components/ScrollArea.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport { useLatestRef } from '../helpers/useLatestRef';\nimport { warnOnce } from '../helpers/devWarnings';\n\n/** Which way the content scrolls. */\nexport type ScrollAreaOrientation = 'vertical' | 'horizontal' | 'both';\n\n/** When the scrollbars show. */\nexport type ScrollAreaScrollbars = 'auto' | 'hover' | 'always';\n\n/** An edge of the content: the block's top and bottom, the line's start and end. */\nexport type ScrollAreaEdge = 'top' | 'bottom' | 'start' | 'end';\n\nconst EDGES: readonly ScrollAreaEdge[] = ['top', 'bottom', 'start', 'end'];\n\n// Layout can land a fraction of a pixel short of an edge, so anything closer\n// than this counts as there.\nconst SLACK = 1;\n\n/**\n * Props for the ScrollArea component.\n *\n * @helperProps The helper props go on the outer `<div>`, the `.scroll-area`\n * frame, with `className` and `style`. The `ref`, the name, `onScroll` and\n * the other attributes go on the `<div>` inside it, the viewport that\n * scrolls.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the viewport, the element that scrolls, for `scrollTo` and the like.\n * @extraProp {string} [aria-label] - Names the region, such as \"Release notes\". It needs a name, so pass this or `aria-labelledby`, and a development build warns when it has neither. The name is rendered with the region's role, while the content overflows, since ARIA doesn't allow naming a plain `div`.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading that names the region, in place of `aria-label`.\n * @extraProp {React.UIEventHandler<HTMLDivElement>} [onScroll] - Called as the viewport scrolls.\n */\nexport interface ScrollAreaProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'role' | 'tabIndex'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes for the outer frame. */\n className?: string;\n /** Inline styles for the outer frame. */\n style?: React.CSSProperties;\n /** What scrolls. */\n children?: React.ReactNode;\n /**\n * The area's height. A number is pixels, and a string is any CSS length\n * (`\"20rem\"`, `\"50vh\"`, `\"100%\"`).\n */\n height?: number | string;\n /**\n * The height it grows to with its content, then scrolls past. A number is\n * pixels. Use it in place of `height` for an area that's only as tall as\n * it needs to be.\n */\n maxHeight?: number | string;\n /**\n * Which way it scrolls. `vertical` and `horizontal` clip the other axis,\n * and `both` scrolls whichever overflows.\n */\n orientation?: ScrollAreaOrientation;\n /**\n * When the scrollbars show. `auto`: whenever the content overflows, the\n * way the system shows them. `hover`: colorless until the pointer is over\n * the area or focus is in it, which hides that the area scrolls until\n * then, so keep it for places where the layout makes that plain.\n * `always`: on every axis it scrolls, even when the content fits. Where the\n * system draws its bars over the content (macOS by default, phones and\n * tablets), they still show only while you scroll, and in forced colors\n * they always show.\n */\n showScrollbars?: ScrollAreaScrollbars;\n /**\n * Keeps room for the vertical scrollbar whether or not it shows, so the\n * content doesn't shift when it starts to overflow. It does nothing where\n * the system draws its bars over the content.\n */\n hasStableGutter?: boolean;\n /**\n * Fades the content out toward each edge that has more past it, without\n * fading the scrollbar. It's off in forced colors and when the user asks\n * for more contrast.\n */\n hasFade?: boolean;\n /**\n * Called with the edge when scrolling reaches it, once each time it\n * arrives, such as to load older messages at the top. It isn't called for\n * the edge the area starts at, or when the content changes size.\n */\n onEdgeReach?: (edge: ScrollAreaEdge) => void;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** What a measurement of the viewport found. */\ninterface Measured {\n /** Overflows on an axis it scrolls. */\n overflowing: boolean;\n /** Each edge that has more content past it. */\n edges: Record<ScrollAreaEdge, boolean>;\n /** The vertical scrollbar's width, and the horizontal one's height. */\n barInline: number;\n barBlock: number;\n}\n\nconst sameMeasure = (a: Measured | null, b: Measured): boolean =>\n !!a &&\n a.overflowing === b.overflowing &&\n a.barInline === b.barInline &&\n a.barBlock === b.barBlock &&\n EDGES.every(edge => a.edges[edge] === b.edges[edge]);\n\n/**\n * The `ScrollArea` component renders a box whose content scrolls inside it, on the browser's own scrollbars in Bulma's colors.\n *\n * The bars are the system's, made thin and colored from Bulma's tokens, so\n * they follow dark mode and keep the system's own settings. While the\n * content overflows, the viewport is a region you can tab to and scroll by\n * keyboard, Safari included, named by `aria-label` or `aria-labelledby`.\n * While it fits, it's a plain box. Setting the bars' colors turns off any\n * `::-webkit-scrollbar` styles inside it.\n *\n * @function\n * @param {ScrollAreaProps} props - Props for the ScrollArea component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the viewport.\n * @returns {JSX.Element} The rendered scroll area.\n *\n * @example\n * <ScrollArea height={240} aria-label=\"Release notes\">\n * <Content>…</Content>\n * </ScrollArea>\n *\n * @example\n * <ScrollArea maxHeight=\"20rem\" aria-label=\"Messages\" hasFade>\n * …\n * </ScrollArea>\n */\nexport const ScrollArea = forwardRef<HTMLDivElement, ScrollAreaProps>(\n (\n {\n className,\n style,\n textColor,\n bgColor,\n height,\n maxHeight,\n orientation = 'both',\n showScrollbars = 'auto',\n hasStableGutter,\n hasFade,\n onEdgeReach,\n onScroll,\n onFocus,\n onBlur,\n children,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n\n const frameClass = usePrefixedClassNames('scroll-area', {\n 'is-vertical': orientation === 'vertical',\n 'is-horizontal': orientation === 'horizontal',\n 'has-hover-scrollbars': showScrollbars === 'hover',\n 'has-visible-scrollbars': showScrollbars === 'always',\n 'has-stable-gutter': !!hasStableGutter,\n 'has-fade': !!hasFade,\n });\n const viewportClass = usePrefixedClassNames('scroll-area-viewport');\n\n const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'ScrollArea:unnamed',\n '[bestax-bulma] <ScrollArea> has no accessible name. Pass ' +\n 'aria-label or aria-labelledby, so a screen reader can say what ' +\n 'took focus when the area overflows.'\n );\n }\n }, [named]);\n\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLDivElement | null) => {\n viewportRef.current = node;\n if (typeof ref === 'function') return ref(node);\n if (ref) ref.current = node;\n },\n [ref]\n );\n\n // Nothing is known about the overflow until the client measures it, so\n // the server renders a plain scroller and hydrates as one.\n const [measured, setMeasured] = useState<Measured | null>(null);\n const [focused, setFocused] = useState(false);\n // The last measurement, read by the scroll handler to see which edges\n // it just reached, and the direction and bar sizes, which only change\n // with layout, so a scroll doesn't read computed styles.\n const lastRef = useRef<Measured | null>(null);\n const layoutRef = useRef({ rtl: false, barInline: 0, barBlock: 0 });\n const onEdgeReachRef = useLatestRef(onEdgeReach);\n\n const measure = useCallback(\n (fromScroll: boolean) => {\n const el = viewportRef.current;\n /* istanbul ignore next: measuring only runs while it's mounted */\n if (!el) return;\n const scrollsY = orientation !== 'horizontal';\n const scrollsX = orientation !== 'vertical';\n const maxTop = el.scrollHeight - el.clientHeight;\n const maxLeft = el.scrollWidth - el.clientWidth;\n const overflowY = scrollsY && maxTop > SLACK;\n const overflowX = scrollsX && maxLeft > SLACK;\n // Right to left, scrollLeft runs from 0 at the start to negative.\n const fromStart = layoutRef.current.rtl\n ? -el.scrollLeft\n : el.scrollLeft;\n const next: Measured = {\n overflowing: overflowY || overflowX,\n edges: {\n top: overflowY && el.scrollTop > SLACK,\n bottom: overflowY && maxTop - el.scrollTop > SLACK,\n start: overflowX && fromStart > SLACK,\n end: overflowX && maxLeft - fromStart > SLACK,\n },\n barInline: layoutRef.current.barInline,\n barBlock: layoutRef.current.barBlock,\n };\n const last = lastRef.current;\n lastRef.current = next;\n if (fromScroll && last) {\n for (const edge of EDGES) {\n if (last.edges[edge] && !next.edges[edge]) {\n onEdgeReachRef.current?.(edge);\n }\n }\n }\n setMeasured(prev => (sameMeasure(prev, next) ? prev : next));\n },\n [orientation, onEdgeReachRef]\n );\n\n // Layout: the direction and the bars' sizes, then the overflow. It runs\n // before paint, and again whenever the viewport or anything in it\n // changes size, or the children change.\n useClientLayoutEffect(() => {\n const el = viewportRef.current;\n /* istanbul ignore next: the effect only runs once it's mounted */\n if (!el) return;\n const relayout = () => {\n const cs = getComputedStyle(el);\n const px = (value: string) => parseFloat(value) || 0;\n layoutRef.current = {\n rtl: cs.direction === 'rtl',\n barInline: Math.max(\n 0,\n el.offsetWidth -\n el.clientWidth -\n px(cs.borderLeftWidth) -\n px(cs.borderRightWidth)\n ),\n barBlock: Math.max(\n 0,\n el.offsetHeight -\n el.clientHeight -\n px(cs.borderTopWidth) -\n px(cs.borderBottomWidth)\n ),\n };\n measure(false);\n };\n relayout();\n if (typeof ResizeObserver === 'undefined') return;\n const resizes = new ResizeObserver(relayout);\n const observeAll = () => {\n resizes.disconnect();\n resizes.observe(el);\n for (const child of Array.from(el.children)) resizes.observe(child);\n };\n observeAll();\n // Content that changes can overflow before any size is reported: a\n // child added or taken away, or text React rewrites in place, which\n // resizes no element when the area's height is fixed, and inline\n // children, which a ResizeObserver doesn't report at all.\n const mutations =\n typeof MutationObserver === 'undefined'\n ? null\n : new MutationObserver(records => {\n if (\n records.some(\n record => record.target === el && record.type === 'childList'\n )\n ) {\n observeAll();\n }\n relayout();\n });\n mutations?.observe(el, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n return () => {\n resizes.disconnect();\n mutations?.disconnect();\n };\n }, [measure]);\n\n const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {\n onScroll?.(event);\n measure(true);\n };\n const handleFocus = (event: React.FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n if (event.target === event.currentTarget) setFocused(true);\n };\n const handleBlur = (event: React.FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n if (event.target === event.currentTarget) setFocused(false);\n };\n\n // A tab stop while it overflows. Taking tabIndex off the focused element\n // would drop focus to the page, so a viewport that stops overflowing\n // keeps it until focus leaves.\n const isRegion = !!measured?.overflowing || focused;\n // The name goes with the role: ARIA prohibits naming a plain div.\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n ...viewportRest\n } = rest;\n const edgeAttributes: Record<string, string> = {};\n if (measured) {\n for (const edge of EDGES) {\n if (measured.edges[edge]) edgeAttributes[`data-overflow-${edge}`] = '';\n }\n }\n const viewportStyle = measured\n ? ({\n '--scroll-area-bar-inline-size': `${measured.barInline}px`,\n '--scroll-area-bar-block-size': `${measured.barBlock}px`,\n } as React.CSSProperties)\n : undefined;\n\n return (\n <div\n className={classNames(frameClass, bulmaHelperClasses, className)}\n style={\n height !== undefined || maxHeight !== undefined\n ? {\n ...style,\n // Only what's given, so `maxHeight` alone keeps a height\n // the style sets.\n ...(height !== undefined && { height }),\n ...(maxHeight !== undefined && { maxHeight }),\n }\n : style\n }\n >\n <div\n ref={setRefs}\n className={viewportClass}\n style={viewportStyle}\n {...viewportRest}\n {...edgeAttributes}\n role={isRegion ? 'region' : undefined}\n aria-label={isRegion ? ariaLabel : undefined}\n aria-labelledby={isRegion ? ariaLabelledby : undefined}\n tabIndex={isRegion ? 0 : undefined}\n onScroll={handleScroll}\n onFocus={handleFocus}\n onBlur={handleBlur}\n >\n {children}\n </div>\n </div>\n );\n }\n);\n\nScrollArea.displayName = 'ScrollArea';\n\nexport default ScrollArea;\n"],"names":["SLACK","_jsx"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
2
+ 'use client';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { forwardRef, useId, useRef, useCallback, useEffect } from 'react';
5
+ import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
6
+ import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
7
+ import { useScrollSpy } from '../helpers/useScrollSpy.js';
8
+ import { MenuLabel, MenuList, MenuItem } from './Menu.js';
9
+
10
+ /** Every id in the tree, in page order. */
11
+ const idsOf = (items) => items.flatMap(item => [item.id, ...idsOf(item.children ?? [])]);
12
+ /**
13
+ * The entries as `MenuItem` links, each entry's own entries in a nested
14
+ * `MenuList`, and the current one marked.
15
+ */
16
+ const entries = (items, active) => items.map(item => {
17
+ const current = item.id === active;
18
+ return (jsxs(MenuItem, { href: `#${encodeURIComponent(item.id)}`, active: current, "aria-current": current ? 'location' : undefined, children: [item.label, item.children?.length ? (jsx(MenuList, { children: entries(item.children, active) })) : null] }, item.id));
19
+ });
20
+ /**
21
+ * The `TableOfContents` component lists a long page's sections as links in Bulma's menu look, and marks the one being read.
22
+ *
23
+ * It's a `<nav>` named by its title, holding `MenuList` and `MenuItem` links
24
+ * to each `#id`, rendered in full on the server. `useScrollSpy` picks the
25
+ * current section, and its link alone gets `is-active` and
26
+ * `aria-current="location"`. Use the hook on its own to mark the section in
27
+ * view somewhere else, such as an app's nav `Menu`.
28
+ *
29
+ * @function
30
+ * @param {TableOfContentsProps} props - Props for the TableOfContents component.
31
+ * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<nav>`.
32
+ * @returns {JSX.Element} The rendered table of contents.
33
+ *
34
+ * @example
35
+ * <TableOfContents
36
+ * isSticky
37
+ * items={[
38
+ * { id: 'install', label: 'Install' },
39
+ * { id: 'usage', label: 'Usage', children: [{ id: 'props', label: 'Props' }] },
40
+ * ]}
41
+ * />
42
+ */
43
+ const TableOfContents = forwardRef(({ className, textColor, bgColor, items, onCurrentChange, offset, isSticky, labels, ...props }, ref) => {
44
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
45
+ color: textColor,
46
+ backgroundColor: bgColor,
47
+ ...props,
48
+ });
49
+ const titleId = useId();
50
+ const active = useScrollSpy({ ids: idsOf(items), offset, onCurrentChange });
51
+ const navRef = useRef(null);
52
+ const setRefs = useCallback((node) => {
53
+ navRef.current = node;
54
+ if (typeof ref === 'function')
55
+ return ref(node);
56
+ if (ref)
57
+ ref.current = node;
58
+ }, [ref]);
59
+ // A list taller than the room it has scrolls itself to keep the current
60
+ // link in view. Only the list moves: scrollIntoView would move the page
61
+ // too whenever the link is off screen.
62
+ useEffect(() => {
63
+ const nav = navRef.current;
64
+ const link = nav.querySelector('a[aria-current="location"]');
65
+ if (!link || nav.scrollHeight <= nav.clientHeight)
66
+ return;
67
+ const top = nav.getBoundingClientRect().top + nav.clientTop;
68
+ const bottom = top + nav.clientHeight;
69
+ const at = link.getBoundingClientRect();
70
+ if (at.top < top)
71
+ nav.scrollTop -= top - at.top;
72
+ else if (at.bottom > bottom)
73
+ nav.scrollTop += at.bottom - bottom;
74
+ }, [active]);
75
+ // Bulma's menu, which draws it, and its own class beside it.
76
+ const menuClass = usePrefixedClassNames('menu');
77
+ const rootClass = usePrefixedClassNames('table-of-contents', {
78
+ 'is-sticky': !!isSticky,
79
+ });
80
+ return (jsxs("nav", { ref: setRefs, className: classNames(menuClass, rootClass, bulmaHelperClasses, className), "aria-labelledby": titleId, ...rest, children: [jsx(MenuLabel, { id: titleId, children: labels?.title || 'On this page' }), jsx(MenuList, { children: entries(items, active) })] }));
81
+ });
82
+ TableOfContents.displayName = 'TableOfContents';
83
+
84
+ export { TableOfContents, useScrollSpy };
85
+ //# sourceMappingURL=TableOfContents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableOfContents.js","sources":["../../../src/components/TableOfContents.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useRef,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useScrollSpy } from '../helpers/useScrollSpy';\nimport { MenuItem, MenuLabel, MenuList } from './Menu';\n\nexport { useScrollSpy } from '../helpers/useScrollSpy';\nexport type { UseScrollSpyOptions } from '../helpers/useScrollSpy';\n\n/** One entry in a TableOfContents. */\nexport interface TableOfContentsItem {\n /** **Required.** The `id` of the section or heading it links to, without the `#`. Unique on the page. */\n id: string;\n /** **Required.** What the link says, usually the heading's own text. */\n label: React.ReactNode;\n /** Entries under this one, drawn as a nested list. */\n children?: TableOfContentsItem[];\n}\n\n/** The visible strings, in your app's language. */\nexport interface TableOfContentsLabels {\n /**\n * The list's visible title, which also names its `<nav>`.\n * @defaultValue 'On this page'\n */\n title?: string;\n}\n\n/**\n * Props for the TableOfContents component. The helper props, the other\n * attributes and the `ref` land on the `<nav>`.\n *\n * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<nav>`.\n */\nexport interface TableOfContentsProps\n extends\n Omit<React.HTMLAttributes<HTMLElement>, 'color' | 'children'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * **Required.** The sections, in page order, as links to their ids. Each\n * link is a plain `<a href=\"#id\">`, so following one is the browser's own\n * jump: put `scroll-padding-top` on the page to land clear of a sticky\n * header, and smooth scrolling, if you want it, in page CSS inside\n * `@media (prefers-reduced-motion: no-preference)`.\n */\n items: TableOfContentsItem[];\n /**\n * Called with the current section's id when it changes, or `null` above\n * the first section. Never called on the server or before hydration.\n */\n onCurrentChange?: (id: string | null) => void;\n /**\n * Pixels from the viewport's top where a section counts as reached.\n * Unset, it's the page's `scroll-padding-top`, so the highlight agrees\n * with where a jump lands under a sticky header.\n */\n offset?: number;\n /**\n * Sticks to the top of the viewport as the page scrolls, at\n * `--bulma-table-of-contents-sticky-top`, and scrolls itself when it's\n * taller than the viewport. Adds `is-sticky`.\n */\n isSticky?: boolean;\n /** The visible strings, in your app's language. */\n labels?: TableOfContentsLabels;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** Every id in the tree, in page order. */\nconst idsOf = (items: TableOfContentsItem[]): string[] =>\n items.flatMap(item => [item.id, ...idsOf(item.children ?? [])]);\n\n/**\n * The entries as `MenuItem` links, each entry's own entries in a nested\n * `MenuList`, and the current one marked.\n */\nconst entries = (items: TableOfContentsItem[], active: string | null) =>\n items.map(item => {\n const current = item.id === active;\n return (\n <MenuItem\n key={item.id}\n href={`#${encodeURIComponent(item.id)}`}\n active={current}\n aria-current={current ? 'location' : undefined}\n >\n {item.label}\n {item.children?.length ? (\n <MenuList>{entries(item.children, active)}</MenuList>\n ) : null}\n </MenuItem>\n );\n });\n\n/**\n * The `TableOfContents` component lists a long page's sections as links in Bulma's menu look, and marks the one being read.\n *\n * It's a `<nav>` named by its title, holding `MenuList` and `MenuItem` links\n * to each `#id`, rendered in full on the server. `useScrollSpy` picks the\n * current section, and its link alone gets `is-active` and\n * `aria-current=\"location\"`. Use the hook on its own to mark the section in\n * view somewhere else, such as an app's nav `Menu`.\n *\n * @function\n * @param {TableOfContentsProps} props - Props for the TableOfContents component.\n * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<nav>`.\n * @returns {JSX.Element} The rendered table of contents.\n *\n * @example\n * <TableOfContents\n * isSticky\n * items={[\n * { id: 'install', label: 'Install' },\n * { id: 'usage', label: 'Usage', children: [{ id: 'props', label: 'Props' }] },\n * ]}\n * />\n */\nexport const TableOfContents = forwardRef<HTMLElement, TableOfContentsProps>(\n (\n {\n className,\n textColor,\n bgColor,\n items,\n onCurrentChange,\n offset,\n isSticky,\n labels,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const titleId = useId();\n const active = useScrollSpy({ ids: idsOf(items), offset, onCurrentChange });\n\n const navRef = useRef<HTMLElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLElement | null) => {\n navRef.current = node;\n if (typeof ref === 'function') return ref(node);\n if (ref) ref.current = node;\n },\n [ref]\n );\n\n // A list taller than the room it has scrolls itself to keep the current\n // link in view. Only the list moves: scrollIntoView would move the page\n // too whenever the link is off screen.\n useEffect(() => {\n const nav = navRef.current!;\n const link = nav.querySelector('a[aria-current=\"location\"]');\n if (!link || nav.scrollHeight <= nav.clientHeight) return;\n const top = nav.getBoundingClientRect().top + nav.clientTop;\n const bottom = top + nav.clientHeight;\n const at = link.getBoundingClientRect();\n if (at.top < top) nav.scrollTop -= top - at.top;\n else if (at.bottom > bottom) nav.scrollTop += at.bottom - bottom;\n }, [active]);\n\n // Bulma's menu, which draws it, and its own class beside it.\n const menuClass = usePrefixedClassNames('menu');\n const rootClass = usePrefixedClassNames('table-of-contents', {\n 'is-sticky': !!isSticky,\n });\n\n return (\n <nav\n ref={setRefs}\n className={classNames(\n menuClass,\n rootClass,\n bulmaHelperClasses,\n className\n )}\n aria-labelledby={titleId}\n {...rest}\n >\n <MenuLabel id={titleId}>{labels?.title || 'On this page'}</MenuLabel>\n <MenuList>{entries(items, active)}</MenuList>\n </nav>\n );\n }\n);\n\nTableOfContents.displayName = 'TableOfContents';\n\nexport default TableOfContents;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -8,6 +8,7 @@ import { flattenChildren } from '../helpers/flattenChildren.js';
8
8
  import { visuallyHidden } from '../helpers/statusRegion.js';
9
9
  import { withSubComponents } from '../helpers/withSubComponents.js';
10
10
  import { Icon } from '../elements/Icon.js';
11
+ import { isGiven } from '../helpers/isGiven.js';
11
12
 
12
13
  const validTimelineColors = [
13
14
  'primary',
@@ -22,11 +23,6 @@ const validTimelineTitleElements = ['h2', 'h3', 'h4', 'h5', 'h6', 'p'];
22
23
  const TimelineItemContext = createContext({
23
24
  titleAs: 'h2',
24
25
  });
25
- /** Whether a prop was given something to render. */
26
- const isGiven$2 = (node) => node !== undefined &&
27
- node !== null &&
28
- typeof node !== 'boolean' &&
29
- node !== '';
30
26
  const isTimelineColor = (color) => !!color && validTimelineColors.includes(color);
31
27
  /**
32
28
  * One event: a marker on the rail, then its title, its time and anything else about it.
@@ -56,7 +52,7 @@ const TimelineItem = forwardRef(({ className, title, time, dateTime, icon, color
56
52
  [`is-${color}`]: isTimelineColor(color),
57
53
  });
58
54
  const markerClass = usePrefixedClassNames('timeline-marker', {
59
- 'is-icon': isGiven$2(icon),
55
+ 'is-icon': isGiven(icon),
60
56
  });
61
57
  const contentClass = usePrefixedClassNames('timeline-content');
62
58
  const titleClass = usePrefixedClassNames('timeline-title');
@@ -68,8 +64,8 @@ const TimelineItem = forwardRef(({ className, title, time, dateTime, icon, color
68
64
  const words = label ? (jsx("span", { style: visuallyHidden, children: ` ${label}` })) : null;
69
65
  return (jsxs("li", { ref: ref, className: classNames(itemClass, bulmaHelperClasses, className), ...rest, ...(state && {
70
66
  'aria-current': state === 'active' ? 'step' : undefined,
71
- }), children: [jsx("div", { className: markerClass, "aria-hidden": "true", children: isGiven$2(icon) &&
72
- (typeof icon === 'string' ? jsx(Icon, { name: icon }) : icon) }), jsxs("div", { className: contentClass, children: [isGiven$2(title) ? (jsxs(TitleTag, { className: titleClass, children: [title, words] })) : (words), isGiven$2(time) &&
67
+ }), children: [jsx("div", { className: markerClass, "aria-hidden": "true", children: isGiven(icon) &&
68
+ (typeof icon === 'string' ? jsx(Icon, { name: icon }) : icon) }), jsxs("div", { className: contentClass, children: [isGiven(title) ? (jsxs(TitleTag, { className: titleClass, children: [title, words] })) : (words), isGiven(time) &&
73
69
  (dateTime ? (jsx("time", { className: timeClass, dateTime: dateTime, children: time })) : (jsx("span", { className: timeClass, children: time }))), children] })] }));
74
70
  });
75
71
  TimelineItem.displayName = 'Timeline.Item';
@@ -143,7 +139,7 @@ const TimelineRoot = forwardRef(({ className, children, active, pending, align =
143
139
  // Each child gets its state through context rather than a clone, so an
144
140
  // app's own component around a Timeline.Item still gets it. Fragments
145
141
  // and arrays are opened up first, so each item inside one counts.
146
- return (jsxs("ol", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, role: "list", children: [flattenChildren(children).map((child, index) => (jsx(TimelineItemContext.Provider, { value: contextFor(stateAt(index)), children: child }, React.isValidElement(child) ? child.key : index))), isGiven$2(pending) && (jsx(TimelineItemContext.Provider, { value: contextFor('pending'), children: jsx(TimelineItem, { title: pending }) }))] }));
142
+ return (jsxs("ol", { ref: ref, className: classNames(rootClass, bulmaHelperClasses, className), ...rest, role: "list", children: [flattenChildren(children).map((child, index) => (jsx(TimelineItemContext.Provider, { value: contextFor(stateAt(index)), children: child }, React.isValidElement(child) ? child.key : index))), isGiven(pending) && (jsx(TimelineItemContext.Provider, { value: contextFor('pending'), children: jsx(TimelineItem, { title: pending }) }))] }));
147
143
  });
148
144
  const Timeline = withSubComponents(TimelineRoot, { Item: TimelineItem }, 'Timeline');
149
145
 
@@ -1 +1 @@
1
- {"version":3,"file":"Timeline.js","sources":["../../../src/components/Timeline.tsx"],"sourcesContent":["import React, { createContext, forwardRef, useContext } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { flattenChildren } from '../helpers/flattenChildren';\nimport { visuallyHidden } from '../helpers/statusRegion';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { Icon } from '../elements/Icon';\n\nconst validTimelineColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n/** The Bulma colors a marker takes. */\nexport type TimelineColor = (typeof validTimelineColors)[number];\n\nconst validTimelineTitleElements = ['h2', 'h3', 'h4', 'h5', 'h6', 'p'] as const;\n/** What each item's title renders as: a heading level, or `p` for a plain line. */\nexport type TimelineTitleElement = (typeof validTimelineTitleElements)[number];\n\n/** Where the rail goes. */\nexport type TimelineAlign = 'start' | 'end' | 'alternate';\n\n/** Where an item is in a progress timeline. */\ntype TimelineState = 'completed' | 'active' | 'upcoming' | 'pending';\n\n/**\n * What the root hands each of its items: the title's element, and in a\n * progress timeline, the item's state and the words read after its title.\n */\ninterface TimelineItemContextValue {\n titleAs: TimelineTitleElement;\n state?: TimelineState;\n label?: string;\n}\n\n// An item outside a Timeline is a record item with an h2 title.\nconst TimelineItemContext = createContext<TimelineItemContextValue>({\n titleAs: 'h2',\n});\n\n/** Whether a prop was given something to render. */\nconst isGiven = (node: React.ReactNode) =>\n node !== undefined &&\n node !== null &&\n typeof node !== 'boolean' &&\n node !== '';\n\nconst isTimelineColor = (color?: string): color is TimelineColor =>\n !!color && (validTimelineColors as readonly string[]).includes(color);\n\n/**\n * Props for the Timeline component. The helper props, the other attributes\n * and the `ref` land on the `<ol>`.\n *\n * @extraProp {React.Ref<HTMLOListElement>} [ref] - Ref forwarded to the `<ol>`.\n */\nexport interface TimelineProps\n extends\n Omit<React.OlHTMLAttributes<HTMLOListElement>, 'color' | 'role'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * `Timeline.Item`s, in the order they're read: newest first or oldest\n * first for a record, oldest first with `active` or `pending`.\n */\n children?: React.ReactNode;\n /**\n * Marks progress. The item at this index, counting the children from 0, is\n * current; the ones before it are completed and the ones after it are\n * upcoming. Leave it out for a record, where nothing is marked. `-1` marks\n * every item upcoming, and an index past the last marks them all completed.\n * A fraction counts as the item it has reached, so `2.5` marks the third\n * item current. Each child counts as one item, with Fragments and arrays opened up, so\n * render one item per child. A component of your own around a\n * `Timeline.Item` still gets its state, but one that renders nothing still\n * counts.\n */\n active?: number;\n /**\n * What's under way, as a last item after the children: its title, read\n * with `pendingLabel`. Write it as words that say so (\"Out for delivery\").\n */\n pending?: React.ReactNode;\n /**\n * `start`: the rail at the start of the line. `end`: at the end, with the\n * text against it. `alternate`: down the middle, the items taking turns and\n * each time across the rail; below Bulma's tablet breakpoint it's `start`.\n */\n align?: TimelineAlign;\n /**\n * What every item's title renders as: a heading at the level under the\n * heading the timeline sits beneath, or `p` for a busy feed.\n */\n titleAs?: TimelineTitleElement;\n /**\n * The markers' color, and the color of progress with `active`. Neutral\n * without it, and Bulma's `link` for progress. The markers are hidden from\n * assistive tech, so say what a color means in the words too.\n */\n color?: TimelineColor;\n /** Read after a completed item's title, visually hidden. In your app's language. */\n completedLabel?: string;\n /** Read after an upcoming item's title, visually hidden. In your app's language. */\n upcomingLabel?: string;\n /** Read after the pending item's title, visually hidden. In your app's language. */\n pendingLabel?: string;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * Props for Timeline.Item, one event. The helper props, the other attributes\n * and the `ref` land on its `<li>`. The native `title` attribute isn't one of\n * them, since `title` here is the event's title.\n *\n * @extraProp {React.Ref<HTMLLIElement>} [ref] - Ref forwarded to the `<li>`.\n */\nexport interface TimelineItemProps\n extends\n Omit<React.LiHTMLAttributes<HTMLLIElement>, 'color' | 'title'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /** What happened, in a line. It renders as the root's `titleAs`. */\n title?: React.ReactNode;\n /**\n * When it happened, as the reader sees it: \"14 October 2026\", \"2 hours\n * ago\". Put the whole date in the words, since screen readers mostly read\n * the text.\n */\n time?: React.ReactNode;\n /**\n * The same moment for machines, as HTML's `datetime` takes it:\n * \"2026-10-14\", \"2026-10-14T08:12\", \"2026-10\". With it, `time` renders in\n * a `<time>`. Keep it the same moment as `time`, since some readers speak it.\n */\n dateTime?: string;\n /**\n * A picture in the marker. A string is an `Icon` name, drawn from the icon\n * library `ConfigProvider` sets, and anything else renders as it is.\n * Without it, the marker is a dot. It's hidden from assistive tech, so\n * what it means goes in the title.\n */\n icon?: React.ReactNode;\n /** This item's marker color, over the root's. Say what it means in the words too. */\n color?: TimelineColor;\n /** Anything else about the event, under the title and the time, as written. */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * One event: a marker on the rail, then its title, its time and anything else about it.\n *\n * Outside a `Timeline` it's a record item with an `h2` title, though the\n * styles are registered on `.timeline`, so the rail and the marker need one.\n *\n * @function\n * @param {TimelineItemProps} props - Props for Timeline.Item.\n * @param {React.Ref<HTMLLIElement>} ref - Forwarded ref to the `<li>`.\n * @returns {JSX.Element} The rendered event.\n *\n * @example\n * <Timeline.Item title=\"Shipped\" time=\"14 October 2026\" dateTime=\"2026-10-14\">\n * Left the warehouse in Leeds.\n * </Timeline.Item>\n */\nexport const TimelineItem = forwardRef<HTMLLIElement, TimelineItemProps>(\n (\n {\n className,\n title,\n time,\n dateTime,\n icon,\n color,\n textColor,\n bgColor,\n children,\n ...props\n },\n ref\n ) => {\n const { titleAs: TitleTag, state, label } = useContext(TimelineItemContext);\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const itemClass = usePrefixedClassNames('timeline-item', {\n [`is-${state}`]: !!state,\n [`is-${color}`]: isTimelineColor(color),\n });\n const markerClass = usePrefixedClassNames('timeline-marker', {\n 'is-icon': isGiven(icon),\n });\n const contentClass = usePrefixedClassNames('timeline-content');\n const titleClass = usePrefixedClassNames('timeline-title');\n const timeClass = usePrefixedClassNames('timeline-time');\n\n // The state in words, read after the title so it's in the heading too:\n // \"Packed completed\". The current item has none, since aria-current\n // says it. The styles are inline, so the words stay hidden in a modular\n // build that leaves out Bulma's helper classes.\n const words = label ? (\n <span style={visuallyHidden}>{` ${label}`}</span>\n ) : null;\n\n return (\n <li\n ref={ref}\n className={classNames(itemClass, bulmaHelperClasses, className)}\n {...rest}\n // In a progress timeline the state decides, whatever was passed.\n {...(state && {\n 'aria-current': state === 'active' ? 'step' : undefined,\n })}\n >\n {/* Decoration, hidden with whatever is in it, an Icon's own label\n included: the words say what a color, an icon or a state means. */}\n <div className={markerClass} aria-hidden=\"true\">\n {isGiven(icon) &&\n (typeof icon === 'string' ? <Icon name={icon} /> : icon)}\n </div>\n <div className={contentClass}>\n {isGiven(title) ? (\n <TitleTag className={titleClass}>\n {title}\n {words}\n </TitleTag>\n ) : (\n words\n )}\n {isGiven(time) &&\n (dateTime ? (\n <time className={timeClass} dateTime={dateTime}>\n {time}\n </time>\n ) : (\n <span className={timeClass}>{time}</span>\n ))}\n {children}\n </div>\n </li>\n );\n }\n);\n\nTimelineItem.displayName = 'Timeline.Item';\n\n/**\n * The `Timeline` component renders a list of events in the order they happened, each with a marker on a line down the side.\n *\n * By default it's a record, an order's history, a ticket's activity or a\n * changelog, and it marks nothing. With `active` it marks progress, for\n * something that moves along on its own and is reported to the user: a\n * parcel, a deployment, a claim. That reads oldest first, and `pending` ends\n * it on what's under way. A journey the user moves through themselves, like\n * a wizard or a checkout, is `Steps`.\n *\n * It's an `<ol role=\"list\">`, so it stays a list in Safari with its numbers\n * hidden, and each title is a heading at the level `titleAs` sets. The\n * markers are decoration and hidden from assistive tech, so the states are\n * read as words: `aria-current=\"step\"` on the current item, and \"completed\",\n * \"upcoming\" or \"in progress\" after the other titles, in the language the\n * label props set. Nothing here is a control or a live region, so a timeline\n * that changes on screen keeps a `role=\"status\"` region beside it.\n *\n * @function\n * @param {TimelineProps} props - Props for the Timeline component.\n * @param {React.Ref<HTMLOListElement>} ref - Forwarded ref to the `<ol>`.\n * @returns {JSX.Element} The rendered timeline.\n *\n * @example\n * <Timeline titleAs=\"h3\">\n * <Timeline.Item title=\"Delivered\" time=\"16 October 2026\" dateTime=\"2026-10-16\" />\n * <Timeline.Item title=\"Order placed\" time=\"12 October 2026\" dateTime=\"2026-10-12\" />\n * </Timeline>\n *\n * @example\n * <Timeline titleAs=\"h3\" active={1} pending=\"Out for delivery\">\n * <Timeline.Item title=\"Order placed\" />\n * <Timeline.Item title=\"Shipped\" />\n * </Timeline>\n */\nconst TimelineRoot = forwardRef<HTMLOListElement, TimelineProps>(\n (\n {\n className,\n children,\n active,\n pending,\n align = 'start',\n titleAs = 'h2',\n color,\n completedLabel = 'completed',\n upcomingLabel = 'upcoming',\n pendingLabel = 'in progress',\n textColor,\n bgColor,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const rootClass = usePrefixedClassNames('timeline', {\n 'is-end': align === 'end',\n 'is-alternate': align === 'alternate',\n [`is-${color}`]: isTimelineColor(color),\n });\n const level = validTimelineTitleElements.includes(titleAs) ? titleAs : 'h2';\n const labels: Record<TimelineState, string | undefined> = {\n completed: completedLabel,\n active: undefined,\n upcoming: upcomingLabel,\n pending: pendingLabel,\n };\n const contextFor = (state?: TimelineState) => ({\n titleAs: level,\n state,\n label: state && labels[state],\n });\n // A fraction counts as the item it has reached, so one item is current.\n const reached = typeof active === 'number' ? Math.floor(active) : undefined;\n const stateAt = (index: number): TimelineState | undefined => {\n if (reached === undefined) return undefined;\n if (index === reached) return 'active';\n return index < reached ? 'completed' : 'upcoming';\n };\n\n // Each child gets its state through context rather than a clone, so an\n // app's own component around a Timeline.Item still gets it. Fragments\n // and arrays are opened up first, so each item inside one counts.\n return (\n <ol\n ref={ref}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n {...rest}\n role=\"list\"\n >\n {flattenChildren(children).map((child, index) => (\n <TimelineItemContext.Provider\n key={React.isValidElement(child) ? child.key : index}\n value={contextFor(stateAt(index))}\n >\n {child}\n </TimelineItemContext.Provider>\n ))}\n {isGiven(pending) && (\n <TimelineItemContext.Provider value={contextFor('pending')}>\n <TimelineItem title={pending} />\n </TimelineItemContext.Provider>\n )}\n </ol>\n );\n }\n);\n\nexport const Timeline = withSubComponents(\n TimelineRoot,\n { Item: TimelineItem },\n 'Timeline'\n);\n\nexport default Timeline;\n"],"names":["isGiven","_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"Timeline.js","sources":["../../../src/components/Timeline.tsx"],"sourcesContent":["import React, { createContext, forwardRef, useContext } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { flattenChildren } from '../helpers/flattenChildren';\nimport { visuallyHidden } from '../helpers/statusRegion';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { Icon } from '../elements/Icon';\nimport { isGiven } from '../helpers/isGiven';\n\nconst validTimelineColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n/** The Bulma colors a marker takes. */\nexport type TimelineColor = (typeof validTimelineColors)[number];\n\nconst validTimelineTitleElements = ['h2', 'h3', 'h4', 'h5', 'h6', 'p'] as const;\n/** What each item's title renders as: a heading level, or `p` for a plain line. */\nexport type TimelineTitleElement = (typeof validTimelineTitleElements)[number];\n\n/** Where the rail goes. */\nexport type TimelineAlign = 'start' | 'end' | 'alternate';\n\n/** Where an item is in a progress timeline. */\ntype TimelineState = 'completed' | 'active' | 'upcoming' | 'pending';\n\n/**\n * What the root hands each of its items: the title's element, and in a\n * progress timeline, the item's state and the words read after its title.\n */\ninterface TimelineItemContextValue {\n titleAs: TimelineTitleElement;\n state?: TimelineState;\n label?: string;\n}\n\n// An item outside a Timeline is a record item with an h2 title.\nconst TimelineItemContext = createContext<TimelineItemContextValue>({\n titleAs: 'h2',\n});\n\nconst isTimelineColor = (color?: string): color is TimelineColor =>\n !!color && (validTimelineColors as readonly string[]).includes(color);\n\n/**\n * Props for the Timeline component. The helper props, the other attributes\n * and the `ref` land on the `<ol>`.\n *\n * @extraProp {React.Ref<HTMLOListElement>} [ref] - Ref forwarded to the `<ol>`.\n */\nexport interface TimelineProps\n extends\n Omit<React.OlHTMLAttributes<HTMLOListElement>, 'color' | 'role'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * `Timeline.Item`s, in the order they're read: newest first or oldest\n * first for a record, oldest first with `active` or `pending`.\n */\n children?: React.ReactNode;\n /**\n * Marks progress. The item at this index, counting the children from 0, is\n * current; the ones before it are completed and the ones after it are\n * upcoming. Leave it out for a record, where nothing is marked. `-1` marks\n * every item upcoming, and an index past the last marks them all completed.\n * A fraction counts as the item it has reached, so `2.5` marks the third\n * item current. Each child counts as one item, with Fragments and arrays opened up, so\n * render one item per child. A component of your own around a\n * `Timeline.Item` still gets its state, but one that renders nothing still\n * counts.\n */\n active?: number;\n /**\n * What's under way, as a last item after the children: its title, read\n * with `pendingLabel`. Write it as words that say so (\"Out for delivery\").\n */\n pending?: React.ReactNode;\n /**\n * `start`: the rail at the start of the line. `end`: at the end, with the\n * text against it. `alternate`: down the middle, the items taking turns and\n * each time across the rail; below Bulma's tablet breakpoint it's `start`.\n */\n align?: TimelineAlign;\n /**\n * What every item's title renders as: a heading at the level under the\n * heading the timeline sits beneath, or `p` for a busy feed.\n */\n titleAs?: TimelineTitleElement;\n /**\n * The markers' color, and the color of progress with `active`. Neutral\n * without it, and Bulma's `link` for progress. The markers are hidden from\n * assistive tech, so say what a color means in the words too.\n */\n color?: TimelineColor;\n /** Read after a completed item's title, visually hidden. In your app's language. */\n completedLabel?: string;\n /** Read after an upcoming item's title, visually hidden. In your app's language. */\n upcomingLabel?: string;\n /** Read after the pending item's title, visually hidden. In your app's language. */\n pendingLabel?: string;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * Props for Timeline.Item, one event. The helper props, the other attributes\n * and the `ref` land on its `<li>`. The native `title` attribute isn't one of\n * them, since `title` here is the event's title.\n *\n * @extraProp {React.Ref<HTMLLIElement>} [ref] - Ref forwarded to the `<li>`.\n */\nexport interface TimelineItemProps\n extends\n Omit<React.LiHTMLAttributes<HTMLLIElement>, 'color' | 'title'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /** What happened, in a line. It renders as the root's `titleAs`. */\n title?: React.ReactNode;\n /**\n * When it happened, as the reader sees it: \"14 October 2026\", \"2 hours\n * ago\". Put the whole date in the words, since screen readers mostly read\n * the text.\n */\n time?: React.ReactNode;\n /**\n * The same moment for machines, as HTML's `datetime` takes it:\n * \"2026-10-14\", \"2026-10-14T08:12\", \"2026-10\". With it, `time` renders in\n * a `<time>`. Keep it the same moment as `time`, since some readers speak it.\n */\n dateTime?: string;\n /**\n * A picture in the marker. A string is an `Icon` name, drawn from the icon\n * library `ConfigProvider` sets, and anything else renders as it is.\n * Without it, the marker is a dot. It's hidden from assistive tech, so\n * what it means goes in the title.\n */\n icon?: React.ReactNode;\n /** This item's marker color, over the root's. Say what it means in the words too. */\n color?: TimelineColor;\n /** Anything else about the event, under the title and the time, as written. */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * One event: a marker on the rail, then its title, its time and anything else about it.\n *\n * Outside a `Timeline` it's a record item with an `h2` title, though the\n * styles are registered on `.timeline`, so the rail and the marker need one.\n *\n * @function\n * @param {TimelineItemProps} props - Props for Timeline.Item.\n * @param {React.Ref<HTMLLIElement>} ref - Forwarded ref to the `<li>`.\n * @returns {JSX.Element} The rendered event.\n *\n * @example\n * <Timeline.Item title=\"Shipped\" time=\"14 October 2026\" dateTime=\"2026-10-14\">\n * Left the warehouse in Leeds.\n * </Timeline.Item>\n */\nexport const TimelineItem = forwardRef<HTMLLIElement, TimelineItemProps>(\n (\n {\n className,\n title,\n time,\n dateTime,\n icon,\n color,\n textColor,\n bgColor,\n children,\n ...props\n },\n ref\n ) => {\n const { titleAs: TitleTag, state, label } = useContext(TimelineItemContext);\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const itemClass = usePrefixedClassNames('timeline-item', {\n [`is-${state}`]: !!state,\n [`is-${color}`]: isTimelineColor(color),\n });\n const markerClass = usePrefixedClassNames('timeline-marker', {\n 'is-icon': isGiven(icon),\n });\n const contentClass = usePrefixedClassNames('timeline-content');\n const titleClass = usePrefixedClassNames('timeline-title');\n const timeClass = usePrefixedClassNames('timeline-time');\n\n // The state in words, read after the title so it's in the heading too:\n // \"Packed completed\". The current item has none, since aria-current\n // says it. The styles are inline, so the words stay hidden in a modular\n // build that leaves out Bulma's helper classes.\n const words = label ? (\n <span style={visuallyHidden}>{` ${label}`}</span>\n ) : null;\n\n return (\n <li\n ref={ref}\n className={classNames(itemClass, bulmaHelperClasses, className)}\n {...rest}\n // In a progress timeline the state decides, whatever was passed.\n {...(state && {\n 'aria-current': state === 'active' ? 'step' : undefined,\n })}\n >\n {/* Decoration, hidden with whatever is in it, an Icon's own label\n included: the words say what a color, an icon or a state means. */}\n <div className={markerClass} aria-hidden=\"true\">\n {isGiven(icon) &&\n (typeof icon === 'string' ? <Icon name={icon} /> : icon)}\n </div>\n <div className={contentClass}>\n {isGiven(title) ? (\n <TitleTag className={titleClass}>\n {title}\n {words}\n </TitleTag>\n ) : (\n words\n )}\n {isGiven(time) &&\n (dateTime ? (\n <time className={timeClass} dateTime={dateTime}>\n {time}\n </time>\n ) : (\n <span className={timeClass}>{time}</span>\n ))}\n {children}\n </div>\n </li>\n );\n }\n);\n\nTimelineItem.displayName = 'Timeline.Item';\n\n/**\n * The `Timeline` component renders a list of events in the order they happened, each with a marker on a line down the side.\n *\n * By default it's a record, an order's history, a ticket's activity or a\n * changelog, and it marks nothing. With `active` it marks progress, for\n * something that moves along on its own and is reported to the user: a\n * parcel, a deployment, a claim. That reads oldest first, and `pending` ends\n * it on what's under way. A journey the user moves through themselves, like\n * a wizard or a checkout, is `Steps`.\n *\n * It's an `<ol role=\"list\">`, so it stays a list in Safari with its numbers\n * hidden, and each title is a heading at the level `titleAs` sets. The\n * markers are decoration and hidden from assistive tech, so the states are\n * read as words: `aria-current=\"step\"` on the current item, and \"completed\",\n * \"upcoming\" or \"in progress\" after the other titles, in the language the\n * label props set. Nothing here is a control or a live region, so a timeline\n * that changes on screen keeps a `role=\"status\"` region beside it.\n *\n * @function\n * @param {TimelineProps} props - Props for the Timeline component.\n * @param {React.Ref<HTMLOListElement>} ref - Forwarded ref to the `<ol>`.\n * @returns {JSX.Element} The rendered timeline.\n *\n * @example\n * <Timeline titleAs=\"h3\">\n * <Timeline.Item title=\"Delivered\" time=\"16 October 2026\" dateTime=\"2026-10-16\" />\n * <Timeline.Item title=\"Order placed\" time=\"12 October 2026\" dateTime=\"2026-10-12\" />\n * </Timeline>\n *\n * @example\n * <Timeline titleAs=\"h3\" active={1} pending=\"Out for delivery\">\n * <Timeline.Item title=\"Order placed\" />\n * <Timeline.Item title=\"Shipped\" />\n * </Timeline>\n */\nconst TimelineRoot = forwardRef<HTMLOListElement, TimelineProps>(\n (\n {\n className,\n children,\n active,\n pending,\n align = 'start',\n titleAs = 'h2',\n color,\n completedLabel = 'completed',\n upcomingLabel = 'upcoming',\n pendingLabel = 'in progress',\n textColor,\n bgColor,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const rootClass = usePrefixedClassNames('timeline', {\n 'is-end': align === 'end',\n 'is-alternate': align === 'alternate',\n [`is-${color}`]: isTimelineColor(color),\n });\n const level = validTimelineTitleElements.includes(titleAs) ? titleAs : 'h2';\n const labels: Record<TimelineState, string | undefined> = {\n completed: completedLabel,\n active: undefined,\n upcoming: upcomingLabel,\n pending: pendingLabel,\n };\n const contextFor = (state?: TimelineState) => ({\n titleAs: level,\n state,\n label: state && labels[state],\n });\n // A fraction counts as the item it has reached, so one item is current.\n const reached = typeof active === 'number' ? Math.floor(active) : undefined;\n const stateAt = (index: number): TimelineState | undefined => {\n if (reached === undefined) return undefined;\n if (index === reached) return 'active';\n return index < reached ? 'completed' : 'upcoming';\n };\n\n // Each child gets its state through context rather than a clone, so an\n // app's own component around a Timeline.Item still gets it. Fragments\n // and arrays are opened up first, so each item inside one counts.\n return (\n <ol\n ref={ref}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n {...rest}\n role=\"list\"\n >\n {flattenChildren(children).map((child, index) => (\n <TimelineItemContext.Provider\n key={React.isValidElement(child) ? child.key : index}\n value={contextFor(stateAt(index))}\n >\n {child}\n </TimelineItemContext.Provider>\n ))}\n {isGiven(pending) && (\n <TimelineItemContext.Provider value={contextFor('pending')}>\n <TimelineItem title={pending} />\n </TimelineItemContext.Provider>\n )}\n </ol>\n );\n }\n);\n\nexport const Timeline = withSubComponents(\n TimelineRoot,\n { Item: TimelineItem },\n 'Timeline'\n);\n\nexport default Timeline;\n"],"names":["_jsx","_jsxs"],"mappings":";;;;;;;;;;;;AAaA,MAAM,mBAAmB,GAAG;IAC1B,SAAS;IACT,MAAM;IACN,MAAM;IACN,SAAS;IACT,SAAS;IACT,QAAQ;CACA;AAIV,MAAM,0BAA0B,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAU;AAoB/E;AACA,MAAM,mBAAmB,GAAG,aAAa,CAA2B;AAClE,IAAA,OAAO,EAAE,IAAI;AACd,CAAA,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,KAAc,KACrC,CAAC,CAAC,KAAK,IAAK,mBAAyC,CAAC,QAAQ,CAAC,KAAK,CAAC;AA6GvE;;;;;;;;;;;;;;;AAeG;AACI,MAAM,YAAY,GAAG,UAAU,CACpC,CACE,EACE,SAAS,EACT,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,SAAS,EACT,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,EACD,GAAG,KACD;AACF,IAAA,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,mBAAmB,CAAC;AAC3E,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AACT,KAAA,CAAC;AACF,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,eAAe,EAAE;AACvD,QAAA,CAAC,MAAM,KAAK,CAAA,CAAE,GAAG,CAAC,CAAC,KAAK;QACxB,CAAC,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,GAAG,eAAe,CAAC,KAAK,CAAC;AACxC,KAAA,CAAC;AACF,IAAA,MAAM,WAAW,GAAG,qBAAqB,CAAC,iBAAiB,EAAE;AAC3D,QAAA,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC;AACzB,KAAA,CAAC;AACF,IAAA,MAAM,YAAY,GAAG,qBAAqB,CAAC,kBAAkB,CAAC;AAC9D,IAAA,MAAM,UAAU,GAAG,qBAAqB,CAAC,gBAAgB,CAAC;AAC1D,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,eAAe,CAAC;;;;;IAMxD,MAAM,KAAK,GAAG,KAAK,IACjBA,cAAM,KAAK,EAAE,cAAc,EAAA,QAAA,EAAG,CAAA,CAAA,EAAI,KAAK,CAAA,CAAE,EAAA,CAAQ,IAC/C,IAAI;IAER,QACEC,aACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,UAAU,CAAC,SAAS,EAAE,kBAAkB,EAAE,SAAS,CAAC,KAC3D,IAAI,EAAA,IAEH,KAAK,IAAI;YACZ,cAAc,EAAE,KAAK,KAAK,QAAQ,GAAG,MAAM,GAAG,SAAS;SACxD,CAAC,EAAA,QAAA,EAAA,CAIFD,GAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,WAAW,EAAA,aAAA,EAAc,MAAM,EAAA,QAAA,EAC5C,OAAO,CAAC,IAAI,CAAC;qBACX,OAAO,IAAI,KAAK,QAAQ,GAAGA,GAAA,CAAC,IAAI,EAAA,EAAC,IAAI,EAAE,IAAI,GAAI,GAAG,IAAI,CAAC,EAAA,CACtD,EACNC,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,YAAY,EAAA,QAAA,EAAA,CACzB,OAAO,CAAC,KAAK,CAAC,IACbA,IAAA,CAAC,QAAQ,EAAA,EAAC,SAAS,EAAE,UAAU,EAAA,QAAA,EAAA,CAC5B,KAAK,EACL,KAAK,CAAA,EAAA,CACG,KAEX,KAAK,CACN,EACA,OAAO,CAAC,IAAI,CAAC;AACZ,yBAAC,QAAQ,IACPD,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAA,QAAA,EAC3C,IAAI,EAAA,CACA,KAEPA,GAAA,CAAA,MAAA,EAAA,EAAM,SAAS,EAAE,SAAS,EAAA,QAAA,EAAG,IAAI,EAAA,CAAQ,CAC1C,CAAC,EACH,QAAQ,CAAA,EAAA,CACL,CAAA,EAAA,CACH;AAET,CAAC;AAGH,YAAY,CAAC,WAAW,GAAG,eAAe;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCG;AACH,MAAM,YAAY,GAAG,UAAU,CAC7B,CACE,EACE,SAAS,EACT,QAAQ,EACR,MAAM,EACN,OAAO,EACP,KAAK,GAAG,OAAO,EACf,OAAO,GAAG,IAAI,EACd,KAAK,EACL,cAAc,GAAG,WAAW,EAC5B,aAAa,GAAG,UAAU,EAC1B,YAAY,GAAG,aAAa,EAC5B,SAAS,EACT,OAAO,EACP,GAAG,KAAK,EACT,EACD,GAAG,KACD;AACF,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AACT,KAAA,CAAC;AACF,IAAA,MAAM,SAAS,GAAG,qBAAqB,CAAC,UAAU,EAAE;QAClD,QAAQ,EAAE,KAAK,KAAK,KAAK;QACzB,cAAc,EAAE,KAAK,KAAK,WAAW;QACrC,CAAC,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,GAAG,eAAe,CAAC,KAAK,CAAC;AACxC,KAAA,CAAC;AACF,IAAA,MAAM,KAAK,GAAG,0BAA0B,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI;AAC3E,IAAA,MAAM,MAAM,GAA8C;AACxD,QAAA,SAAS,EAAE,cAAc;AACzB,QAAA,MAAM,EAAE,SAAS;AACjB,QAAA,QAAQ,EAAE,aAAa;AACvB,QAAA,OAAO,EAAE,YAAY;KACtB;AACD,IAAA,MAAM,UAAU,GAAG,CAAC,KAAqB,MAAM;AAC7C,QAAA,OAAO,EAAE,KAAK;QACd,KAAK;AACL,QAAA,KAAK,EAAE,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC;AAC9B,KAAA,CAAC;;AAEF,IAAA,MAAM,OAAO,GAAG,OAAO,MAAM,KAAK,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS;AAC3E,IAAA,MAAM,OAAO,GAAG,CAAC,KAAa,KAA+B;QAC3D,IAAI,OAAO,KAAK,SAAS;AAAE,YAAA,OAAO,SAAS;QAC3C,IAAI,KAAK,KAAK,OAAO;AAAE,YAAA,OAAO,QAAQ;QACtC,OAAO,KAAK,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU;AACnD,IAAA,CAAC;;;;IAKD,QACEC,aACE,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,UAAU,CAAC,SAAS,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAAA,GAC3D,IAAI,EACR,IAAI,EAAC,MAAM,EAAA,QAAA,EAAA,CAEV,eAAe,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,MAC1CD,IAAC,mBAAmB,CAAC,QAAQ,EAAA,EAE3B,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,YAEhC,KAAK,EAAA,EAHD,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAIvB,CAChC,CAAC,EACD,OAAO,CAAC,OAAO,CAAC,KACfA,GAAA,CAAC,mBAAmB,CAAC,QAAQ,IAAC,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,YACxDA,GAAA,CAAC,YAAY,EAAA,EAAC,KAAK,EAAE,OAAO,GAAI,EAAA,CACH,CAChC,CAAA,EAAA,CACE;AAET,CAAC,CACF;AAEM,MAAM,QAAQ,GAAG,iBAAiB,CACvC,YAAY,EACZ,EAAE,IAAI,EAAE,YAAY,EAAE,EACtB,UAAU;;"}
@@ -11,6 +11,7 @@ import { useTreeView, TreeViewHiddenContext } from '../helpers/useTreeView.js';
11
11
  import { StatusRegion, spokenText } from '../helpers/statusRegion.js';
12
12
  import { Checkbox } from '../form/Checkbox.js';
13
13
  import { Icon } from '../elements/Icon.js';
14
+ import { isGiven } from '../helpers/isGiven.js';
14
15
 
15
16
  const validTreeViewSizes = ['small', 'medium', 'large'];
16
17
  // Not exported, so they stay out of the public API that `index.ts` re-exports.
@@ -21,11 +22,6 @@ const TOP = { path: [], hidden: false };
21
22
  const BranchContext = createContext(TOP);
22
23
  /** The box's input lets the row's click toggle it, so a change on its own does nothing. */
23
24
  const ignoreChange$1 = () => { };
24
- /** Whether an `icon` was given something to render. */
25
- const isGiven$1 = (node) => node !== undefined &&
26
- node !== null &&
27
- typeof node !== 'boolean' &&
28
- node !== '';
29
25
  /**
30
26
  * The `TreeView` component renders a tree of items you browse and pick from, the way a file explorer works: one tab stop, the arrow keys to move, and Right and Left to open and close.
31
27
  *
@@ -144,7 +140,7 @@ const TreeViewItem = forwardRef(function TreeViewItem({ value, label, icon, chil
144
140
  ? null
145
141
  : isLoading
146
142
  ? 'loading'
147
- : isGiven$1(loadError)
143
+ : isGiven(loadError)
148
144
  ? 'error'
149
145
  : null;
150
146
  const labelRef = useRef(null);
@@ -253,7 +249,7 @@ const TreeViewItem = forwardRef(function TreeViewItem({ value, label, icon, chil
253
249
  if (!(event.target instanceof HTMLInputElement)) {
254
250
  event.preventDefault();
255
251
  }
256
- }, children: jsx(Checkbox, { tabIndex: -1, checked: checked === true, indeterminate: checked === 'mixed', disabled: disabled, onChange: ignoreChange$1 }) })), isGiven$1(icon) && (jsx("span", { className: iconClass, "aria-hidden": "true", children: typeof icon === 'string' ? jsx(Icon, { name: icon }) : icon })), jsx("span", { className: labelClass, id: labelId, ref: labelRef, children: label })] }), open && (hasItems || status) ? (jsxs("ul", { className: groupClass, role: "group", children: [items, status && (jsx(StatusRow, { kind: status, parent: value, level: branch.path.length + 2, message: loadError, onRetry: onRetry, labelRef: statusLabelRef }))] })) : (items)] }));
252
+ }, children: jsx(Checkbox, { tabIndex: -1, checked: checked === true, indeterminate: checked === 'mixed', disabled: disabled, onChange: ignoreChange$1 }) })), isGiven(icon) && (jsx("span", { className: iconClass, "aria-hidden": "true", children: typeof icon === 'string' ? jsx(Icon, { name: icon }) : icon })), jsx("span", { className: labelClass, id: labelId, ref: labelRef, children: label })] }), open && (hasItems || status) ? (jsxs("ul", { className: groupClass, role: "group", children: [items, status && (jsx(StatusRow, { kind: status, parent: value, level: branch.path.length + 2, message: loadError, onRetry: onRetry, labelRef: statusLabelRef }))] })) : (items)] }));
257
253
  });
258
254
  TreeViewItem.displayName = 'TreeView.Item';
259
255
  /**