@bitrise/bitkit-v2 0.3.502-beta.3240 → 0.3.502-beta.3242

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 (60) hide show
  1. package/AGENTS.md +3 -7
  2. package/dist/components/BitkitButton/BitkitButton.d.ts +0 -7
  3. package/dist/components/BitkitButton/BitkitButton.js +3 -12
  4. package/dist/components/BitkitButton/BitkitButton.js.map +1 -1
  5. package/dist/components/BitkitCalendar/components/CalendarMonth.js +1 -1
  6. package/dist/components/BitkitContentWrapper/BitkitContentWrapper.d.ts +16 -0
  7. package/dist/components/BitkitContentWrapper/BitkitContentWrapper.js +21 -0
  8. package/dist/components/BitkitContentWrapper/BitkitContentWrapper.js.map +1 -0
  9. package/dist/components/BitkitFilter/BitkitFilter.d.ts +4 -9
  10. package/dist/components/BitkitFilter/BitkitFilter.js +4 -6
  11. package/dist/components/BitkitFilter/BitkitFilter.js.map +1 -1
  12. package/dist/components/BitkitFilter/BitkitFilterAdd.js +1 -1
  13. package/dist/components/BitkitIconButton/BitkitIconButton.d.ts +0 -8
  14. package/dist/components/BitkitIconButton/BitkitIconButton.js +4 -7
  15. package/dist/components/BitkitIconButton/BitkitIconButton.js.map +1 -1
  16. package/dist/components/BitkitPageHeader/BitkitPageHeader.d.ts +6 -28
  17. package/dist/components/BitkitPageHeader/BitkitPageHeader.js +5 -3
  18. package/dist/components/BitkitPageHeader/BitkitPageHeader.js.map +1 -1
  19. package/dist/components/BitkitPageSubheader/BitkitPageSubheader.d.ts +18 -0
  20. package/dist/components/BitkitPageSubheader/BitkitPageSubheader.js +61 -0
  21. package/dist/components/BitkitPageSubheader/BitkitPageSubheader.js.map +1 -0
  22. package/dist/components/BitkitSectionHeading/BitkitSectionHeading.d.ts +2 -0
  23. package/dist/components/BitkitSectionHeading/BitkitSectionHeading.js +2 -1
  24. package/dist/components/BitkitSectionHeading/BitkitSectionHeading.js.map +1 -1
  25. package/dist/components/BitkitStepsCard/BitkitStepsCard.d.ts +2 -0
  26. package/dist/components/BitkitStepsCard/BitkitStepsCard.js +2 -1
  27. package/dist/components/BitkitStepsCard/BitkitStepsCard.js.map +1 -1
  28. package/dist/components/BitkitTooltip/BitkitTooltip.d.ts +3 -3
  29. package/dist/components/BitkitTooltip/BitkitTooltip.js.map +1 -1
  30. package/dist/components/index.d.ts +2 -0
  31. package/dist/main.js +4 -2
  32. package/dist/theme/common/ContentWidth.common.d.ts +20 -0
  33. package/dist/theme/common/ContentWidth.common.js +15 -0
  34. package/dist/theme/common/ContentWidth.common.js.map +1 -0
  35. package/dist/theme/recipes/ContentWrapper.recipe.d.ts +18 -0
  36. package/dist/theme/recipes/ContentWrapper.recipe.js +29 -0
  37. package/dist/theme/recipes/ContentWrapper.recipe.js.map +1 -0
  38. package/dist/theme/recipes/Separator.recipe.d.ts +8 -0
  39. package/dist/theme/recipes/Separator.recipe.js +5 -1
  40. package/dist/theme/recipes/Separator.recipe.js.map +1 -1
  41. package/dist/theme/recipes/index.js +2 -0
  42. package/dist/theme/recipes/index.js.map +1 -1
  43. package/dist/theme/slot-recipes/Filter.recipe.d.ts +0 -8
  44. package/dist/theme/slot-recipes/Filter.recipe.js +22 -28
  45. package/dist/theme/slot-recipes/Filter.recipe.js.map +1 -1
  46. package/dist/theme/slot-recipes/PageHeader.recipe.d.ts +10 -27
  47. package/dist/theme/slot-recipes/PageHeader.recipe.js +18 -22
  48. package/dist/theme/slot-recipes/PageHeader.recipe.js.map +1 -1
  49. package/dist/theme/slot-recipes/PageSubheader.recipe.d.ts +28 -0
  50. package/dist/theme/slot-recipes/PageSubheader.recipe.js +139 -0
  51. package/dist/theme/slot-recipes/PageSubheader.recipe.js.map +1 -0
  52. package/dist/theme/slot-recipes/StepCard.recipe.js +1 -1
  53. package/dist/theme/slot-recipes/StepCard.recipe.js.map +1 -1
  54. package/dist/theme/slot-recipes/index.d.ts +43 -19
  55. package/dist/theme/slot-recipes/index.js +2 -0
  56. package/dist/theme/slot-recipes/index.js.map +1 -1
  57. package/package.json +2 -1
  58. package/dist/utilities/focusableDisabledProps.d.ts +0 -22
  59. package/dist/utilities/focusableDisabledProps.js +0 -28
  60. package/dist/utilities/focusableDisabledProps.js.map +0 -1
package/AGENTS.md CHANGED
@@ -13,7 +13,7 @@ The mistakes that break things silently or come up in almost every review. Each
13
13
  3. **Don't override a Bitkit component's styling**, and don't pass an empty or placeholder value to satisfy a required prop. Layout props (`width`, `flex`, `margin*`, …) are fine. → _Building UI_
14
14
  4. **Never wrap a Bitkit component in `<Box>` for styling.** Put the style props on the component. → _Coding rules_
15
15
  5. **Only semantic colors.** No palette colors, `sys/*` colors, hex codes or color names. → _Coding rules_
16
- 6. **Sizes are tokens.** For a content width use a `layout/maxWidth/*` token; anything else above 128 uses `rem()`. A bare-number string like `"420"` is dropped. → _Coding rules_
16
+ 6. **Sizes are tokens.** A page's content width is the `contentWidth` of `BitkitContentWrapper` and the components above it; anything else above 128 uses `rem()`. A bare-number string like `"420"` is dropped. → _Coding rules_
17
17
  7. **Omit default prop values.** → _Coding rules_
18
18
 
19
19
  ## Before you start (instructions for the AI assistant)
@@ -81,7 +81,7 @@ import { BitkitProvider, BitkitButton } from '@bitrise/bitkit-v2';
81
81
  ## Building UI
82
82
 
83
83
  - **Don't rebuild what Bitkit has.** Before hand-building a dialog, select, search input, list, table, tabs or empty state from Chakra primitives or HTML, look for the Bitkit component in the MCP. Raw Chakra is for layout and text (`Box`, `Stack`, `Text`) only — never Chakra's own `Button`, `Input`, `Dialog` and the like: the Bitkit theme styles them, so they look right, but they lack Bitkit's API and behaviour.
84
- - **A whole page starts from the page-level components** — `BitkitPageHeader`, `BitkitUISideNav`, `BitkitPageFooter` — not from a hand-built frame.
84
+ - **A whole page starts from the page-level components** — `BitkitPageHeader`, `BitkitPageSubheader`, `BitkitContentWrapper`, `BitkitUISideNav`, `BitkitPageFooter` — not from a hand-built frame. Before building or changing a page layout, read the `Docs/Page Layout` page: content widths, alignment, spacing and heading levels.
85
85
  - **Handle every state of a data view.** Anything that shows data needs a loading state (see *Skeleton loading*), an empty state (`BitkitEmptyState`) and an error state (`BitkitAlert`, or `createBitkitToast` for a failed action).
86
86
  - **Don't override a Bitkit component's styling** — no `border`, `borderRadius`, `background`, `boxShadow` or `color` fighting its design — and don't pass an empty or placeholder value just to satisfy a required prop. Layout props (`width`, `flex`, `margin*`, …) are fine.
87
87
  - **When Bitkit doesn't fit, it's a design-system gap, not a styling task.** Stop and tell the user; the gap gets fixed in Bitkit, once. At Bitrise, report it in #engineering-domain-frontend.
@@ -111,10 +111,6 @@ Bitkit components use consistent prop names across the library:
111
111
  - **A component that branches on `size` must resolve the default itself** — the recipe's `defaultVariants` applies in CSS, so an omitted `size` still reaches the component as `undefined`. Any JS that derives an icon or addon size from it then takes the wrong branch, and the element renders at one size with its icons at another. Give the prop a default where it is destructured (`size = 'lg'`), or resolve it at the point of use (`(props.size ?? 'lg') === 'lg' ? … : …`) when the component destructures in several places. Never rely on phrasing the condition so `undefined` happens to fall through — that is how `BitkitNumberInput`, `BitkitSelect`, `BitkitIconButton` and `BitkitCloseButton` each ended up mismatched.
112
112
  The fallback must equal the recipe's `defaultVariants.size`; both are needed, since the recipe's is what raw Chakra and slot consumers get. And when a component calls its own recipe, pass the variant (`recipe({ size })`, never `recipe()`).
113
113
  - **`state` prop replaces `disabled`/`loading`** — use `<BitkitButton state="disabled">`, not `<BitkitButton disabled>`. Values: `'disabled' | 'loading' | 'skeleton'`.
114
- - **Say why a button is disabled with `disabledReason`** — on `BitkitButton` and `BitkitIconButton`, next to `state="disabled"`. The button shows it in a tooltip, stays focusable and is announced as disabled. Pass it unconditionally (it only shows while disabled), so the button isn't remounted when it gets enabled. Don't wrap a disabled button in `BitkitTooltip wrapTrigger`, and don't turn the icon button's label tooltip off for it:
115
- ```tsx
116
- <BitkitIconButton disabledReason="Only owners can delete" icon={IconTrash} label="Delete" state="disabled" />
117
- ```
118
114
  - **Dot notation for compound components** — sub-components are exposed as static properties on the parent, not as separate named exports:
119
115
  ```tsx
120
116
  <BitkitSegmentedControl value={value} onValueChange={setValue}>
@@ -238,7 +234,7 @@ Bitkit components use consistent prop names across the library:
238
234
  - **A surface that stays dark in both color modes** (logs, terminal output) — `background/fixed-contrast` with `text/on-color` (and `color/white/*` for dimmer text). `background/contrast` turns light in dark mode, so the white text on it disappears. Don't reach for a palette color instead.
239
235
  - **Always use `textStyle`** — never set `fontFamily` or `fontSize` on `<Text>`. Use `textStyle="body/md/regular"`, `textStyle="code/md"`, etc.
240
236
  - **Sizes as strings** — `width="32"`, not `width={32}`. Tokens are strings.
241
- - **Use existing size tokens** — closest existing token (1, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96, 128) representing pixel values (1=1px, 32=32px). Avoid arbitrary px (`width="100px"`). Above 128: for a content width use a `layout/maxWidth/*` token (`640`, `960`, `1280`, `1616`; e.g. `maxWidth="layout/maxWidth/960"`, sizes only, not spacing); for anything else use Bitkit's `rem()` helper (`import { rem } from '@bitrise/bitkit-v2'`, `maxWidth={rem(800)}`), never a bare-number string — `maxWidth="800"` is not a token, reaches the CSS without a unit, and the browser silently drops it.
237
+ - **Use existing size tokens** — closest existing token (1, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96, 128) representing pixel values (1=1px, 32=32px). Avoid arbitrary px (`width="100px"`). Above 128: a page's content width is `contentWidth` (`640`, `960`, `1280`, `1616`, `full`) on `BitkitContentWrapper` and on the page header, subheader and filter bar above it — see `Docs/Page Layout`; for anything else use Bitkit's `rem()` helper (`import { rem } from '@bitrise/bitkit-v2'`, `maxWidth={rem(800)}`), never a bare-number string — `maxWidth="800"` is not a token, reaches the CSS without a unit, and the browser silently drops it.
242
238
  - **Omit default prop values** — `<IconCross />`, not `<IconCross size="24" />` when 24 is the default.
243
239
  - **Never pass `color` to a colored icon** — multi/fixed-color icons (`Icon*Color`, `IconWarningYellow`, `Icon*White`, etc.) have their colors baked in. They omit `color` from their props, so passing it is a type error. Only monochrome icons follow `color` (via `currentColor`).
244
240
  - **No shorthand props** — use full names: `marginInline`, `paddingBlockEnd`, `width`, `background`, `padding`. Never `mx`, `pb`, `w`, `bg`, `p`, `mt`.
@@ -10,13 +10,6 @@ interface BitkitButtonCommonProps {
10
10
  export interface BitkitButtonAsButtonProps extends BitkitButtonCommonProps, Omit<ButtonProps, BitkitButtonOmittedFromButton> {
11
11
  asChild?: false;
12
12
  children: string;
13
- /**
14
- * Why the button is disabled, shown in a tooltip on it while `state` is `"disabled"`. The button
15
- * then stays focusable (`aria-disabled` instead of the native attribute), so the tooltip opens on
16
- * hover and focus, and screen readers announce the reason. Pass it whether or not the button is
17
- * disabled right now. Use it instead of wrapping a disabled button in `BitkitTooltip`.
18
- */
19
- disabledReason?: string;
20
13
  href?: undefined;
