@bitrise/bitkit-v2 0.3.503-beta.3248 → 0.3.503

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -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}>
@@ -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
@@ -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"}
@@ -32,23 +32,11 @@ var BitkitPromoBanner = (props) => {
32
32
  tablet: rem(11)
33
33
  },
34
34
  css: [{ "& .alert__title + *": { marginBlockStart: 0 } }, css],
35
- children: [illustration ? /* @__PURE__ */ jsx(Box, {
35
+ children: [/* @__PURE__ */ jsx(Box, {
36
+ display: "flex",
37
+ alignItems: "center",
38
+ alignSelf: { tablet: "stretch" },
36
39
  flexShrink: 0,
37
- marginInlineEnd: {
38
- base: "0",
39
- tablet: "12"
40
- },
41
- "aria-hidden": true,
42
- children: illustration
43
- }) : /* @__PURE__ */ jsx(Icon, {
44
- asChild: true,
45
- width: rem(72),
46
- height: rem(68),
47
- flexShrink: 0,
48
- marginInlineEnd: {
49
- base: "0",
50
- tablet: "12"
51
- },
52
40
  marginBlock: {
53
41
  base: "0",
54
42
  tablet: rem(-11)
@@ -57,7 +45,17 @@ var BitkitPromoBanner = (props) => {
57
45
  base: "0",
58
46
  tablet: rem(-12)
59
47
  },
60
- children: /* @__PURE__ */ jsx(ForwardRef, {})
48
+ marginInlineEnd: {
49
+ base: "0",
50
+ tablet: "16"
51
+ },
52
+ "aria-hidden": true,
53
+ children: illustration ?? /* @__PURE__ */ jsx(Icon, {
54
+ asChild: true,
55
+ width: rem(72),
56
+ height: rem(68),
57
+ children: /* @__PURE__ */ jsx(ForwardRef, {})
58
+ })
61
59
  }), /* @__PURE__ */ jsx(NotificationContent, {
62
60
  action: actionable,
63
61
  colorVariant: PROMO_BANNER_COLOR,
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitPromoBanner.js","names":["MascotSvg"],"sources":["../../../lib/components/BitkitPromoBanner/BitkitPromoBanner.tsx"],"sourcesContent":["import { Alert, type AlertRootProps } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { Icon } from '@chakra-ui/react/icon';\nimport { type ReactNode } from 'react';\n\nimport { rem } from '../../theme/themeUtils';\nimport { NotificationContent } from '../common/NotificationContent';\nimport { BUTTON_COLORS_MAP, type NotificationAction } from '../common/notificationMaps';\nimport MascotSvg from './mascot.svg?react';\n\nexport type PromoBannerAction = NotificationAction;\n\nconst PROMO_BANNER_COLOR = BUTTON_COLORS_MAP.info;\n\nexport interface BitkitPromoBannerProps extends Omit<AlertRootProps, 'title' | 'variant'> {\n actionable?: PromoBannerAction;\n dismissible?: boolean;\n /** Custom leading illustration. Defaults to the built-in mascot. */\n illustration?: ReactNode;\n message: ReactNode;\n onClose?: () => void;\n title: ReactNode;\n}\n\nconst BitkitPromoBanner = (props: BitkitPromoBannerProps) => {\n const { actionable, css, dismissible, illustration, message, onClose, title, ...rest } = props;\n\n return (\n <Alert.Root\n {...rest}\n variant=\"info\"\n border=\"none\"\n position=\"relative\"\n flexDirection={{ base: 'column', tablet: 'row' }}\n alignItems={{ base: 'stretch', tablet: 'center' }}\n rowGap={{ base: '16', tablet: '0' }}\n paddingBlockEnd={{ base: actionable ? '20' : '16', tablet: rem(11) }}\n css={[{ '& .alert__title + *': { marginBlockStart: 0 } }, css]}\n >\n {illustration ? (\n <Box flexShrink={0} marginInlineEnd={{ base: '0', tablet: '12' }} aria-hidden>\n {illustration}\n </Box>\n ) : (\n <Icon\n asChild\n width={rem(72)}\n height={rem(68)}\n flexShrink={0}\n marginInlineEnd={{ base: '0', tablet: '12' }}\n marginBlock={{ base: '0', tablet: rem(-11) }}\n marginInlineStart={{ base: '0', tablet: rem(-12) }}\n >\n <MascotSvg />\n </Icon>\n )}\n <NotificationContent\n action={actionable}\n colorVariant={PROMO_BANNER_COLOR}\n dismissible={dismissible}\n messageText={message}\n onClose={onClose}\n stackable\n titleText={title}\n />\n </Alert.Root>\n );\n};\n\nBitkitPromoBanner.displayName = 'BitkitPromoBanner';\n\nexport default BitkitPromoBanner;\n"],"mappings":";;;;;;;;;AAYA,IAAM,qBAAqB,kBAAkB;AAY7C,IAAM,qBAAqB,UAAkC;CAC3D,MAAM,EAAE,YAAY,KAAK,aAAa,cAAc,SAAS,SAAS,OAAO,GAAG,SAAS;CAEzF,OACE,qBAAC,MAAM,MAAP;EACE,GAAI;EACJ,SAAQ;EACR,QAAO;EACP,UAAS;EACT,eAAe;GAAE,MAAM;GAAU,QAAQ;EAAM;EAC/C,YAAY;GAAE,MAAM;GAAW,QAAQ;EAAS;EAChD,QAAQ;GAAE,MAAM;GAAM,QAAQ;EAAI;EAClC,iBAAiB;GAAE,MAAM,aAAa,OAAO;GAAM,QAAQ,IAAI,EAAE;EAAE;EACnE,KAAK,CAAC,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,EAAE,GAAG,GAAG;EAT/D,UAAA,CAWG,eACC,oBAAC,KAAD;GAAK,YAAY;GAAG,iBAAiB;IAAE,MAAM;IAAK,QAAQ;GAAK;GAAG,eAAA;GAC/D,UAAA;EACE,CAAA,IAEL,oBAAC,MAAD;GACE,SAAA;GACA,OAAO,IAAI,EAAE;GACb,QAAQ,IAAI,EAAE;GACd,YAAY;GACZ,iBAAiB;IAAE,MAAM;IAAK,QAAQ;GAAK;GAC3C,aAAa;IAAE,MAAM;IAAK,QAAQ,IAAI,GAAG;GAAE;GAC3C,mBAAmB;IAAE,MAAM;IAAK,QAAQ,IAAI,GAAG;GAAE;GAEjD,UAAA,oBAACA,YAAD,CAAY,CAAA;EACR,CAAA,GAER,oBAAC,qBAAD;GACE,QAAQ;GACR,cAAc;GACD;GACb,aAAa;GACJ;GACT,WAAA;GACA,WAAW;EACZ,CAAA,CACS;;AAEhB;AAEA,kBAAkB,cAAc"}
1
+ {"version":3,"file":"BitkitPromoBanner.js","names":["MascotSvg"],"sources":["../../../lib/components/BitkitPromoBanner/BitkitPromoBanner.tsx"],"sourcesContent":["import { Alert, type AlertRootProps } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { Icon } from '@chakra-ui/react/icon';\nimport { type ReactNode } from 'react';\n\nimport { rem } from '../../theme/themeUtils';\nimport { NotificationContent } from '../common/NotificationContent';\nimport { BUTTON_COLORS_MAP, type NotificationAction } from '../common/notificationMaps';\nimport MascotSvg from './mascot.svg?react';\n\nexport type PromoBannerAction = NotificationAction;\n\nconst PROMO_BANNER_COLOR = BUTTON_COLORS_MAP.info;\n\nexport interface BitkitPromoBannerProps extends Omit<AlertRootProps, 'title' | 'variant'> {\n actionable?: PromoBannerAction;\n dismissible?: boolean;\n /** Custom leading illustration. Defaults to the built-in mascot. */\n illustration?: ReactNode;\n message: ReactNode;\n onClose?: () => void;\n title: ReactNode;\n}\n\nconst BitkitPromoBanner = (props: BitkitPromoBannerProps) => {\n const { actionable, css, dismissible, illustration, message, onClose, title, ...rest } = props;\n\n return (\n <Alert.Root\n {...rest}\n variant=\"info\"\n border=\"none\"\n position=\"relative\"\n flexDirection={{ base: 'column', tablet: 'row' }}\n alignItems={{ base: 'stretch', tablet: 'center' }}\n rowGap={{ base: '16', tablet: '0' }}\n paddingBlockEnd={{ base: actionable ? '20' : '16', tablet: rem(11) }}\n css={[{ '& .alert__title + *': { marginBlockStart: 0 } }, css]}\n >\n <Box\n display=\"flex\"\n alignItems=\"center\"\n alignSelf={{ tablet: 'stretch' }}\n flexShrink={0}\n marginBlock={{ base: '0', tablet: rem(-11) }}\n marginInlineStart={{ base: '0', tablet: rem(-12) }}\n marginInlineEnd={{ base: '0', tablet: '16' }}\n aria-hidden\n >\n {illustration ?? (\n <Icon asChild width={rem(72)} height={rem(68)}>\n <MascotSvg />\n </Icon>\n )}\n </Box>\n <NotificationContent\n action={actionable}\n colorVariant={PROMO_BANNER_COLOR}\n dismissible={dismissible}\n messageText={message}\n onClose={onClose}\n stackable\n titleText={title}\n />\n </Alert.Root>\n );\n};\n\nBitkitPromoBanner.displayName = 'BitkitPromoBanner';\n\nexport default BitkitPromoBanner;\n"],"mappings":";;;;;;;;;AAYA,IAAM,qBAAqB,kBAAkB;AAY7C,IAAM,qBAAqB,UAAkC;CAC3D,MAAM,EAAE,YAAY,KAAK,aAAa,cAAc,SAAS,SAAS,OAAO,GAAG,SAAS;CAEzF,OACE,qBAAC,MAAM,MAAP;EACE,GAAI;EACJ,SAAQ;EACR,QAAO;EACP,UAAS;EACT,eAAe;GAAE,MAAM;GAAU,QAAQ;EAAM;EAC/C,YAAY;GAAE,MAAM;GAAW,QAAQ;EAAS;EAChD,QAAQ;GAAE,MAAM;GAAM,QAAQ;EAAI;EAClC,iBAAiB;GAAE,MAAM,aAAa,OAAO;GAAM,QAAQ,IAAI,EAAE;EAAE;EACnE,KAAK,CAAC,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,EAAE,GAAG,GAAG;EAT/D,UAAA,CAWE,oBAAC,KAAD;GACE,SAAQ;GACR,YAAW;GACX,WAAW,EAAE,QAAQ,UAAU;GAC/B,YAAY;GACZ,aAAa;IAAE,MAAM;IAAK,QAAQ,IAAI,GAAG;GAAE;GAC3C,mBAAmB;IAAE,MAAM;IAAK,QAAQ,IAAI,GAAG;GAAE;GACjD,iBAAiB;IAAE,MAAM;IAAK,QAAQ;GAAK;GAC3C,eAAA;GAEC,UAAA,gBACC,oBAAC,MAAD;IAAM,SAAA;IAAQ,OAAO,IAAI,EAAE;IAAG,QAAQ,IAAI,EAAE;IAC1C,UAAA,oBAACA,YAAD,CAAY,CAAA;GACR,CAAA;EAEL,CAAA,GACL,oBAAC,qBAAD;GACE,QAAQ;GACR,cAAc;GACD;GACb,aAAa;GACJ;GACT,WAAA;GACA,WAAW;EACZ,CAAA,CACS;;AAEhB;AAEA,kBAAkB,cAAc"}
@@ -36,8 +36,7 @@ export type BitkitTooltipProps = {
36
36
  * that cannot take focus — on a focusable one this adds a second, redundant tab stop for the same
37
37
  * control. A disabled `<button>` dispatches no pointer events and is out of the tab order, so
38
38
  * without a wrapper the tooltip never opens — in the one state whose explanation the user actually
39
- * needs. For a disabled `BitkitButton` or `BitkitIconButton`, pass its `disabledReason` instead:
40
- * the button stays focusable and announced as disabled, with no wrapper.
39
+ * needs.
41
40
  *
42
41
  * Chakra v2's `Tooltip` had this built in as `shouldWrapChildren`, rendering a
43
42
  * `<span display="inline-block" tabIndex={0}>` that carried the trigger props. Ark's tooltip has no
@@ -47,7 +46,8 @@ export type BitkitTooltipProps = {
47
46
  *
48
47
  * The wrapper is a plain focusable span, so focusing it announces the trigger's label and the
49
48
  * tooltip text, not the trigger's own state — a disabled button inside is not re-announced as
50
- * disabled; that is what `disabledReason` on the buttons fixes.
49
+ * disabled. Conveying that would mean keeping the trigger focusable with `aria-disabled` instead
50
+ * of the native attribute, which is a change to what `disabled` means, not a tooltip concern.
51
51
  */
52
52
  wrapTrigger?: boolean;
53
53
  };
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitTooltip.js","names":[],"sources":["../../../lib/components/BitkitTooltip/BitkitTooltip.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Tooltip, type TooltipRootProps } from '@chakra-ui/react/tooltip';\nimport { isValidElement, type ReactNode, type Ref, useContext, useRef } from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\n\nexport type BitkitTooltipProps = {\n /**\n * The trigger the tooltip is anchored to. Rendered via `asChild`, so it must accept a ref —\n * unless `wrapTrigger` puts a wrapper in between.\n */\n children: ReactNode;\n /** Dismiss the tooltip when the trigger is clicked. */\n closeOnClick?: TooltipRootProps['closeOnClick'];\n /**\n * Render the tooltip inline instead of in a portal. Only needed when it must stay inside its\n * parent's DOM subtree — a dialog already sets this for you.\n */\n disablePortal?: boolean;\n /** Render the trigger without a tooltip — for hints that only apply in some states. */\n disabled?: TooltipRootProps['disabled'];\n /**\n * Icon rendered after `text`, at size 16. Use it for the affordance the tooltip describes — an\n * `IconArrowNortheast` when the trigger opens something in a new tab, for instance.\n */\n icon?: BitkitIconComponent;\n /** Called when the tooltip opens or closes, including from hover and focus. */\n onOpenChange?: TooltipRootProps['onOpenChange'];\n /** Controlled open state. Leave unset to let hover and focus drive it. */\n open?: TooltipRootProps['open'];\n /** Delay in ms before an uncontrolled tooltip opens on hover. */\n openDelay?: TooltipRootProps['openDelay'];\n /** Where the tooltip sits relative to the trigger (default `top`). It flips if space is short. */\n placement?: NonNullable<TooltipRootProps['positioning']>['placement'];\n /** The tooltip's text. Plain string only — use `icon` for a glyph after it. */\n text: string;\n /**\n * Render the trigger inside a focusable wrapper instead of on the trigger itself. Only for triggers\n * that cannot take focus — on a focusable one this adds a second, redundant tab stop for the same\n * control. A disabled `<button>` dispatches no pointer events and is out of the tab order, so\n * without a wrapper the tooltip never opens — in the one state whose explanation the user actually\n * needs. For a disabled `BitkitButton` or `BitkitIconButton`, pass its `disabledReason` instead:\n * the button stays focusable and announced as disabled, with no wrapper.\n *\n * Chakra v2's `Tooltip` had this built in as `shouldWrapChildren`, rendering a\n * `<span display=\"inline-block\" tabIndex={0}>` that carried the trigger props. Ark's tooltip has no\n * equivalent, and Chakra's own v2->v3 codemod replaces it with a bare `<span>` — which restores\n * hover but silently drops the tab stop, so the tooltip stays unreachable by keyboard. This prop is\n * that escape hatch, `tabIndex` included.\n *\n * The wrapper is a plain focusable span, so focusing it announces the trigger's label and the\n * tooltip text, not the trigger's own state — a disabled button inside is not re-announced as\n * disabled; that is what `disabledReason` on the buttons fixes.\n */\n wrapTrigger?: boolean;\n};\n\n/**\n * A hover/focus tooltip anchored to its child: `text` carries the message and `icon` appends a\n * glyph after it. Text only — a tooltip closes when focus leaves its trigger and ignores touch, so a\n * link or a button inside one couldn't be reached. For those use `BitkitToggletip`, which looks the\n * same and opens on click.\n *\n * The content renders in a portal, so it escapes any ancestor that traps stacking (`isolation`,\n * `transform`, a `z-index`ed positioned box) or clips overflow. Dialogs turn that off through\n * `BitkitPortalContext`; `disablePortal` is the manual override.\n */\nconst BitkitTooltip = ({\n children,\n closeOnClick,\n disablePortal,\n disabled,\n icon: Icon,\n onOpenChange,\n open,\n openDelay,\n placement = 'top',\n text,\n wrapTrigger,\n}: BitkitTooltipProps) => {\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n\n // Anchor positioning to the trigger node by ref rather than relying on Zag's\n // id/data-scope lookup. When the trigger is also wrapped by another `asChild`\n // trigger (e.g. Menu.Trigger), the outer trigger's id and data-scope/data-part\n // overwrite ours on the shared node, so `getTriggerEl()` can't find it and the\n // tooltip loses its anchor. `getAnchorElement` takes precedence over the id\n // lookup, so this is safe in the standalone case too.\n const triggerRef = useRef<HTMLElement>(null);\n\n return (\n <Tooltip.Root\n closeOnClick={closeOnClick}\n disabled={disabled}\n onOpenChange={onOpenChange}\n open={open}\n openDelay={openDelay}\n positioning={{ getAnchorElement: () => triggerRef.current, placement, offset: { mainAxis: 4, crossAxis: 0 } }}\n >\n <Tooltip.Trigger ref={triggerRef as Ref<HTMLButtonElement>} asChild>\n {wrapTrigger || !isValidElement(children) ? (\n // inline-flex so the wrapper shrink-wraps the trigger: a plain inline span adds descender\n // space below it and shifts the alignment of whatever sits next to it.\n <Box\n as=\"span\"\n display=\"inline-flex\"\n tabIndex={0}\n css={{\n // The wrapper owns the tab stop, but the focus ring belongs on the trigger: an outline\n // follows the border-radius of the element it is drawn on, and the wrapper has none —\n // so leaving it here squares off a pill or a round trigger.\n _focusVisible: {\n outline: 'none',\n '& > *': {\n outlineColor: 'var(--focus-ring-color)',\n outlineOffset: 'var(--focus-ring-offset, 2px)',\n outlineStyle: 'var(--focus-ring-style, solid)',\n outlineWidth: 'var(--focus-ring-width, 2px)',\n },\n },\n }}\n >\n {children}\n </Box>\n ) : (\n children\n )}\n </Tooltip.Trigger>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <Tooltip.Positioner>\n <Tooltip.Content>\n <Tooltip.Arrow>\n <Tooltip.ArrowTip />\n </Tooltip.Arrow>\n <Box display=\"flex\" gap=\"8\">\n {text}\n {Icon && <Icon flexShrink=\"0\" size=\"16\" />}\n </Box>\n </Tooltip.Content>\n </Tooltip.Positioner>\n </Portal>\n </Tooltip.Root>\n );\n};\n\nBitkitTooltip.displayName = 'BitkitTooltip';\n\nexport default BitkitTooltip;\n"],"mappings":";;;;;;;;;;;;;;;;;AAqEA,IAAM,iBAAiB,EACrB,UACA,cACA,eACA,UACA,MAAM,MACN,cACA,MACA,WACA,YAAY,OACZ,MACA,kBACwB;CACxB,MAAM,EAAE,eAAe,6BAA6B,WAAW,mBAAmB;CAQlF,MAAM,aAAa,OAAoB,IAAI;CAE3C,OACE,qBAAC,QAAQ,MAAT;EACgB;EACJ;EACI;EACR;EACK;EACX,aAAa;GAAE,wBAAwB,WAAW;GAAS;GAAW,QAAQ;IAAE,UAAU;IAAG,WAAW;GAAE;EAAE;EAN9G,UAAA,CAQE,oBAAC,QAAQ,SAAT;GAAiB,KAAK;GAAsC,SAAA;GACzD,UAAA,eAAe,CAAC,eAAe,QAAQ,IAGtC,oBAAC,KAAD;IACE,IAAG;IACH,SAAQ;IACR,UAAU;IACV,KAAK,EAIH,eAAe;KACb,SAAS;KACT,SAAS;MACP,cAAc;MACd,eAAe;MACf,cAAc;MACd,cAAc;KAChB;IACF,EACF;IAEC;GACE,CAAA,IAEL;EAEa,CAAA,GACjB,oBAAC,QAAD;GAAQ,UAAU,iBAAiB;GACjC,UAAA,oBAAC,QAAQ,YAAT,EAAA,UACE,qBAAC,QAAQ,SAAT,EAAA,UAAA,CACE,oBAAC,QAAQ,OAAT,EAAA,UACE,oBAAC,QAAQ,UAAT,CAAmB,CAAA,EACN,CAAA,GACf,qBAAC,KAAD;IAAK,SAAQ;IAAO,KAAI;IAAxB,UAAA,CACG,MACA,QAAQ,oBAAC,MAAD;KAAM,YAAW;KAAI,MAAK;IAAM,CAAA,CACtC;GACU,CAAA,CAAA,EAAA,CAAA,EACC,CAAA;EACd,CAAA,CACI;;AAElB;AAEA,cAAc,cAAc"}
1
+ {"version":3,"file":"BitkitTooltip.js","names":[],"sources":["../../../lib/components/BitkitTooltip/BitkitTooltip.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Tooltip, type TooltipRootProps } from '@chakra-ui/react/tooltip';\nimport { isValidElement, type ReactNode, type Ref, useContext, useRef } from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\n\nexport type BitkitTooltipProps = {\n /**\n * The trigger the tooltip is anchored to. Rendered via `asChild`, so it must accept a ref —\n * unless `wrapTrigger` puts a wrapper in between.\n */\n children: ReactNode;\n /** Dismiss the tooltip when the trigger is clicked. */\n closeOnClick?: TooltipRootProps['closeOnClick'];\n /**\n * Render the tooltip inline instead of in a portal. Only needed when it must stay inside its\n * parent's DOM subtree — a dialog already sets this for you.\n */\n disablePortal?: boolean;\n /** Render the trigger without a tooltip — for hints that only apply in some states. */\n disabled?: TooltipRootProps['disabled'];\n /**\n * Icon rendered after `text`, at size 16. Use it for the affordance the tooltip describes — an\n * `IconArrowNortheast` when the trigger opens something in a new tab, for instance.\n */\n icon?: BitkitIconComponent;\n /** Called when the tooltip opens or closes, including from hover and focus. */\n onOpenChange?: TooltipRootProps['onOpenChange'];\n /** Controlled open state. Leave unset to let hover and focus drive it. */\n open?: TooltipRootProps['open'];\n /** Delay in ms before an uncontrolled tooltip opens on hover. */\n openDelay?: TooltipRootProps['openDelay'];\n /** Where the tooltip sits relative to the trigger (default `top`). It flips if space is short. */\n placement?: NonNullable<TooltipRootProps['positioning']>['placement'];\n /** The tooltip's text. Plain string only — use `icon` for a glyph after it. */\n text: string;\n /**\n * Render the trigger inside a focusable wrapper instead of on the trigger itself. Only for triggers\n * that cannot take focus — on a focusable one this adds a second, redundant tab stop for the same\n * control. A disabled `<button>` dispatches no pointer events and is out of the tab order, so\n * without a wrapper the tooltip never opens — in the one state whose explanation the user actually\n * needs.\n *\n * Chakra v2's `Tooltip` had this built in as `shouldWrapChildren`, rendering a\n * `<span display=\"inline-block\" tabIndex={0}>` that carried the trigger props. Ark's tooltip has no\n * equivalent, and Chakra's own v2->v3 codemod replaces it with a bare `<span>` — which restores\n * hover but silently drops the tab stop, so the tooltip stays unreachable by keyboard. This prop is\n * that escape hatch, `tabIndex` included.\n *\n * The wrapper is a plain focusable span, so focusing it announces the trigger's label and the\n * tooltip text, not the trigger's own state — a disabled button inside is not re-announced as\n * disabled. Conveying that would mean keeping the trigger focusable with `aria-disabled` instead\n * of the native attribute, which is a change to what `disabled` means, not a tooltip concern.\n */\n wrapTrigger?: boolean;\n};\n\n/**\n * A hover/focus tooltip anchored to its child: `text` carries the message and `icon` appends a\n * glyph after it. Text only — a tooltip closes when focus leaves its trigger and ignores touch, so a\n * link or a button inside one couldn't be reached. For those use `BitkitToggletip`, which looks the\n * same and opens on click.\n *\n * The content renders in a portal, so it escapes any ancestor that traps stacking (`isolation`,\n * `transform`, a `z-index`ed positioned box) or clips overflow. Dialogs turn that off through\n * `BitkitPortalContext`; `disablePortal` is the manual override.\n */\nconst BitkitTooltip = ({\n children,\n closeOnClick,\n disablePortal,\n disabled,\n icon: Icon,\n onOpenChange,\n open,\n openDelay,\n placement = 'top',\n text,\n wrapTrigger,\n}: BitkitTooltipProps) => {\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n\n // Anchor positioning to the trigger node by ref rather than relying on Zag's\n // id/data-scope lookup. When the trigger is also wrapped by another `asChild`\n // trigger (e.g. Menu.Trigger), the outer trigger's id and data-scope/data-part\n // overwrite ours on the shared node, so `getTriggerEl()` can't find it and the\n // tooltip loses its anchor. `getAnchorElement` takes precedence over the id\n // lookup, so this is safe in the standalone case too.\n const triggerRef = useRef<HTMLElement>(null);\n\n return (\n <Tooltip.Root\n closeOnClick={closeOnClick}\n disabled={disabled}\n onOpenChange={onOpenChange}\n open={open}\n openDelay={openDelay}\n positioning={{ getAnchorElement: () => triggerRef.current, placement, offset: { mainAxis: 4, crossAxis: 0 } }}\n >\n <Tooltip.Trigger ref={triggerRef as Ref<HTMLButtonElement>} asChild>\n {wrapTrigger || !isValidElement(children) ? (\n // inline-flex so the wrapper shrink-wraps the trigger: a plain inline span adds descender\n // space below it and shifts the alignment of whatever sits next to it.\n <Box\n as=\"span\"\n display=\"inline-flex\"\n tabIndex={0}\n css={{\n // The wrapper owns the tab stop, but the focus ring belongs on the trigger: an outline\n // follows the border-radius of the element it is drawn on, and the wrapper has none —\n // so leaving it here squares off a pill or a round trigger.\n _focusVisible: {\n outline: 'none',\n '& > *': {\n outlineColor: 'var(--focus-ring-color)',\n outlineOffset: 'var(--focus-ring-offset, 2px)',\n outlineStyle: 'var(--focus-ring-style, solid)',\n outlineWidth: 'var(--focus-ring-width, 2px)',\n },\n },\n }}\n >\n {children}\n </Box>\n ) : (\n children\n )}\n </Tooltip.Trigger>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <Tooltip.Positioner>\n <Tooltip.Content>\n <Tooltip.Arrow>\n <Tooltip.ArrowTip />\n </Tooltip.Arrow>\n <Box display=\"flex\" gap=\"8\">\n {text}\n {Icon && <Icon flexShrink=\"0\" size=\"16\" />}\n </Box>\n </Tooltip.Content>\n </Tooltip.Positioner>\n </Portal>\n </Tooltip.Root>\n );\n};\n\nBitkitTooltip.displayName = 'BitkitTooltip';\n\nexport default BitkitTooltip;\n"],"mappings":";;;;;;;;;;;;;;;;;AAqEA,IAAM,iBAAiB,EACrB,UACA,cACA,eACA,UACA,MAAM,MACN,cACA,MACA,WACA,YAAY,OACZ,MACA,kBACwB;CACxB,MAAM,EAAE,eAAe,6BAA6B,WAAW,mBAAmB;CAQlF,MAAM,aAAa,OAAoB,IAAI;CAE3C,OACE,qBAAC,QAAQ,MAAT;EACgB;EACJ;EACI;EACR;EACK;EACX,aAAa;GAAE,wBAAwB,WAAW;GAAS;GAAW,QAAQ;IAAE,UAAU;IAAG,WAAW;GAAE;EAAE;EAN9G,UAAA,CAQE,oBAAC,QAAQ,SAAT;GAAiB,KAAK;GAAsC,SAAA;GACzD,UAAA,eAAe,CAAC,eAAe,QAAQ,IAGtC,oBAAC,KAAD;IACE,IAAG;IACH,SAAQ;IACR,UAAU;IACV,KAAK,EAIH,eAAe;KACb,SAAS;KACT,SAAS;MACP,cAAc;MACd,eAAe;MACf,cAAc;MACd,cAAc;KAChB;IACF,EACF;IAEC;GACE,CAAA,IAEL;EAEa,CAAA,GACjB,oBAAC,QAAD;GAAQ,UAAU,iBAAiB;GACjC,UAAA,oBAAC,QAAQ,YAAT,EAAA,UACE,qBAAC,QAAQ,SAAT,EAAA,UAAA,CACE,oBAAC,QAAQ,OAAT,EAAA,UACE,oBAAC,QAAQ,UAAT,CAAmB,CAAA,EACN,CAAA,GACf,qBAAC,KAAD;IAAK,SAAQ;IAAO,KAAI;IAAxB,UAAA,CACG,MACA,QAAQ,oBAAC,MAAD;KAAM,YAAW;KAAI,MAAK;IAAM,CAAA,CACtC;GACU,CAAA,CAAA,EAAA,CAAA,EACC,CAAA;EACd,CAAA,CACI;;AAElB;AAEA,cAAc,cAAc"}
package/dist/main.js CHANGED
@@ -310,8 +310,8 @@ import BitkitAvatar from "./components/BitkitAvatar/BitkitAvatar.js";
310
310
  import BitkitBadge from "./components/BitkitBadge/BitkitBadge.js";
311
311
  import BitkitControlButton from "./components/BitkitControlButton/BitkitControlButton.js";
312
312
  import BitkitBreadcrumb from "./components/BitkitBreadcrumb/BitkitBreadcrumb.js";
313
- import BitkitTooltip from "./components/BitkitTooltip/BitkitTooltip.js";
314
313
  import BitkitButton from "./components/BitkitButton/BitkitButton.js";
314
+ import BitkitTooltip from "./components/BitkitTooltip/BitkitTooltip.js";
315
315
  import BitkitGroupHeading from "./components/BitkitGroupHeading/BitkitGroupHeading.js";
316
316
  import BitkitSelectMenuAction from "./components/BitkitSelectMenu/BitkitSelectMenuAction.js";
317
317
  import BitkitSelectMenu from "./components/BitkitSelectMenu/BitkitSelectMenu.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit-v2",
3
3
  "private": false,
4
- "version": "0.3.503-beta.3248",
4
+ "version": "0.3.503",
5
5
  "description": "Bitrise Design System Components built with Chakra UI V3",
6
6
  "keywords": [
7
7
  "react",
@@ -1,22 +0,0 @@
1
- import { MouseEvent } from 'react';
2
- /**
3
- * Disables a button without the native `disabled` attribute, for a button whose tooltip explains
4
- * why: a natively disabled `<button>` gets no pointer events and no focus, so that tooltip could
5
- * never open. It stays focusable and is announced as unavailable via `aria-disabled`.
6
- *
7
- * Spread it after the consumer's props. `onClick` cancels the click, which a mouse press, Enter,
8
- * Space and a form's implicit submission all go through, so nothing activates the button; it also
9
- * replaces a dialog or popover trigger's handler, which bails on `defaultPrevented` anyway. It stops
10
- * propagation too: a natively disabled button fires no click at all, so a clickable row or card
11
- * around it must not get one either.
12
- * `onKeyDown` is cleared so a menu trigger's arrow keys don't open the menu. `data-disabled` must be
13
- * exactly `""`: the recipe's `_disabled` matches any value, but Zag's menu trigger only treats `""`
14
- * as disabled.
15
- */
16
- declare const focusableDisabledProps: {
17
- readonly 'aria-disabled': true;
18
- readonly 'data-disabled': "";
19
- readonly onClick: (event: MouseEvent) => void;
20
- readonly onKeyDown: undefined;
21
- };
22
- export default focusableDisabledProps;
@@ -1,28 +0,0 @@
1
- //#region lib/utilities/focusableDisabledProps.ts
2
- /**
3
- * Disables a button without the native `disabled` attribute, for a button whose tooltip explains
4
- * why: a natively disabled `<button>` gets no pointer events and no focus, so that tooltip could
5
- * never open. It stays focusable and is announced as unavailable via `aria-disabled`.
6
- *
7
- * Spread it after the consumer's props. `onClick` cancels the click, which a mouse press, Enter,
8
- * Space and a form's implicit submission all go through, so nothing activates the button; it also
9
- * replaces a dialog or popover trigger's handler, which bails on `defaultPrevented` anyway. It stops
10
- * propagation too: a natively disabled button fires no click at all, so a clickable row or card
11
- * around it must not get one either.
12
- * `onKeyDown` is cleared so a menu trigger's arrow keys don't open the menu. `data-disabled` must be
13
- * exactly `""`: the recipe's `_disabled` matches any value, but Zag's menu trigger only treats `""`
14
- * as disabled.
15
- */
16
- var focusableDisabledProps = {
17
- "aria-disabled": true,
18
- "data-disabled": "",
19
- onClick: (event) => {
20
- event.preventDefault();
21
- event.stopPropagation();
22
- },
23
- onKeyDown: void 0
24
- };
25
- //#endregion
26
- export { focusableDisabledProps as default };
27
-
28
- //# sourceMappingURL=focusableDisabledProps.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"focusableDisabledProps.js","names":[],"sources":["../../lib/utilities/focusableDisabledProps.ts"],"sourcesContent":["import { type MouseEvent } from 'react';\n\n/**\n * Disables a button without the native `disabled` attribute, for a button whose tooltip explains\n * why: a natively disabled `<button>` gets no pointer events and no focus, so that tooltip could\n * never open. It stays focusable and is announced as unavailable via `aria-disabled`.\n *\n * Spread it after the consumer's props. `onClick` cancels the click, which a mouse press, Enter,\n * Space and a form's implicit submission all go through, so nothing activates the button; it also\n * replaces a dialog or popover trigger's handler, which bails on `defaultPrevented` anyway. It stops\n * propagation too: a natively disabled button fires no click at all, so a clickable row or card\n * around it must not get one either.\n * `onKeyDown` is cleared so a menu trigger's arrow keys don't open the menu. `data-disabled` must be\n * exactly `\"\"`: the recipe's `_disabled` matches any value, but Zag's menu trigger only treats `\"\"`\n * as disabled.\n */\nconst focusableDisabledProps = {\n 'aria-disabled': true,\n 'data-disabled': '',\n onClick: (event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n },\n onKeyDown: undefined,\n} as const;\n\nexport default focusableDisabledProps;\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,IAAM,yBAAyB;CAC7B,iBAAiB;CACjB,iBAAiB;CACjB,UAAU,UAAsB;EAC9B,MAAM,eAAe;EACrB,MAAM,gBAAgB;CACxB;CACA,WAAW,KAAA;AACb"}