21
14
  isExternal?: undefined;
22
15
  state?: 'disabled' | 'loading' | 'skeleton';
@@ -1,5 +1,3 @@
1
- import focusableDisabledProps from "../../utilities/focusableDisabledProps.js";
2
- import BitkitTooltip from "../BitkitTooltip/BitkitTooltip.js";
3
1
  import { chakra, useRecipe } from "@chakra-ui/react/styled-system";
4
2
  import { Children, cloneElement, forwardRef } from "react";
5
3
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -86,17 +84,15 @@ var BitkitButton = forwardRef((props, ref) => {
86
84
  children: anchorElement
87
85
  }) : anchorElement;
88
86
  }
89
- const { children, disabledReason, icon: _icon, state, suffixIcon: _suffixIcon, ...buttonRest } = props;
90
- const hasReason = isDisabled && !!disabledReason;
91
- const buttonElement = /* @__PURE__ */ jsx(Skeleton, {
87
+ const { children, icon: _icon, state, suffixIcon: _suffixIcon, ...buttonRest } = props;
88
+ return /* @__PURE__ */ jsx(Skeleton, {
92
89
  asChild: true,
93
90
  loading: isSkeleton,
94
91
  children: /* @__PURE__ */ jsxs(Button, {
95
- disabled: isDisabled && !hasReason,
92
+ disabled: isDisabled,
96
93
  loading: state === "loading",
97
94
  ref,
98
95
  ...buttonRest,
99
- ...hasReason && focusableDisabledProps,
100
96
  children: [
101
97
  Icon && /* @__PURE__ */ jsx(Icon, {
102
98
  marginInlineStart: "-4",
@@ -110,11 +106,6 @@ var BitkitButton = forwardRef((props, ref) => {
110
106
  ]
111
107
  })
112
108
  });
113
- return disabledReason ? /* @__PURE__ */ jsx(BitkitTooltip, {
114
- disabled: !isDisabled,
115
- text: disabledReason,
116
- children: buttonElement
117
- }) : buttonElement;
118
109
  });
119
110
  BitkitButton.displayName = "BitkitButton";
120
111
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitButton.js","names":["_Fragment"],"sources":["../../../lib/components/BitkitButton/BitkitButton.tsx"],"sourcesContent":["import { Button, type ButtonProps } from '@chakra-ui/react/button';\nimport { Skeleton } from '@chakra-ui/react/skeleton';\nimport { chakra, type HTMLChakraProps, type RecipeProps, useRecipe } from '@chakra-ui/react/styled-system';\nimport {\n Children,\n cloneElement,\n forwardRef,\n type KeyboardEvent,\n type MouseEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport focusableDisabledProps from '../../utilities/focusableDisabledProps';\nimport BitkitTooltip from '../BitkitTooltip/BitkitTooltip';\n\ntype BitkitButtonOmittedFromButton =\n 'asChild' | 'children' | 'colorPalette' | 'disabled' | 'loading' | 'loadingText' | 'spinner' | 'spinnerPlacement';\n\ninterface BitkitButtonCommonProps {\n icon?: BitkitIconComponent;\n suffixIcon?: BitkitIconComponent;\n}\n\nexport interface BitkitButtonAsButtonProps\n extends BitkitButtonCommonProps, Omit<ButtonProps, BitkitButtonOmittedFromButton> {\n asChild?: false;\n children: string;\n /**\n * Why the button is disabled, shown in a tooltip on it while `state` is `\"disabled\"`. The button\n * then stays focusable (`aria-disabled` instead of the native attribute), so the tooltip opens on\n * hover and focus, and screen readers announce the reason. Pass it whether or not the button is\n * disabled right now. Use it instead of wrapping a disabled button in `BitkitTooltip`.\n */\n disabledReason?: string;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\nexport interface BitkitButtonAsAnchorProps\n extends BitkitButtonCommonProps, Omit<HTMLChakraProps<'a'>, 'children' | 'colorPalette'>, RecipeProps<'button'> {\n asChild?: false;\n children: string;\n href: string;\n isExternal?: boolean;\n state?: 'disabled' | 'skeleton';\n}\n\ninterface BitkitButtonAsChildProps extends BitkitButtonCommonProps, Omit<ButtonProps, BitkitButtonOmittedFromButton> {\n asChild: true;\n children: ReactElement;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\nexport type BitkitButtonProps = BitkitButtonAsButtonProps | BitkitButtonAsAnchorProps | BitkitButtonAsChildProps;\n\n// The forwarded ref is typed as HTMLElement to cover all three modes (button, anchor, slot/asChild),\n// since asChild can render any element (router Link, custom wrappers, etc.). Inner narrow casts\n// satisfy the wrapped Chakra components without affecting the consumer-visible ref shape.\nconst BitkitButton = forwardRef<HTMLElement, BitkitButtonProps>((props, ref) => {\n const buttonRecipe = useRecipe({ key: 'button' });\n const Icon = props.icon;\n const SuffixIcon = props.suffixIcon;\n // Resolve the size the recipe would apply before branching on it: an unset `size` reaches us as\n // `undefined` because the recipe's default lives in CSS, not on the prop. Keep the fallback equal\n // to `defaultVariants.size` in Button.recipe.ts.\n const iconSize = (props.size ?? 'lg') === 'lg' ? '24' : '16';\n const isDisabled = props.state === 'disabled';\n const isSkeleton = props.state === 'skeleton';\n\n // --- Slot mode (asChild) ---\n if (props.asChild) {\n const { asChild: _asChild, children, icon: _icon, state, suffixIcon: _suffixIcon, ...buttonRest } = props;\n const onlyChild = Children.only(children) as ReactElement<{ children?: ReactNode }>;\n const buttonElement = (\n <Button\n asChild\n disabled={isDisabled}\n loading={state === 'loading'}\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n >\n {cloneElement(\n onlyChild,\n undefined,\n <>\n {Icon && <Icon marginInlineStart=\"-4\" size={iconSize} />}\n {onlyChild.props.children}\n {SuffixIcon && <SuffixIcon marginInlineEnd=\"-4\" size={iconSize} />}\n </>,\n )}\n </Button>\n );\n return isSkeleton ? (\n <Skeleton asChild loading>\n {buttonElement}\n </Skeleton>\n ) : (\n buttonElement\n );\n }\n\n // --- Anchor mode (href) ---\n if (props.href !== undefined) {\n const {\n children,\n css,\n href,\n icon: _icon,\n isExternal,\n onClick,\n onKeyDown,\n rel,\n size,\n state: _state,\n suffixIcon: _suffixIcon,\n target,\n variant,\n ...anchorRest\n } = props;\n const effectiveTarget = isExternal ? '_blank' : target;\n const effectiveRel = isExternal ? (rel ? `${rel} noreferrer noopener` : 'noreferrer noopener') : rel;\n const handleClick = isDisabled\n ? (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n const handleKeyDown = isDisabled\n ? (e: KeyboardEvent<HTMLAnchorElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n : onKeyDown;\n const anchorElement = (\n <chakra.a\n ref={ref as Ref<HTMLAnchorElement>}\n {...anchorRest}\n aria-disabled={isDisabled || undefined}\n // A native <a> can't be `:disabled`, so mark `data-disabled` too: it's the styling/interaction hook the\n // recipe's `_hover`/`_active` conditions exclude (`:not(…, [data-disabled])`), while `aria-disabled` stays\n // for a11y. Without it, hover/active would still match a disabled anchor and override the disabled colors.\n css={[buttonRecipe({ size, variant }), css]}\n data-disabled={isDisabled || undefined}\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n rel={effectiveRel}\n target={effectiveTarget}\n >\n {Icon && <Icon marginInlineStart=\"-4\" size={iconSize} />}\n {children}\n {SuffixIcon && <SuffixIcon marginInlineEnd=\"-4\" size={iconSize} />}\n </chakra.a>\n );\n return isSkeleton ? (\n <Skeleton asChild loading>\n {anchorElement}\n </Skeleton>\n ) : (\n anchorElement\n );\n }\n\n // --- Button mode (default) ---\n const { children, disabledReason, icon: _icon, state, suffixIcon: _suffixIcon, ...buttonRest } = props;\n const hasReason = isDisabled && !!disabledReason;\n const buttonElement = (\n <Skeleton asChild loading={isSkeleton}>\n <Button\n disabled={isDisabled && !hasReason}\n loading={state === 'loading'}\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n {...(hasReason && focusableDisabledProps)}\n >\n {Icon && <Icon marginInlineStart=\"-4\" size={iconSize} />}\n {children}\n {SuffixIcon && <SuffixIcon marginInlineEnd=\"-4\" size={iconSize} />}\n </Button>\n </Skeleton>\n );\n // Keyed on the prop, not on `state`: wrapping the button only while it's disabled would remount it\n // whenever it gets enabled or disabled, and drop its focus.\n return disabledReason ? (\n <BitkitTooltip disabled={!isDisabled} text={disabledReason}>\n {buttonElement}\n </BitkitTooltip>\n ) : (\n buttonElement\n );\n});\n\nBitkitButton.displayName = 'BitkitButton';\n\nexport default BitkitButton;\n"],"mappings":";;;;;;;;AAgEA,IAAM,eAAe,YAA4C,OAAO,QAAQ;CAC9E,MAAM,eAAe,UAAU,EAAE,KAAK,SAAS,CAAC;CAChD,MAAM,OAAO,MAAM;CACnB,MAAM,aAAa,MAAM;CAIzB,MAAM,YAAY,MAAM,QAAQ,UAAU,OAAO,OAAO;CACxD,MAAM,aAAa,MAAM,UAAU;CACnC,MAAM,aAAa,MAAM,UAAU;CAGnC,IAAI,MAAM,SAAS;EACjB,MAAM,EAAE,SAAS,UAAU,UAAU,MAAM,OAAO,OAAO,YAAY,aAAa,GAAG,eAAe;EACpG,MAAM,YAAY,SAAS,KAAK,QAAQ;EACxC,MAAM,gBACJ,oBAAC,QAAD;GACE,SAAA;GACA,UAAU;GACV,SAAS,UAAU;GACd;GACL,GAAI;GAEH,UAAA,aACC,WACA,KAAA,GACA,qBAAAA,YAAA,EAAA,UAAA;IACG,QAAQ,oBAAC,MAAD;KAAM,mBAAkB;KAAK,MAAM;IAAW,CAAA;IACtD,UAAU,MAAM;IAChB,cAAc,oBAAC,YAAD;KAAY,iBAAgB;KAAK,MAAM;IAAW,CAAA;GACjE,EAAA,CAAA,CACJ;EACM,CAAA;EAEV,OAAO,aACL,oBAAC,UAAD;GAAU,SAAA;GAAQ,SAAA;GACf,UAAA;EACO,CAAA,IAEV;CAEJ;CAGA,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,EACJ,UACA,KACA,MACA,MAAM,OACN,YACA,SACA,WACA,KACA,MACA,OAAO,QACP,YAAY,aACZ,QACA,SACA,GAAG,eACD;EACJ,MAAM,kBAAkB,aAAa,WAAW;EAChD,MAAM,eAAe,aAAc,MAAM,GAAG,IAAI,wBAAwB,wBAAyB;EACjG,MAAM,cAAc,cACf,MAAqC;GACpC,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB,IACA;EACJ,MAAM,gBAAgB,cACjB,MAAwC;GACvC,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;IACtC,EAAE,eAAe;IACjB,EAAE,gBAAgB;GACpB;EACF,IACA;EACJ,MAAM,gBACJ,qBAAC,OAAO,GAAR;GACO;GACL,GAAI;GACJ,iBAAe,cAAc,KAAA;GAI7B,KAAK,CAAC,aAAa;IAAE;IAAM;GAAQ,CAAC,GAAG,GAAG;GAC1C,iBAAe,cAAc,KAAA;GAC7B,MAAM,aAAa,KAAA,IAAY;GAC/B,SAAS;GACT,WAAW;GACX,KAAK;GACL,QAAQ;GAbV,UAAA;IAeG,QAAQ,oBAAC,MAAD;KAAM,mBAAkB;KAAK,MAAM;IAAW,CAAA;IACtD;IACA,cAAc,oBAAC,YAAD;KAAY,iBAAgB;KAAK,MAAM;IAAW,CAAA;GACzD;;EAEZ,OAAO,aACL,oBAAC,UAAD;GAAU,SAAA;GAAQ,SAAA;GACf,UAAA;EACO,CAAA,IAEV;CAEJ;CAGA,MAAM,EAAE,UAAU,gBAAgB,MAAM,OAAO,OAAO,YAAY,aAAa,GAAG,eAAe;CACjG,MAAM,YAAY,cAAc,CAAC,CAAC;CAClC,MAAM,gBACJ,oBAAC,UAAD;EAAU,SAAA;EAAQ,SAAS;EACzB,UAAA,qBAAC,QAAD;GACE,UAAU,cAAc,CAAC;GACzB,SAAS,UAAU;GACd;GACL,GAAI;GACJ,GAAK,aAAa;GALpB,UAAA;IAOG,QAAQ,oBAAC,MAAD;KAAM,mBAAkB;KAAK,MAAM;IAAW,CAAA;IACtD;IACA,cAAc,oBAAC,YAAD;KAAY,iBAAgB;KAAK,MAAM;IAAW,CAAA;GAC3D;;CACA,CAAA;CAIZ,OAAO,iBACL,oBAAC,eAAD;EAAe,UAAU,CAAC;EAAY,MAAM;EACzC,UAAA;CACY,CAAA,IAEf;AAEJ,CAAC;AAED,aAAa,cAAc"}
1
+ {"version":3,"file":"BitkitButton.js","names":["_Fragment"],"sources":["../../../lib/components/BitkitButton/BitkitButton.tsx"],"sourcesContent":["import { Button, type ButtonProps } from '@chakra-ui/react/button';\nimport { Skeleton } from '@chakra-ui/react/skeleton';\nimport { chakra, type HTMLChakraProps, type RecipeProps, useRecipe } from '@chakra-ui/react/styled-system';\nimport {\n Children,\n cloneElement,\n forwardRef,\n type KeyboardEvent,\n type MouseEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\n\ntype BitkitButtonOmittedFromButton =\n 'asChild' | 'children' | 'colorPalette' | 'disabled' | 'loading' | 'loadingText' | 'spinner' | 'spinnerPlacement';\n\ninterface BitkitButtonCommonProps {\n icon?: BitkitIconComponent;\n suffixIcon?: BitkitIconComponent;\n}\n\nexport interface BitkitButtonAsButtonProps\n extends BitkitButtonCommonProps, Omit<ButtonProps, BitkitButtonOmittedFromButton> {\n asChild?: false;\n children: string;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\nexport interface BitkitButtonAsAnchorProps\n extends BitkitButtonCommonProps, Omit<HTMLChakraProps<'a'>, 'children' | 'colorPalette'>, RecipeProps<'button'> {\n asChild?: false;\n children: string;\n href: string;\n isExternal?: boolean;\n state?: 'disabled' | 'skeleton';\n}\n\ninterface BitkitButtonAsChildProps extends BitkitButtonCommonProps, Omit<ButtonProps, BitkitButtonOmittedFromButton> {\n asChild: true;\n children: ReactElement;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\nexport type BitkitButtonProps = BitkitButtonAsButtonProps | BitkitButtonAsAnchorProps | BitkitButtonAsChildProps;\n\n// The forwarded ref is typed as HTMLElement to cover all three modes (button, anchor, slot/asChild),\n// since asChild can render any element (router Link, custom wrappers, etc.). Inner narrow casts\n// satisfy the wrapped Chakra components without affecting the consumer-visible ref shape.\nconst BitkitButton = forwardRef<HTMLElement, BitkitButtonProps>((props, ref) => {\n const buttonRecipe = useRecipe({ key: 'button' });\n const Icon = props.icon;\n const SuffixIcon = props.suffixIcon;\n // Resolve the size the recipe would apply before branching on it: an unset `size` reaches us as\n // `undefined` because the recipe's default lives in CSS, not on the prop. Keep the fallback equal\n // to `defaultVariants.size` in Button.recipe.ts.\n const iconSize = (props.size ?? 'lg') === 'lg' ? '24' : '16';\n const isDisabled = props.state === 'disabled';\n const isSkeleton = props.state === 'skeleton';\n\n // --- Slot mode (asChild) ---\n if (props.asChild) {\n const { asChild: _asChild, children, icon: _icon, state, suffixIcon: _suffixIcon, ...buttonRest } = props;\n const onlyChild = Children.only(children) as ReactElement<{ children?: ReactNode }>;\n const buttonElement = (\n <Button\n asChild\n disabled={isDisabled}\n loading={state === 'loading'}\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n >\n {cloneElement(\n onlyChild,\n undefined,\n <>\n {Icon && <Icon marginInlineStart=\"-4\" size={iconSize} />}\n {onlyChild.props.children}\n {SuffixIcon && <SuffixIcon marginInlineEnd=\"-4\" size={iconSize} />}\n </>,\n )}\n </Button>\n );\n return isSkeleton ? (\n <Skeleton asChild loading>\n {buttonElement}\n </Skeleton>\n ) : (\n buttonElement\n );\n }\n\n // --- Anchor mode (href) ---\n if (props.href !== undefined) {\n const {\n children,\n css,\n href,\n icon: _icon,\n isExternal,\n onClick,\n onKeyDown,\n rel,\n size,\n state: _state,\n suffixIcon: _suffixIcon,\n target,\n variant,\n ...anchorRest\n } = props;\n const effectiveTarget = isExternal ? '_blank' : target;\n const effectiveRel = isExternal ? (rel ? `${rel} noreferrer noopener` : 'noreferrer noopener') : rel;\n const handleClick = isDisabled\n ? (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n const handleKeyDown = isDisabled\n ? (e: KeyboardEvent<HTMLAnchorElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n : onKeyDown;\n const anchorElement = (\n <chakra.a\n ref={ref as Ref<HTMLAnchorElement>}\n {...anchorRest}\n aria-disabled={isDisabled || undefined}\n // A native <a> can't be `:disabled`, so mark `data-disabled` too: it's the styling/interaction hook the\n // recipe's `_hover`/`_active` conditions exclude (`:not(…, [data-disabled])`), while `aria-disabled` stays\n // for a11y. Without it, hover/active would still match a disabled anchor and override the disabled colors.\n css={[buttonRecipe({ size, variant }), css]}\n data-disabled={isDisabled || undefined}\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n rel={effectiveRel}\n target={effectiveTarget}\n >\n {Icon && <Icon marginInlineStart=\"-4\" size={iconSize} />}\n {children}\n {SuffixIcon && <SuffixIcon marginInlineEnd=\"-4\" size={iconSize} />}\n </chakra.a>\n );\n return isSkeleton ? (\n <Skeleton asChild loading>\n {anchorElement}\n </Skeleton>\n ) : (\n anchorElement\n );\n }\n\n // --- Button mode (default) ---\n const { children, icon: _icon, state, suffixIcon: _suffixIcon, ...buttonRest } = props;\n return (\n <Skeleton asChild loading={isSkeleton}>\n <Button disabled={isDisabled} loading={state === 'loading'} ref={ref as Ref<HTMLButtonElement>} {...buttonRest}>\n {Icon && <Icon marginInlineStart=\"-4\" size={iconSize} />}\n {children}\n {SuffixIcon && <SuffixIcon marginInlineEnd=\"-4\" size={iconSize} />}\n </Button>\n </Skeleton>\n );\n});\n\nBitkitButton.displayName = 'BitkitButton';\n\nexport default BitkitButton;\n"],"mappings":";;;;;;AAuDA,IAAM,eAAe,YAA4C,OAAO,QAAQ;CAC9E,MAAM,eAAe,UAAU,EAAE,KAAK,SAAS,CAAC;CAChD,MAAM,OAAO,MAAM;CACnB,MAAM,aAAa,MAAM;CAIzB,MAAM,YAAY,MAAM,QAAQ,UAAU,OAAO,OAAO;CACxD,MAAM,aAAa,MAAM,UAAU;CACnC,MAAM,aAAa,MAAM,UAAU;CAGnC,IAAI,MAAM,SAAS;EACjB,MAAM,EAAE,SAAS,UAAU,UAAU,MAAM,OAAO,OAAO,YAAY,aAAa,GAAG,eAAe;EACpG,MAAM,YAAY,SAAS,KAAK,QAAQ;EACxC,MAAM,gBACJ,oBAAC,QAAD;GACE,SAAA;GACA,UAAU;GACV,SAAS,UAAU;GACd;GACL,GAAI;GAEH,UAAA,aACC,WACA,KAAA,GACA,qBAAAA,YAAA,EAAA,UAAA;IACG,QAAQ,oBAAC,MAAD;KAAM,mBAAkB;KAAK,MAAM;IAAW,CAAA;IACtD,UAAU,MAAM;IAChB,cAAc,oBAAC,YAAD;KAAY,iBAAgB;KAAK,MAAM;IAAW,CAAA;GACjE,EAAA,CAAA,CACJ;EACM,CAAA;EAEV,OAAO,aACL,oBAAC,UAAD;GAAU,SAAA;GAAQ,SAAA;GACf,UAAA;EACO,CAAA,IAEV;CAEJ;CAGA,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,EACJ,UACA,KACA,MACA,MAAM,OACN,YACA,SACA,WACA,KACA,MACA,OAAO,QACP,YAAY,aACZ,QACA,SACA,GAAG,eACD;EACJ,MAAM,kBAAkB,aAAa,WAAW;EAChD,MAAM,eAAe,aAAc,MAAM,GAAG,IAAI,wBAAwB,wBAAyB;EACjG,MAAM,cAAc,cACf,MAAqC;GACpC,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB,IACA;EACJ,MAAM,gBAAgB,cACjB,MAAwC;GACvC,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;IACtC,EAAE,eAAe;IACjB,EAAE,gBAAgB;GACpB;EACF,IACA;EACJ,MAAM,gBACJ,qBAAC,OAAO,GAAR;GACO;GACL,GAAI;GACJ,iBAAe,cAAc,KAAA;GAI7B,KAAK,CAAC,aAAa;IAAE;IAAM;GAAQ,CAAC,GAAG,GAAG;GAC1C,iBAAe,cAAc,KAAA;GAC7B,MAAM,aAAa,KAAA,IAAY;GAC/B,SAAS;GACT,WAAW;GACX,KAAK;GACL,QAAQ;GAbV,UAAA;IAeG,QAAQ,oBAAC,MAAD;KAAM,mBAAkB;KAAK,MAAM;IAAW,CAAA;IACtD;IACA,cAAc,oBAAC,YAAD;KAAY,iBAAgB;KAAK,MAAM;IAAW,CAAA;GACzD;;EAEZ,OAAO,aACL,oBAAC,UAAD;GAAU,SAAA;GAAQ,SAAA;GACf,UAAA;EACO,CAAA,IAEV;CAEJ;CAGA,MAAM,EAAE,UAAU,MAAM,OAAO,OAAO,YAAY,aAAa,GAAG,eAAe;CACjF,OACE,oBAAC,UAAD;EAAU,SAAA;EAAQ,SAAS;EACzB,UAAA,qBAAC,QAAD;GAAQ,UAAU;GAAY,SAAS,UAAU;GAAgB;GAA+B,GAAI;GAApG,UAAA;IACG,QAAQ,oBAAC,MAAD;KAAM,mBAAkB;KAAK,MAAM;IAAW,CAAA;IACtD;IACA,cAAc,oBAAC,YAAD;KAAY,iBAAgB;KAAK,MAAM;IAAW,CAAA;GAC3D;;CACA,CAAA;AAEd,CAAC;AAED,aAAa,cAAc"}
@@ -1,6 +1,6 @@
1
- import BitkitTooltip from "../../BitkitTooltip/BitkitTooltip.js";
2
1
  import { DatePicker, DatePickerContext } from "../../../atoms/DatePicker/DatePicker.js";
3
2
  import { calendarDateToDate } from "../Calendar.utils.js";
3
+ import BitkitTooltip from "../../BitkitTooltip/BitkitTooltip.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { Text } from "@chakra-ui/react/text";
6
6
  //#region lib/components/BitkitCalendar/components/CalendarMonth.tsx
@@ -0,0 +1,16 @@
1
+ import { BoxProps } from '@chakra-ui/react/box';
2
+ import { ContentWidth } from '../../theme/common/ContentWidth.common';
3
+ /** A page content width: one of the `layout/maxWidth` tokens, or `full` for no cap. */
4
+ export type BitkitContentWidth = ContentWidth;
5
+ export interface BitkitContentWrapperProps extends BoxProps {
6
+ /**
7
+ * Side-by-side columns from `tablet` up, as a CSS grid template — `'1fr 1fr'` for two equal columns,
8
+ * `'1fr 25rem'` for one that fills next to a fixed one. Below `tablet` the columns stack.
9
+ */
10
+ columnTemplate?: string;
11
+ /** The cap on the page's content column, padding included. Pick the same value for the page header above it. */
12
+ contentWidth: BitkitContentWidth;
13
+ }
14
+ /** The page's content grid: capped at a `layout/maxWidth` token, centred, one column of sections 32 apart unless `columnTemplate` says otherwise. */
15
+ declare const BitkitContentWrapper: import('react').ForwardRefExoticComponent<BitkitContentWrapperProps & import('react').RefAttributes<HTMLDivElement>>;
16
+ export default BitkitContentWrapper;
@@ -0,0 +1,21 @@
1
+ import { useRecipe } from "@chakra-ui/react/styled-system";
2
+ import { forwardRef } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { Box } from "@chakra-ui/react/box";
5
+ //#region lib/components/BitkitContentWrapper/BitkitContentWrapper.tsx
6
+ /** The page's content grid: capped at a `layout/maxWidth` token, centred, one column of sections 32 apart unless `columnTemplate` says otherwise. */
7
+ var BitkitContentWrapper = forwardRef((props, ref) => {
8
+ const { columnTemplate, contentWidth, css, ...rest } = props;
9
+ const recipe = useRecipe({ key: "contentWrapper" });
10
+ return /* @__PURE__ */ jsx(Box, {
11
+ css: [recipe({ contentWidth }), css],
12
+ gridTemplateColumns: columnTemplate ? { tablet: columnTemplate } : void 0,
13
+ ref,
14
+ ...rest
15
+ });
16
+ });
17
+ BitkitContentWrapper.displayName = "BitkitContentWrapper";
18
+ //#endregion
19
+ export { BitkitContentWrapper as default };
20
+
21
+ //# sourceMappingURL=BitkitContentWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BitkitContentWrapper.js","names":[],"sources":["../../../lib/components/BitkitContentWrapper/BitkitContentWrapper.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef } from 'react';\n\nimport { type ContentWidth } from '../../theme/common/ContentWidth.common';\n\n/** A page content width: one of the `layout/maxWidth` tokens, or `full` for no cap. */\nexport type BitkitContentWidth = ContentWidth;\n\nexport interface BitkitContentWrapperProps extends BoxProps {\n /**\n * Side-by-side columns from `tablet` up, as a CSS grid template — `'1fr 1fr'` for two equal columns,\n * `'1fr 25rem'` for one that fills next to a fixed one. Below `tablet` the columns stack.\n */\n columnTemplate?: string;\n /** The cap on the page's content column, padding included. Pick the same value for the page header above it. */\n contentWidth: BitkitContentWidth;\n}\n\n/** The page's content grid: capped at a `layout/maxWidth` token, centred, one column of sections 32 apart unless `columnTemplate` says otherwise. */\nconst BitkitContentWrapper = forwardRef<HTMLDivElement, BitkitContentWrapperProps>((props, ref) => {\n const { columnTemplate, contentWidth, css, ...rest } = props;\n\n const recipe = useRecipe({ key: 'contentWrapper' });\n\n return (\n <Box\n css={[recipe({ contentWidth }), css]}\n gridTemplateColumns={columnTemplate ? { tablet: columnTemplate } : undefined}\n ref={ref}\n {...rest}\n />\n );\n});\n\nBitkitContentWrapper.displayName = 'BitkitContentWrapper';\n\nexport default BitkitContentWrapper;\n"],"mappings":";;;;;;AAoBA,IAAM,uBAAuB,YAAuD,OAAO,QAAQ;CACjG,MAAM,EAAE,gBAAgB,cAAc,KAAK,GAAG,SAAS;CAEvD,MAAM,SAAS,UAAU,EAAE,KAAK,iBAAiB,CAAC;CAElD,OACE,oBAAC,KAAD;EACE,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,GAAG,GAAG;EACnC,qBAAqB,iBAAiB,EAAE,QAAQ,eAAe,IAAI,KAAA;EAC9D;EACL,GAAI;CACL,CAAA;AAEL,CAAC;AAED,qBAAqB,cAAc"}
@@ -1,25 +1,20 @@
1
1
  import { BoxProps } from '@chakra-ui/react/box';
2
2
  import { ReactNode } from 'react';
3
+ import { BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';
3
4
  export interface BitkitFilterProps extends Omit<BoxProps, 'children'> {
4
5
  /**
5
6
  * The filters: `BitkitFilter.Switch`, `.Select`, `.DateRange`, `.Add` and `.Search`, in the
6
7
  * order they appear. `.Search` always sits at the end of the bar.
7
8
  */
8
9
  children: ReactNode;
9
- /**
10
- * Where a capped content block sits: `center` over a centred page column, `start` over one that
11
- * starts against a sidebar. Pass the same alignment as the page header and content below it.
12
- * Only applies with a `contentWidth` in rems. Desktop only.
13
- */
14
- contentAlign?: 'center' | 'start';
15
10
  /**
16
11
  * Lines the bar up with the page content below it, for a bar under a page header: it gains a
17
- * bottom border and the page's inline padding (32, 16 below tablet). Pass the content column's
18
- * `max-width` in rems, padding included, as for `BitkitPageHeader`, or `'full'` for a full-width column.
12
+ * bottom border and the page's inline padding (32, 16 below tablet). Pass the `BitkitContentWrapper`'s value,
13
+ * as for `BitkitPageHeader`; the bar is capped at that width and centred.
19
14
  *
20
15
  * Without it the bar has no padding of its own: place it with style props, like any block. Desktop only.
21
16
  */
22
- contentWidth?: `${number}rem` | 'full';
17
+ contentWidth?: BitkitContentWidth;
23
18
  /**
24
19
  * Forces the desktop bar or the mobile "Filters" dialog. Defaults to the viewport: mobile below the
25
20
  * tablet breakpoint. The mobile dialog is made for narrow screens; from tablet up it is a 320px drawer.
@@ -7,6 +7,7 @@ import BitkitBadge from "../BitkitBadge/BitkitBadge.js";
7
7
  import { flattenChildren } from "../../utilities/flattenChildren.js";
8
8
  import BitkitButton from "../BitkitButton/BitkitButton.js";
9
9
  import useResponsive from "../../hooks/useResponsive.js";
10
+ import { contentMaxWidth } from "../../theme/common/ContentWidth.common.js";
10
11
  import { FilterContextProvider } from "./BitkitFilter.context.js";
11
12
  import BitkitFilterAdd from "./BitkitFilterAdd.js";
12
13
  import BitkitFilterDateRange from "./BitkitFilterDateRange.js";
@@ -35,7 +36,7 @@ var toggle = (set, id, isIn) => {
35
36
  * where every filter is a field opening its own view. A filter still applies on its view's "Apply".
36
37
  */
37
38
  var BitkitFilter = forwardRef((props, ref) => {
38
- const { children, contentAlign, contentWidth, css, device, onClear, showClear = true, showIcon = true, state, ...rest } = props;
39
+ const { children, contentWidth, css, device, onClear, showClear = true, showIcon = true, state, ...rest } = props;
39
40
  const { isMobile: isMobileViewport } = useResponsive();
40
41
  const isMobile = device ? device === "mobile" : isMobileViewport;
41
42
  const isDisabled = state === "loading";
@@ -44,10 +45,7 @@ var BitkitFilter = forwardRef((props, ref) => {
44
45
  const recipe = useSlotRecipe({ key: "filter" });
45
46
  let pageLayout;
46
47
  if (contentWidth) pageLayout = contentWidth === "full" ? "full" : "capped";
47
- const styles = recipe({
48
- contentAlign: pageLayout === "capped" ? contentAlign : void 0,
49
- pageLayout
50
- });
48
+ const styles = recipe({ pageLayout });
51
49
  const resets = useRef(/* @__PURE__ */ new Map());
52
50
  const [dirtyIds, setDirtyIds] = useState(/* @__PURE__ */ new Set());
53
51
  const [activeIds, setActiveIds] = useState(/* @__PURE__ */ new Set());
@@ -203,7 +201,7 @@ var BitkitFilter = forwardRef((props, ref) => {
203
201
  ...rest,
204
202
  children: /* @__PURE__ */ jsxs(Box, {
205
203
  css: styles.content,
206
- maxWidth: contentWidth === "full" ? void 0 : contentWidth,
204
+ maxWidth: contentMaxWidth(contentWidth),
207
205
  ref: focusTarget,
208
206
  children: [/* @__PURE__ */ jsxs(Box, {
209
207
  css: styles.filters,
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitFilter.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilter.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Drawer } from '@chakra-ui/react/drawer';\nimport { Portal } from '@chakra-ui/react/portal';\n// `chakra.button`: the mobile \"Filters\" button carries a count badge, which BitkitButton's string-only label can't.\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport useResponsive from '../../hooks/useResponsive';\nimport { IconCross, IconFilter } from '../../icons';\nimport { usePortalContext } from '../../utilities/BitkitPortalContext';\nimport { flattenChildren } from '../../utilities/flattenChildren';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitBadge from '../BitkitBadge/BitkitBadge';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport { FilterContextProvider, type FilterStatus } from './BitkitFilter.context';\nimport BitkitFilterAdd from './BitkitFilterAdd';\nimport BitkitFilterDateRange from './BitkitFilterDateRange';\nimport BitkitFilterSearch from './BitkitFilterSearch';\nimport BitkitFilterSelect from './BitkitFilterSelect';\nimport BitkitFilterSwitch from './BitkitFilterSwitch';\n\nexport interface BitkitFilterProps extends Omit<BoxProps, 'children'> {\n /**\n * The filters: `BitkitFilter.Switch`, `.Select`, `.DateRange`, `.Add` and `.Search`, in the\n * order they appear. `.Search` always sits at the end of the bar.\n */\n children: ReactNode;\n /**\n * Where a capped content block sits: `center` over a centred page column, `start` over one that\n * starts against a sidebar. Pass the same alignment as the page header and content below it.\n * Only applies with a `contentWidth` in rems. Desktop only.\n */\n contentAlign?: 'center' | 'start';\n /**\n * Lines the bar up with the page content below it, for a bar under a page header: it gains a\n * bottom border and the page's inline padding (32, 16 below tablet). Pass the content column's\n * `max-width` in rems, padding included, as for `BitkitPageHeader`, or `'full'` for a full-width column.\n *\n * Without it the bar has no padding of its own: place it with style props, like any block. Desktop only.\n */\n contentWidth?: `${number}rem` | 'full';\n /**\n * Forces the desktop bar or the mobile \"Filters\" dialog. Defaults to the viewport: mobile below the\n * tablet breakpoint. The mobile dialog is made for narrow screens; from tablet up it is a 320px drawer.\n */\n device?: 'desktop' | 'mobile';\n /**\n * Called once on \"Clear filters\" in place of handing each changed filter its `resetValue` — for a\n * page that resets them all in one write, e.g. one URL update. The `resetValue`s still decide when\n * the button shows.\n */\n onClear?: () => void;\n /**\n * Shows \"Clear filters\" while any filter differs from its `resetValue`. It hands each of those\n * filters its `resetValue` back through its own `onValueChange`, or calls `onClear`.\n */\n showClear?: boolean;\n /** Shows the filter icon at the start of the bar. Desktop only. */\n showIcon?: boolean;\n /** `loading`: while the page's data loads, the filters can't be opened, removed or cleared. The search stays usable. */\n state?: 'loading';\n}\n\nconst toggle = (set: ReadonlySet<string>, id: string, isIn: boolean) => {\n if (set.has(id) === isIn) return set;\n const next = new Set(set);\n if (isIn) next.add(id);\n else next.delete(id);\n return next;\n};\n\n/**\n * A bar of filters above a list. Each filter is a controlled sub-component with its own `value` and\n * `onValueChange`; the bar lays them out and offers \"Clear filters\" across all of them.\n *\n * Below the tablet breakpoint the bar collapses into a \"Filters\" button opening a full-screen dialog,\n * where every filter is a field opening its own view. A filter still applies on its view's \"Apply\".\n */\nconst BitkitFilter = forwardRef<HTMLDivElement, BitkitFilterProps>((props, ref) => {\n const {\n children,\n contentAlign,\n contentWidth,\n css,\n device,\n onClear,\n showClear = true,\n showIcon = true,\n state,\n ...rest\n } = props;\n const { isMobile: isMobileViewport } = useResponsive();\n const isMobile = device ? device === 'mobile' : isMobileViewport;\n const isDisabled = state === 'loading';\n const { disablePortal } = usePortalContext();\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n\n const recipe = useSlotRecipe({ key: 'filter' });\n let pageLayout: 'capped' | 'full' | undefined;\n if (contentWidth) pageLayout = contentWidth === 'full' ? 'full' : 'capped';\n const styles = recipe({ contentAlign: pageLayout === 'capped' ? contentAlign : undefined, pageLayout });\n\n const resets = useRef(new Map<string, () => void>());\n const [dirtyIds, setDirtyIds] = useState<ReadonlySet<string>>(new Set());\n const [activeIds, setActiveIds] = useState<ReadonlySet<string>>(new Set());\n\n const register = useCallback((id: string, reset: () => void) => {\n resets.current.set(id, reset);\n }, []);\n\n const setStatus = useCallback((id: string, { isActive, isDirty }: FilterStatus) => {\n setDirtyIds((prev) => toggle(prev, id, isDirty));\n setActiveIds((prev) => toggle(prev, id, isActive));\n }, []);\n\n const unregister = useCallback(\n (id: string) => {\n resets.current.delete(id);\n setStatus(id, { isActive: false, isDirty: false });\n },\n [setStatus],\n );\n\n const context = useMemo(\n () => ({ isDisabled, isMobile, register, setStatus, styles, unregister }),\n [isDisabled, isMobile, register, setStatus, styles, unregister],\n );\n\n const filters: ReactNode[] = [];\n const searches: ReactNode[] = [];\n // Flattened, so a filter grouped in a fragment is still sorted to the right place.\n flattenChildren(children).forEach((child) => {\n if (child.type === BitkitFilterSearch) searches.push(child);\n else filters.push(child);\n });\n\n const isDirty = dirtyIds.size > 0;\n const activeCount = activeIds.size;\n // The \"Clear filters\" button goes away (or is disabled) once nothing differs, so focus then moves\n // to the first control of the cleared filters rather than dropping to the page. Only if it hasn't\n // gone anywhere else meanwhile, e.g. while a slow reset was under way.\n const focusTarget = useRef<HTMLDivElement>(null);\n const isFocusPending = useRef(false);\n const clearAll = () => {\n if (isDisabled) return;\n isFocusPending.current = true;\n // Called off once focus lands anywhere, so a clear that never fully lands can't pull it in later.\n document.addEventListener(\n 'focusin',\n () => {\n isFocusPending.current = false;\n },\n { once: true },\n );\n if (onClear) onClear();\n else dirtyIds.forEach((id) => resets.current.get(id)?.());\n };\n useEffect(() => {\n if (!isFocusPending.current || isDirty) return;\n isFocusPending.current = false;\n const active = document.activeElement;\n if (active && active !== document.body && !active.matches('[data-filter-clear]')) return;\n focusTarget.current?.querySelector<HTMLElement>('button:not(:disabled), input:not(:disabled)')?.focus();\n }, [isDirty]);\n\n // While the page loads, the controls are only `aria-disabled`, so they keep a focus handed to them.\n // `data-disabled` takes them out of the hover and active styles too.\n const disabledProps = { 'aria-disabled': isDisabled || undefined, 'data-disabled': isDisabled ? '' : undefined };\n const clearProps = { ...disabledProps, 'data-filter-clear': '', onClick: clearAll };\n\n if (isMobile) {\n return (\n <FilterContextProvider value={context}>\n <Box css={css} ref={ref} {...rest}>\n <Box css={styles.mobileBar} ref={isDialogOpen ? undefined : focusTarget}>\n <chakra.button\n aria-expanded={isDialogOpen}\n aria-haspopup=\"dialog\"\n aria-label={`Filters, ${activeCount} applied`}\n {...disabledProps}\n css={styles.mobileTrigger}\n onClick={() => !isDisabled && setIsDialogOpen(true)}\n type=\"button\"\n >\n <IconFilter size=\"16\" />\n Filters\n <BitkitBadge colorVariant={activeCount ? 'purple' : 'neutral'}>{activeCount}</BitkitBadge>\n </chakra.button>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches}\n {/* Kept mounted while closed: the filters inside register their values with the bar. */}\n <Drawer.Root\n lazyMount={false}\n onOpenChange={({ open }) => setIsDialogOpen(open)}\n open={isDialogOpen}\n unmountOnExit={false}\n >\n <Portal disabled={disablePortal}>\n <Drawer.Positioner>\n <Drawer.Content>\n <Box css={styles.mobileHeader}>\n <Drawer.Title css={styles.mobileTitle} unstyled>\n Filters\n </Drawer.Title>\n <Drawer.CloseTrigger asChild unstyled>\n <BitkitCloseButton size=\"sm\" />\n </Drawer.CloseTrigger>\n </Box>\n <Drawer.Body css={styles.mobileBody} ref={isDialogOpen ? focusTarget : undefined}>\n {filters}\n </Drawer.Body>\n <Drawer.Footer>\n <Box css={styles.mobileFooter}>\n {showClear && (\n <BitkitButton\n {...clearProps}\n size=\"md\"\n state={isDirty ? undefined : 'disabled'}\n variant=\"secondary\"\n >\n Clear filters\n </BitkitButton>\n )}\n <BitkitButton onClick={() => setIsDialogOpen(false)} size=\"md\">\n Show results\n </BitkitButton>\n </Box>\n </Drawer.Footer>\n </Drawer.Content>\n </Drawer.Positioner>\n </Portal>\n </Drawer.Root>\n </Box>\n </FilterContextProvider>\n );\n }\n\n return (\n <FilterContextProvider value={context}>\n <Box css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.content} maxWidth={contentWidth === 'full' ? undefined : contentWidth} ref={focusTarget}>\n <Box css={styles.filters}>\n {showIcon && (\n <Box css={styles.icon}>\n <IconFilter />\n </Box>\n )}\n <Box css={styles.items}>{filters}</Box>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} flexShrink=\"0\" icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches.length > 0 && <Box css={styles.searchBlock}>{searches}</Box>}\n </Box>\n </Box>\n </FilterContextProvider>\n );\n});\n\nBitkitFilter.displayName = 'BitkitFilter';\n\nexport default withSubComponents(BitkitFilter, {\n Add: BitkitFilterAdd,\n DateRange: BitkitFilterDateRange,\n Search: BitkitFilterSearch,\n Select: BitkitFilterSelect,\n Switch: BitkitFilterSwitch,\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitFilter.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilter.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Drawer } from '@chakra-ui/react/drawer';\nimport { Portal } from '@chakra-ui/react/portal';\n// `chakra.button`: the mobile \"Filters\" button carries a count badge, which BitkitButton's string-only label can't.\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport useResponsive from '../../hooks/useResponsive';\nimport { IconCross, IconFilter } from '../../icons';\nimport { contentMaxWidth } from '../../theme/common/ContentWidth.common';\nimport { usePortalContext } from '../../utilities/BitkitPortalContext';\nimport { flattenChildren } from '../../utilities/flattenChildren';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitBadge from '../BitkitBadge/BitkitBadge';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport { type BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';\nimport { FilterContextProvider, type FilterStatus } from './BitkitFilter.context';\nimport BitkitFilterAdd from './BitkitFilterAdd';\nimport BitkitFilterDateRange from './BitkitFilterDateRange';\nimport BitkitFilterSearch from './BitkitFilterSearch';\nimport BitkitFilterSelect from './BitkitFilterSelect';\nimport BitkitFilterSwitch from './BitkitFilterSwitch';\n\nexport interface BitkitFilterProps extends Omit<BoxProps, 'children'> {\n /**\n * The filters: `BitkitFilter.Switch`, `.Select`, `.DateRange`, `.Add` and `.Search`, in the\n * order they appear. `.Search` always sits at the end of the bar.\n */\n children: ReactNode;\n /**\n * Lines the bar up with the page content below it, for a bar under a page header: it gains a\n * bottom border and the page's inline padding (32, 16 below tablet). Pass the `BitkitContentWrapper`'s value,\n * as for `BitkitPageHeader`; the bar is capped at that width and centred.\n *\n * Without it the bar has no padding of its own: place it with style props, like any block. Desktop only.\n */\n contentWidth?: BitkitContentWidth;\n /**\n * Forces the desktop bar or the mobile \"Filters\" dialog. Defaults to the viewport: mobile below the\n * tablet breakpoint. The mobile dialog is made for narrow screens; from tablet up it is a 320px drawer.\n */\n device?: 'desktop' | 'mobile';\n /**\n * Called once on \"Clear filters\" in place of handing each changed filter its `resetValue` — for a\n * page that resets them all in one write, e.g. one URL update. The `resetValue`s still decide when\n * the button shows.\n */\n onClear?: () => void;\n /**\n * Shows \"Clear filters\" while any filter differs from its `resetValue`. It hands each of those\n * filters its `resetValue` back through its own `onValueChange`, or calls `onClear`.\n */\n showClear?: boolean;\n /** Shows the filter icon at the start of the bar. Desktop only. */\n showIcon?: boolean;\n /** `loading`: while the page's data loads, the filters can't be opened, removed or cleared. The search stays usable. */\n state?: 'loading';\n}\n\nconst toggle = (set: ReadonlySet<string>, id: string, isIn: boolean) => {\n if (set.has(id) === isIn) return set;\n const next = new Set(set);\n if (isIn) next.add(id);\n else next.delete(id);\n return next;\n};\n\n/**\n * A bar of filters above a list. Each filter is a controlled sub-component with its own `value` and\n * `onValueChange`; the bar lays them out and offers \"Clear filters\" across all of them.\n *\n * Below the tablet breakpoint the bar collapses into a \"Filters\" button opening a full-screen dialog,\n * where every filter is a field opening its own view. A filter still applies on its view's \"Apply\".\n */\nconst BitkitFilter = forwardRef<HTMLDivElement, BitkitFilterProps>((props, ref) => {\n const { children, contentWidth, css, device, onClear, showClear = true, showIcon = true, state, ...rest } = props;\n const { isMobile: isMobileViewport } = useResponsive();\n const isMobile = device ? device === 'mobile' : isMobileViewport;\n const isDisabled = state === 'loading';\n const { disablePortal } = usePortalContext();\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n\n const recipe = useSlotRecipe({ key: 'filter' });\n let pageLayout: 'capped' | 'full' | undefined;\n if (contentWidth) pageLayout = contentWidth === 'full' ? 'full' : 'capped';\n const styles = recipe({ pageLayout });\n\n const resets = useRef(new Map<string, () => void>());\n const [dirtyIds, setDirtyIds] = useState<ReadonlySet<string>>(new Set());\n const [activeIds, setActiveIds] = useState<ReadonlySet<string>>(new Set());\n\n const register = useCallback((id: string, reset: () => void) => {\n resets.current.set(id, reset);\n }, []);\n\n const setStatus = useCallback((id: string, { isActive, isDirty }: FilterStatus) => {\n setDirtyIds((prev) => toggle(prev, id, isDirty));\n setActiveIds((prev) => toggle(prev, id, isActive));\n }, []);\n\n const unregister = useCallback(\n (id: string) => {\n resets.current.delete(id);\n setStatus(id, { isActive: false, isDirty: false });\n },\n [setStatus],\n );\n\n const context = useMemo(\n () => ({ isDisabled, isMobile, register, setStatus, styles, unregister }),\n [isDisabled, isMobile, register, setStatus, styles, unregister],\n );\n\n const filters: ReactNode[] = [];\n const searches: ReactNode[] = [];\n // Flattened, so a filter grouped in a fragment is still sorted to the right place.\n flattenChildren(children).forEach((child) => {\n if (child.type === BitkitFilterSearch) searches.push(child);\n else filters.push(child);\n });\n\n const isDirty = dirtyIds.size > 0;\n const activeCount = activeIds.size;\n // The \"Clear filters\" button goes away (or is disabled) once nothing differs, so focus then moves\n // to the first control of the cleared filters rather than dropping to the page. Only if it hasn't\n // gone anywhere else meanwhile, e.g. while a slow reset was under way.\n const focusTarget = useRef<HTMLDivElement>(null);\n const isFocusPending = useRef(false);\n const clearAll = () => {\n if (isDisabled) return;\n isFocusPending.current = true;\n // Called off once focus lands anywhere, so a clear that never fully lands can't pull it in later.\n document.addEventListener(\n 'focusin',\n () => {\n isFocusPending.current = false;\n },\n { once: true },\n );\n if (onClear) onClear();\n else dirtyIds.forEach((id) => resets.current.get(id)?.());\n };\n useEffect(() => {\n if (!isFocusPending.current || isDirty) return;\n isFocusPending.current = false;\n const active = document.activeElement;\n if (active && active !== document.body && !active.matches('[data-filter-clear]')) return;\n focusTarget.current?.querySelector<HTMLElement>('button:not(:disabled), input:not(:disabled)')?.focus();\n }, [isDirty]);\n\n // While the page loads, the controls are only `aria-disabled`, so they keep a focus handed to them.\n // `data-disabled` takes them out of the hover and active styles too.\n const disabledProps = { 'aria-disabled': isDisabled || undefined, 'data-disabled': isDisabled ? '' : undefined };\n const clearProps = { ...disabledProps, 'data-filter-clear': '', onClick: clearAll };\n\n if (isMobile) {\n return (\n <FilterContextProvider value={context}>\n <Box css={css} ref={ref} {...rest}>\n <Box css={styles.mobileBar} ref={isDialogOpen ? undefined : focusTarget}>\n <chakra.button\n aria-expanded={isDialogOpen}\n aria-haspopup=\"dialog\"\n aria-label={`Filters, ${activeCount} applied`}\n {...disabledProps}\n css={styles.mobileTrigger}\n onClick={() => !isDisabled && setIsDialogOpen(true)}\n type=\"button\"\n >\n <IconFilter size=\"16\" />\n Filters\n <BitkitBadge colorVariant={activeCount ? 'purple' : 'neutral'}>{activeCount}</BitkitBadge>\n </chakra.button>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches}\n {/* Kept mounted while closed: the filters inside register their values with the bar. */}\n <Drawer.Root\n lazyMount={false}\n onOpenChange={({ open }) => setIsDialogOpen(open)}\n open={isDialogOpen}\n unmountOnExit={false}\n >\n <Portal disabled={disablePortal}>\n <Drawer.Positioner>\n <Drawer.Content>\n <Box css={styles.mobileHeader}>\n <Drawer.Title css={styles.mobileTitle} unstyled>\n Filters\n </Drawer.Title>\n <Drawer.CloseTrigger asChild unstyled>\n <BitkitCloseButton size=\"sm\" />\n </Drawer.CloseTrigger>\n </Box>\n <Drawer.Body css={styles.mobileBody} ref={isDialogOpen ? focusTarget : undefined}>\n {filters}\n </Drawer.Body>\n <Drawer.Footer>\n <Box css={styles.mobileFooter}>\n {showClear && (\n <BitkitButton\n {...clearProps}\n size=\"md\"\n state={isDirty ? undefined : 'disabled'}\n variant=\"secondary\"\n >\n Clear filters\n </BitkitButton>\n )}\n <BitkitButton onClick={() => setIsDialogOpen(false)} size=\"md\">\n Show results\n </BitkitButton>\n </Box>\n </Drawer.Footer>\n </Drawer.Content>\n </Drawer.Positioner>\n </Portal>\n </Drawer.Root>\n </Box>\n </FilterContextProvider>\n );\n }\n\n return (\n <FilterContextProvider value={context}>\n <Box css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.content} maxWidth={contentMaxWidth(contentWidth)} ref={focusTarget}>\n <Box css={styles.filters}>\n {showIcon && (\n <Box css={styles.icon}>\n <IconFilter />\n </Box>\n )}\n <Box css={styles.items}>{filters}</Box>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} flexShrink=\"0\" icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches.length > 0 && <Box css={styles.searchBlock}>{searches}</Box>}\n </Box>\n </Box>\n </FilterContextProvider>\n );\n});\n\nBitkitFilter.displayName = 'BitkitFilter';\n\nexport default withSubComponents(BitkitFilter, {\n Add: BitkitFilterAdd,\n DateRange: BitkitFilterDateRange,\n Search: BitkitFilterSearch,\n Select: BitkitFilterSelect,\n Switch: BitkitFilterSwitch,\n});\n"],"mappings":"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"}
@@ -2,8 +2,8 @@ import IconChevronRight from "../../icons/IconChevronRight.js";
2
2
  import IconInfoCircle from "../../icons/IconInfoCircle.js";
3
3
  import IconPlus from "../../icons/IconPlus.js";
4
4
  import { usePortalContext } from "../../utilities/BitkitPortalContext.js";
5
- import BitkitTooltip from "../BitkitTooltip/BitkitTooltip.js";
6
5
  import BitkitButton from "../BitkitButton/BitkitButton.js";
6
+ import BitkitTooltip from "../BitkitTooltip/BitkitTooltip.js";
7
7
  import { FilterAddContextProvider, useFilterContext } from "./BitkitFilter.context.js";
8
8
  import { chakra } from "@chakra-ui/react/styled-system";
9
9
  import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
@@ -16,14 +16,6 @@ interface BitkitIconButtonCommonProps {
16
16
  }
17
17
  interface BitkitIconButtonAsButtonProps extends BitkitIconButtonCommonProps, Omit<IconButtonProps, BitkitIconButtonOmittedFromButton> {
18
18
  asChild?: false;
19
- /**
20
- * Why the button is disabled, shown in its tooltip in place of the label while `state` is
21
- * `"disabled"`. The button then stays focusable (`aria-disabled` instead of the native attribute),
22
- * so the tooltip opens on hover and focus, and screen readers announce the reason after the
23
- * label. Pass it whether or not the button is disabled right now. Use it instead of wrapping a
24
- * disabled button in `BitkitTooltip`.
25
- */
26
- disabledReason?: string;
27
19
  href?: undefined;
28
20
  isExternal?: undefined;
29
21
  state?: 'disabled' | 'loading' | 'skeleton';
@@ -1,6 +1,5 @@
1
1
  import BitkitLabelTooltip from "../BitkitLabelTooltip/BitkitLabelTooltip.js";
2
2
  import opensPopup from "../../utilities/opensPopup.js";
3
- import focusableDisabledProps from "../../utilities/focusableDisabledProps.js";
4
3
  import { chakra, useRecipe } from "@chakra-ui/react/styled-system";
5
4
  import { Children, cloneElement, forwardRef } from "react";
6
5
  import { jsx } from "react/jsx-runtime";
@@ -85,22 +84,20 @@ var BitkitIconButton = forwardRef((props, ref) => {
85
84
  }) : anchorElement
86
85
  });
87
86
  }
88
- const { disabledReason, icon: _icon, label, state, tooltipProps, ...buttonRest } = props;
89
- const reason = isDisabled ? disabledReason : void 0;
87
+ const { icon: _icon, label, state, tooltipProps, ...buttonRest } = props;
90
88
  return /* @__PURE__ */ jsx(BitkitLabelTooltip, {
91
- text: reason ?? label,
92
- disabled: !reason && isPopupTrigger,
89
+ text: label,
90
+ disabled: isPopupTrigger,
93
91
  ...tooltipProps,
94
92
  children: /* @__PURE__ */ jsx(Skeleton, {
95
93
  asChild: true,
96
94
  loading: isSkeleton,
97
95
  children: /* @__PURE__ */ jsx(IconButton, {
98
96
  "aria-label": label,
99
- disabled: isDisabled && !reason,
97
+ disabled: isDisabled,
100
98
  loading: state === "loading",
101
99
  ref,
102
100
  ...buttonRest,
103
- ...reason && focusableDisabledProps,
104
101
  children: /* @__PURE__ */ jsx(Icon, { size: iconSize })
105
102
  })
106
103
  })
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitIconButton.js","names":[],"sources":["../../../lib/components/BitkitIconButton/BitkitIconButton.tsx"],"sourcesContent":["import { IconButton, type IconButtonProps } from '@chakra-ui/react/button';\nimport { Skeleton } from '@chakra-ui/react/skeleton';\nimport { chakra, type HTMLChakraProps, type RecipeProps, useRecipe } from '@chakra-ui/react/styled-system';\nimport {\n Children,\n cloneElement,\n forwardRef,\n type KeyboardEvent,\n type MouseEvent,\n type ReactElement,\n type Ref,\n} from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport focusableDisabledProps from '../../utilities/focusableDisabledProps';\nimport opensPopup from '../../utilities/opensPopup';\nimport BitkitLabelTooltip, { type BitkitLabelTooltipProps } from '../BitkitLabelTooltip/BitkitLabelTooltip';\n\ntype BitkitIconButtonOmittedFromButton =\n | 'aria-label'\n | 'asChild'\n | 'children'\n | 'colorPalette'\n | 'disabled'\n | 'loading'\n | 'loadingText'\n | 'spinner'\n | 'spinnerPlacement';\n\ninterface BitkitIconButtonCommonProps {\n icon: BitkitIconComponent;\n label: string;\n /**\n * Overrides for the label tooltip. It is off by default when this control opens a popup — a menu,\n * select or popover trigger — where the panel is the affordance and a tooltip would hover over it.\n * Pass `{ disabled: false }` to bring it back. The accessible name comes from `label` either way.\n */\n tooltipProps?: Partial<Omit<BitkitLabelTooltipProps, 'children' | 'text'>>;\n}\n\ninterface BitkitIconButtonAsButtonProps\n extends BitkitIconButtonCommonProps, Omit<IconButtonProps, BitkitIconButtonOmittedFromButton> {\n asChild?: false;\n /**\n * Why the button is disabled, shown in its tooltip in place of the label while `state` is\n * `\"disabled\"`. The button then stays focusable (`aria-disabled` instead of the native attribute),\n * so the tooltip opens on hover and focus, and screen readers announce the reason after the\n * label. Pass it whether or not the button is disabled right now. Use it instead of wrapping a\n * disabled button in `BitkitTooltip`.\n */\n disabledReason?: string;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\ninterface BitkitIconButtonAsAnchorProps\n extends\n BitkitIconButtonCommonProps,\n Omit<HTMLChakraProps<'a'>, 'aria-label' | 'children' | 'colorPalette'>,\n RecipeProps<'button'> {\n asChild?: false;\n href: string;\n isExternal?: boolean;\n state?: 'disabled' | 'skeleton';\n}\n\ninterface BitkitIconButtonAsChildProps\n extends BitkitIconButtonCommonProps, Omit<IconButtonProps, BitkitIconButtonOmittedFromButton> {\n asChild: true;\n children: ReactElement;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\nexport type BitkitIconButtonProps =\n BitkitIconButtonAsButtonProps | BitkitIconButtonAsAnchorProps | BitkitIconButtonAsChildProps;\n\n// The forwarded ref is typed as HTMLElement to cover all three modes (button, anchor, slot/asChild),\n// since asChild can render any element (router Link, custom wrappers, etc.). Inner narrow casts\n// satisfy the wrapped Chakra components without affecting the consumer-visible ref shape.\nconst BitkitIconButton = forwardRef<HTMLElement, BitkitIconButtonProps>((props, ref) => {\n const buttonRecipe = useRecipe({ key: 'button' });\n const Icon = props.icon;\n // An unset `size` reaches us as `undefined` — the `button` recipe's `lg` default lives in CSS,\n // not on the prop — so resolve it here rather than letting it fall to the 16px branch.\n const iconSize = (props.size ?? 'lg') === 'lg' ? '24' : '16';\n const isDisabled = props.state === 'disabled';\n const isSkeleton = props.state === 'skeleton';\n const isPopupTrigger = opensPopup(props);\n\n // --- Slot mode (asChild) ---\n if (props.asChild) {\n const { asChild: _asChild, children, icon: _icon, label, state, tooltipProps, ...buttonRest } = props;\n const onlyChild = Children.only(children) as ReactElement<{ 'aria-label'?: string }>;\n const iconButtonElement = (\n <IconButton\n asChild\n aria-label={label}\n disabled={isDisabled}\n loading={state === 'loading'}\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n >\n {cloneElement(onlyChild, { 'aria-label': onlyChild.props['aria-label'] ?? label }, <Icon size={iconSize} />)}\n </IconButton>\n );\n return (\n <BitkitLabelTooltip text={label} disabled={isPopupTrigger} {...tooltipProps}>\n {isSkeleton ? (\n <Skeleton asChild loading>\n {iconButtonElement}\n </Skeleton>\n ) : (\n iconButtonElement\n )}\n </BitkitLabelTooltip>\n );\n }\n\n // --- Anchor mode (href) ---\n if (props.href !== undefined) {\n const {\n css,\n href,\n icon: _icon,\n isExternal,\n label,\n onClick,\n onKeyDown,\n rel,\n size,\n state: _state,\n target,\n tooltipProps,\n variant,\n ...anchorRest\n } = props;\n const effectiveTarget = isExternal ? '_blank' : target;\n const effectiveRel = isExternal ? (rel ? `${rel} noreferrer noopener` : 'noreferrer noopener') : rel;\n const handleClick = isDisabled\n ? (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n const handleKeyDown = isDisabled\n ? (e: KeyboardEvent<HTMLAnchorElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n : onKeyDown;\n const anchorElement = (\n <chakra.a\n ref={ref as Ref<HTMLAnchorElement>}\n {...anchorRest}\n aria-disabled={isDisabled || undefined}\n aria-label={label}\n css={[buttonRecipe({ size, variant }), { paddingBlock: 0, paddingInline: 0 }, css]}\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n rel={effectiveRel}\n target={effectiveTarget}\n >\n <Icon size={iconSize} />\n </chakra.a>\n );\n return (\n <BitkitLabelTooltip text={label} disabled={isPopupTrigger} {...tooltipProps}>\n {isSkeleton ? (\n <Skeleton asChild loading>\n {anchorElement}\n </Skeleton>\n ) : (\n anchorElement\n )}\n </BitkitLabelTooltip>\n );\n }\n\n // --- Button mode (default) ---\n const { disabledReason, icon: _icon, label, state, tooltipProps, ...buttonRest } = props;\n const reason = isDisabled ? disabledReason : undefined;\n // One tooltip whose text switches, rather than a second tooltip component: swapping components\n // when the state changes would remount the button and drop its focus. The label is still\n // announced as the button's name, and a popup trigger shows the reason since the popup can't open.\n return (\n <BitkitLabelTooltip text={reason ?? label} disabled={!reason && isPopupTrigger} {...tooltipProps}>\n <Skeleton asChild loading={isSkeleton}>\n <IconButton\n aria-label={label}\n disabled={isDisabled && !reason}\n loading={state === 'loading'}\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n {...(reason && focusableDisabledProps)}\n >\n <Icon size={iconSize} />\n </IconButton>\n </Skeleton>\n </BitkitLabelTooltip>\n );\n});\n\nBitkitIconButton.displayName = 'BitkitIconButton';\n\nexport default BitkitIconButton;\n"],"mappings":";;;;;;;;;AAkFA,IAAM,mBAAmB,YAAgD,OAAO,QAAQ;CACtF,MAAM,eAAe,UAAU,EAAE,KAAK,SAAS,CAAC;CAChD,MAAM,OAAO,MAAM;CAGnB,MAAM,YAAY,MAAM,QAAQ,UAAU,OAAO,OAAO;CACxD,MAAM,aAAa,MAAM,UAAU;CACnC,MAAM,aAAa,MAAM,UAAU;CACnC,MAAM,iBAAiB,WAAW,KAAK;CAGvC,IAAI,MAAM,SAAS;EACjB,MAAM,EAAE,SAAS,UAAU,UAAU,MAAM,OAAO,OAAO,OAAO,cAAc,GAAG,eAAe;EAChG,MAAM,YAAY,SAAS,KAAK,QAAQ;EACxC,MAAM,oBACJ,oBAAC,YAAD;GACE,SAAA;GACA,cAAY;GACZ,UAAU;GACV,SAAS,UAAU;GACd;GACL,GAAI;GAEH,UAAA,aAAa,WAAW,EAAE,cAAc,UAAU,MAAM,iBAAiB,MAAM,GAAG,oBAAC,MAAD,EAAM,MAAM,SAAW,CAAA,CAAC;EACjG,CAAA;EAEd,OACE,oBAAC,oBAAD;GAAoB,MAAM;GAAO,UAAU;GAAgB,GAAI;GAC5D,UAAA,aACC,oBAAC,UAAD;IAAU,SAAA;IAAQ,SAAA;IACf,UAAA;GACO,CAAA,IAEV;EAEgB,CAAA;CAExB;CAGA,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,EACJ,KACA,MACA,MAAM,OACN,YACA,OACA,SACA,WACA,KACA,MACA,OAAO,QACP,QACA,cACA,SACA,GAAG,eACD;EACJ,MAAM,kBAAkB,aAAa,WAAW;EAChD,MAAM,eAAe,aAAc,MAAM,GAAG,IAAI,wBAAwB,wBAAyB;EACjG,MAAM,cAAc,cACf,MAAqC;GACpC,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB,IACA;EACJ,MAAM,gBAAgB,cACjB,MAAwC;GACvC,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;IACtC,EAAE,eAAe;IACjB,EAAE,gBAAgB;GACpB;EACF,IACA;EACJ,MAAM,gBACJ,oBAAC,OAAO,GAAR;GACO;GACL,GAAI;GACJ,iBAAe,cAAc,KAAA;GAC7B,cAAY;GACZ,KAAK;IAAC,aAAa;KAAE;KAAM;IAAQ,CAAC;IAAG;KAAE,cAAc;KAAG,eAAe;IAAE;IAAG;GAAG;GACjF,MAAM,aAAa,KAAA,IAAY;GAC/B,SAAS;GACT,WAAW;GACX,KAAK;GACL,QAAQ;GAER,UAAA,oBAAC,MAAD,EAAM,MAAM,SAAW,CAAA;EACf,CAAA;EAEZ,OACE,oBAAC,oBAAD;GAAoB,MAAM;GAAO,UAAU;GAAgB,GAAI;GAC5D,UAAA,aACC,oBAAC,UAAD;IAAU,SAAA;IAAQ,SAAA;IACf,UAAA;GACO,CAAA,IAEV;EAEgB,CAAA;CAExB;CAGA,MAAM,EAAE,gBAAgB,MAAM,OAAO,OAAO,OAAO,cAAc,GAAG,eAAe;CACnF,MAAM,SAAS,aAAa,iBAAiB,KAAA;CAI7C,OACE,oBAAC,oBAAD;EAAoB,MAAM,UAAU;EAAO,UAAU,CAAC,UAAU;EAAgB,GAAI;EAClF,UAAA,oBAAC,UAAD;GAAU,SAAA;GAAQ,SAAS;GACzB,UAAA,oBAAC,YAAD;IACE,cAAY;IACZ,UAAU,cAAc,CAAC;IACzB,SAAS,UAAU;IACd;IACL,GAAI;IACJ,GAAK,UAAU;IAEf,UAAA,oBAAC,MAAD,EAAM,MAAM,SAAW,CAAA;GACb,CAAA;EACJ,CAAA;CACQ,CAAA;AAExB,CAAC;AAED,iBAAiB,cAAc"}
1
+ {"version":3,"file":"BitkitIconButton.js","names":[],"sources":["../../../lib/components/BitkitIconButton/BitkitIconButton.tsx"],"sourcesContent":["import { IconButton, type IconButtonProps } from '@chakra-ui/react/button';\nimport { Skeleton } from '@chakra-ui/react/skeleton';\nimport { chakra, type HTMLChakraProps, type RecipeProps, useRecipe } from '@chakra-ui/react/styled-system';\nimport {\n Children,\n cloneElement,\n forwardRef,\n type KeyboardEvent,\n type MouseEvent,\n type ReactElement,\n type Ref,\n} from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport opensPopup from '../../utilities/opensPopup';\nimport BitkitLabelTooltip, { type BitkitLabelTooltipProps } from '../BitkitLabelTooltip/BitkitLabelTooltip';\n\ntype BitkitIconButtonOmittedFromButton =\n | 'aria-label'\n | 'asChild'\n | 'children'\n | 'colorPalette'\n | 'disabled'\n | 'loading'\n | 'loadingText'\n | 'spinner'\n | 'spinnerPlacement';\n\ninterface BitkitIconButtonCommonProps {\n icon: BitkitIconComponent;\n label: string;\n /**\n * Overrides for the label tooltip. It is off by default when this control opens a popup — a menu,\n * select or popover trigger — where the panel is the affordance and a tooltip would hover over it.\n * Pass `{ disabled: false }` to bring it back. The accessible name comes from `label` either way.\n */\n tooltipProps?: Partial<Omit<BitkitLabelTooltipProps, 'children' | 'text'>>;\n}\n\ninterface BitkitIconButtonAsButtonProps\n extends BitkitIconButtonCommonProps, Omit<IconButtonProps, BitkitIconButtonOmittedFromButton> {\n asChild?: false;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\ninterface BitkitIconButtonAsAnchorProps\n extends\n BitkitIconButtonCommonProps,\n Omit<HTMLChakraProps<'a'>, 'aria-label' | 'children' | 'colorPalette'>,\n RecipeProps<'button'> {\n asChild?: false;\n href: string;\n isExternal?: boolean;\n state?: 'disabled' | 'skeleton';\n}\n\ninterface BitkitIconButtonAsChildProps\n extends BitkitIconButtonCommonProps, Omit<IconButtonProps, BitkitIconButtonOmittedFromButton> {\n asChild: true;\n children: ReactElement;\n href?: undefined;\n isExternal?: undefined;\n state?: 'disabled' | 'loading' | 'skeleton';\n}\n\nexport type BitkitIconButtonProps =\n BitkitIconButtonAsButtonProps | BitkitIconButtonAsAnchorProps | BitkitIconButtonAsChildProps;\n\n// The forwarded ref is typed as HTMLElement to cover all three modes (button, anchor, slot/asChild),\n// since asChild can render any element (router Link, custom wrappers, etc.). Inner narrow casts\n// satisfy the wrapped Chakra components without affecting the consumer-visible ref shape.\nconst BitkitIconButton = forwardRef<HTMLElement, BitkitIconButtonProps>((props, ref) => {\n const buttonRecipe = useRecipe({ key: 'button' });\n const Icon = props.icon;\n // An unset `size` reaches us as `undefined` — the `button` recipe's `lg` default lives in CSS,\n // not on the prop — so resolve it here rather than letting it fall to the 16px branch.\n const iconSize = (props.size ?? 'lg') === 'lg' ? '24' : '16';\n const isDisabled = props.state === 'disabled';\n const isSkeleton = props.state === 'skeleton';\n const isPopupTrigger = opensPopup(props);\n\n // --- Slot mode (asChild) ---\n if (props.asChild) {\n const { asChild: _asChild, children, icon: _icon, label, state, tooltipProps, ...buttonRest } = props;\n const onlyChild = Children.only(children) as ReactElement<{ 'aria-label'?: string }>;\n const iconButtonElement = (\n <IconButton\n asChild\n aria-label={label}\n disabled={isDisabled}\n loading={state === 'loading'}\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n >\n {cloneElement(onlyChild, { 'aria-label': onlyChild.props['aria-label'] ?? label }, <Icon size={iconSize} />)}\n </IconButton>\n );\n return (\n <BitkitLabelTooltip text={label} disabled={isPopupTrigger} {...tooltipProps}>\n {isSkeleton ? (\n <Skeleton asChild loading>\n {iconButtonElement}\n </Skeleton>\n ) : (\n iconButtonElement\n )}\n </BitkitLabelTooltip>\n );\n }\n\n // --- Anchor mode (href) ---\n if (props.href !== undefined) {\n const {\n css,\n href,\n icon: _icon,\n isExternal,\n label,\n onClick,\n onKeyDown,\n rel,\n size,\n state: _state,\n target,\n tooltipProps,\n variant,\n ...anchorRest\n } = props;\n const effectiveTarget = isExternal ? '_blank' : target;\n const effectiveRel = isExternal ? (rel ? `${rel} noreferrer noopener` : 'noreferrer noopener') : rel;\n const handleClick = isDisabled\n ? (e: MouseEvent<HTMLAnchorElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }\n : onClick;\n const handleKeyDown = isDisabled\n ? (e: KeyboardEvent<HTMLAnchorElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n : onKeyDown;\n const anchorElement = (\n <chakra.a\n ref={ref as Ref<HTMLAnchorElement>}\n {...anchorRest}\n aria-disabled={isDisabled || undefined}\n aria-label={label}\n css={[buttonRecipe({ size, variant }), { paddingBlock: 0, paddingInline: 0 }, css]}\n href={isDisabled ? undefined : href}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n rel={effectiveRel}\n target={effectiveTarget}\n >\n <Icon size={iconSize} />\n </chakra.a>\n );\n return (\n <BitkitLabelTooltip text={label} disabled={isPopupTrigger} {...tooltipProps}>\n {isSkeleton ? (\n <Skeleton asChild loading>\n {anchorElement}\n </Skeleton>\n ) : (\n anchorElement\n )}\n </BitkitLabelTooltip>\n );\n }\n\n // --- Button mode (default) ---\n const { icon: _icon, label, state, tooltipProps, ...buttonRest } = props;\n return (\n <BitkitLabelTooltip text={label} disabled={isPopupTrigger} {...tooltipProps}>\n <Skeleton asChild loading={isSkeleton}>\n <IconButton\n aria-label={label}\n disabled={isDisabled}\n loading={state === 'loading'}\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonRest}\n >\n <Icon size={iconSize} />\n </IconButton>\n </Skeleton>\n </BitkitLabelTooltip>\n );\n});\n\nBitkitIconButton.displayName = 'BitkitIconButton';\n\nexport default BitkitIconButton;\n"],"mappings":";;;;;;;;AAyEA,IAAM,mBAAmB,YAAgD,OAAO,QAAQ;CACtF,MAAM,eAAe,UAAU,EAAE,KAAK,SAAS,CAAC;CAChD,MAAM,OAAO,MAAM;CAGnB,MAAM,YAAY,MAAM,QAAQ,UAAU,OAAO,OAAO;CACxD,MAAM,aAAa,MAAM,UAAU;CACnC,MAAM,aAAa,MAAM,UAAU;CACnC,MAAM,iBAAiB,WAAW,KAAK;CAGvC,IAAI,MAAM,SAAS;EACjB,MAAM,EAAE,SAAS,UAAU,UAAU,MAAM,OAAO,OAAO,OAAO,cAAc,GAAG,eAAe;EAChG,MAAM,YAAY,SAAS,KAAK,QAAQ;EACxC,MAAM,oBACJ,oBAAC,YAAD;GACE,SAAA;GACA,cAAY;GACZ,UAAU;GACV,SAAS,UAAU;GACd;GACL,GAAI;GAEH,UAAA,aAAa,WAAW,EAAE,cAAc,UAAU,MAAM,iBAAiB,MAAM,GAAG,oBAAC,MAAD,EAAM,MAAM,SAAW,CAAA,CAAC;EACjG,CAAA;EAEd,OACE,oBAAC,oBAAD;GAAoB,MAAM;GAAO,UAAU;GAAgB,GAAI;GAC5D,UAAA,aACC,oBAAC,UAAD;IAAU,SAAA;IAAQ,SAAA;IACf,UAAA;GACO,CAAA,IAEV;EAEgB,CAAA;CAExB;CAGA,IAAI,MAAM,SAAS,KAAA,GAAW;EAC5B,MAAM,EACJ,KACA,MACA,MAAM,OACN,YACA,OACA,SACA,WACA,KACA,MACA,OAAO,QACP,QACA,cACA,SACA,GAAG,eACD;EACJ,MAAM,kBAAkB,aAAa,WAAW;EAChD,MAAM,eAAe,aAAc,MAAM,GAAG,IAAI,wBAAwB,wBAAyB;EACjG,MAAM,cAAc,cACf,MAAqC;GACpC,EAAE,eAAe;GACjB,EAAE,gBAAgB;EACpB,IACA;EACJ,MAAM,gBAAgB,cACjB,MAAwC;GACvC,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;IACtC,EAAE,eAAe;IACjB,EAAE,gBAAgB;GACpB;EACF,IACA;EACJ,MAAM,gBACJ,oBAAC,OAAO,GAAR;GACO;GACL,GAAI;GACJ,iBAAe,cAAc,KAAA;GAC7B,cAAY;GACZ,KAAK;IAAC,aAAa;KAAE;KAAM;IAAQ,CAAC;IAAG;KAAE,cAAc;KAAG,eAAe;IAAE;IAAG;GAAG;GACjF,MAAM,aAAa,KAAA,IAAY;GAC/B,SAAS;GACT,WAAW;GACX,KAAK;GACL,QAAQ;GAER,UAAA,oBAAC,MAAD,EAAM,MAAM,SAAW,CAAA;EACf,CAAA;EAEZ,OACE,oBAAC,oBAAD;GAAoB,MAAM;GAAO,UAAU;GAAgB,GAAI;GAC5D,UAAA,aACC,oBAAC,UAAD;IAAU,SAAA;IAAQ,SAAA;IACf,UAAA;GACO,CAAA,IAEV;EAEgB,CAAA;CAExB;CAGA,MAAM,EAAE,MAAM,OAAO,OAAO,OAAO,cAAc,GAAG,eAAe;CACnE,OACE,oBAAC,oBAAD;EAAoB,MAAM;EAAO,UAAU;EAAgB,GAAI;EAC7D,UAAA,oBAAC,UAAD;GAAU,SAAA;GAAQ,SAAS;GACzB,UAAA,oBAAC,YAAD;IACE,cAAY;IACZ,UAAU;IACV,SAAS,UAAU;IACd;IACL,GAAI;IAEJ,UAAA,oBAAC,MAAD,EAAM,MAAM,SAAW,CAAA;GACb,CAAA;EACJ,CAAA;CACQ,CAAA;AAExB,CAAC;AAED,iBAAiB,cAAc"}
@@ -1,5 +1,6 @@
1
1
  import { BoxProps } from '@chakra-ui/react/box';
2
2
  import { ReactNode } from 'react';
3
+ import { BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';
3
4
  export interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {
4
5
  /**
5
6
  * Avatar, app icon or thumbnail rendered before the title. Hidden in the collapsed layout.
@@ -38,14 +39,6 @@ export interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {
38
39
  * collapsed variants, so `sm` is the closer of the two.
39
40
  */
40
41
  children?: ReactNode;
41
- /**
42
- * Where the content block sits when `contentWidth` caps it: `center` over a centred content
43
- * column, `start` over one that starts against a sidebar beside the header. Pass the same
44
- * alignment as that column, so the title and the content share their left edge at every width.
45
- *
46
- * No effect with `contentWidth="full"` or without `contentWidth`, where nothing is capped.
47
- */
48
- contentAlign?: 'center' | 'start';
49
42
  /**
50
43
  * Shape of the title block, mirroring the design's `content` property. `title` is a page title
51
44
  * with an optional subtitle; `app` and `user` are the tighter entity layouts — a larger avatar
@@ -54,27 +47,12 @@ export interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {
54
47
  */
55
48
  contentVariant?: 'title' | 'app' | 'user';
56
49
  /**
57
- * Lines the header's content up with the page content below it. The root stays full-width, so
58
- * the background and the bottom border still run edge to edge — which a wrapper around the whole
59
- * header could not do.
60
- *
61
- * Pass the same value as that column's `max-width`, as rems — `rem()` builds one from a pixel
62
- * figure — and the content is capped with it, centred unless `contentAlign` says otherwise. Box-sizing is border-box throughout
63
- * Bitkit, so the value includes the header's own inline padding, which this layout puts at 32 on
64
- * both sides rather than the default 24 and 32.
65
- *
66
- * `'full'` is the same layout without the cap, for a content column that runs the full width: the
67
- * 32 on both sides, nothing to centre. The default 24 is for a header that runs over a sidebar and
68
- * lines up with its items instead.
69
- *
70
- * Its presence is the toggle: without it the header keeps the default inset.
71
- *
72
- * The cap applies at every breakpoint, deliberately — a content column's own `max-width` is not
73
- * breakpoint-scoped either, so scoping this one would misalign the two wherever the column is
74
- * narrow enough to bite on mobile. What does change with the breakpoint is the inline padding,
75
- * 16 below `tablet` and 32 above, which is the same pair such a column uses.
50
+ * Lines the header's content up with the page content below it: pass the `BitkitContentWrapper`'s value. The title,
51
+ * the actions and the tabs are capped at that width and centred, while the background and the bottom border still run
52
+ * edge to edge. `'full'` keeps the same 32px inset without a cap. Without it the header keeps the default inset, which
53
+ * lines up with a sidebar below it.
76
54
  */
77
- contentWidth?: `${number}rem` | 'full';
55
+ contentWidth?: BitkitContentWidth;
78
56
  /**
79
57
  * Inline controls between the trail and the actions — a view switcher, filters, a settings menu.
80
58
  * Their presence spreads the row into three groups instead of the usual trail-and-actions pair,
@@ -1,5 +1,6 @@
1
1
  import IconMenuHamburger from "../../icons/IconMenuHamburger.js";
2
2
  import { isEmptyNode } from "../../utilities/isEmptyNode.js";
3
+ import { contentMaxWidth } from "../../theme/common/ContentWidth.common.js";
3
4
  import BitkitIconButton from "../BitkitIconButton/BitkitIconButton.js";
4
5
  import BitkitOverflowTooltip from "../BitkitOverflowTooltip/BitkitOverflowTooltip.js";
5
6
  import useIsTruncated from "../../utilities/useIsTruncated.js";
@@ -10,7 +11,7 @@ import { Box } from "@chakra-ui/react/box";
10
11
  import { Text } from "@chakra-ui/react/text";
11
12
  //#region lib/components/BitkitPageHeader/BitkitPageHeader.tsx
12
13
  var BitkitPageHeader = forwardRef((props, ref) => {
13
- const { avatar, badge, breadcrumb, children, contentAlign, contentVariant, contentWidth, controls, css, isSticky, onMenuClick, secondaryText, sectionTitle, showDivider = true, tabs, title, variant, ...rest } = props;
14
+ const { avatar, badge, breadcrumb, children, contentVariant, contentWidth, controls, css, isSticky, onMenuClick, secondaryText, sectionTitle, showDivider = true, tabs, title, variant, ...rest } = props;
14
15
  const hasAvatar = !isEmptyNode(avatar);
15
16
  const hasBadge = !isEmptyNode(badge);
16
17
  const hasBreadcrumb = !isEmptyNode(breadcrumb);
@@ -24,6 +25,7 @@ var BitkitPageHeader = forwardRef((props, ref) => {
24
25
  const isCollapsed = variant === "minimal" || Boolean(isSticky);
25
26
  const showControls = !isEmptyNode(controls) && isCollapsed;
26
27
  const canShowTitleTooltip = isTitleTruncated && !(isCollapsed && hasBreadcrumb);
28
+ const capWidth = contentMaxWidth(contentWidth);
27
29
  const styles = useSlotRecipe({ key: "pageHeader" })({
28
30
  contentVariant,
29
31
  hasBreadcrumb,
@@ -31,7 +33,6 @@ var BitkitPageHeader = forwardRef((props, ref) => {
31
33
  hasDivider: showDivider || hasTabs,
32
34
  hasSidebar,
33
35
  alignsToContent: Boolean(contentWidth),
34
- contentAlign,
35
36
  isSticky,
36
37
  variant
37
38
  });
@@ -83,7 +84,7 @@ var BitkitPageHeader = forwardRef((props, ref) => {
83
84
  ...rest,
84
85
  children: [/* @__PURE__ */ jsxs(Box, {
85
86
  css: styles.headerBlock,
86
- maxWidth: contentWidth === "full" ? void 0 : contentWidth,
87
+ maxWidth: capWidth,
87
88
  children: [
88
89
  hasBreadcrumb && /* @__PURE__ */ jsx(Box, {
89
90
  css: styles.breadcrumb,
@@ -124,6 +125,7 @@ var BitkitPageHeader = forwardRef((props, ref) => {
124
125
  ]
125
126
  }), hasTabs && /* @__PURE__ */ jsx(Box, {
126
127
  css: styles.tabs,
128
+ maxWidth: capWidth,
127
129
  children: tabs
128
130
  })]
129
131
  });