@bitrise/bitkit-v2 0.3.504 → 0.3.506
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 +1 -1
- package/dist/components/BitkitButton/BitkitButton.d.ts +2 -2
- package/dist/components/BitkitButton/BitkitButton.js +4 -4
- package/dist/components/BitkitButton/BitkitButton.js.map +1 -1
- package/dist/components/BitkitIconButton/BitkitIconButton.d.ts +1 -2
- package/dist/components/BitkitIconButton/BitkitIconButton.js.map +1 -1
- package/dist/theme/slot-recipes/Tour.recipe.js +1 -1
- package/dist/theme/slot-recipes/Tour.recipe.js.map +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -111,7 +111,7 @@ 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.
|
|
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. Don't wrap a disabled button in `BitkitTooltip wrapTrigger`, and don't turn the icon button's label tooltip off for it:
|
|
115
115
|
```tsx
|
|
116
116
|
<BitkitIconButton disabledReason="Only owners can delete" icon={IconTrash} label="Delete" state="disabled" />
|
|
117
117
|
```
|
|
@@ -13,8 +13,8 @@ export interface BitkitButtonAsButtonProps extends BitkitButtonCommonProps, Omit
|
|
|
13
13
|
/**
|
|
14
14
|
* Why the button is disabled, shown in a tooltip on it while `state` is `"disabled"`. The button
|
|
15
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.
|
|
17
|
-
*
|
|
16
|
+
* hover and focus, and screen readers announce the reason. Use it instead of wrapping a disabled
|
|
17
|
+
* button in `BitkitTooltip`.
|
|
18
18
|
*/
|
|
19
19
|
disabledReason?: string;
|
|
20
20
|
href?: undefined;
|
|
@@ -110,11 +110,11 @@ var BitkitButton = forwardRef((props, ref) => {
|
|
|
110
110
|
]
|
|
111
111
|
})
|
|
112
112
|
});
|
|
113
|
-
return
|
|
114
|
-
disabled: !
|
|
115
|
-
text: disabledReason,
|
|
113
|
+
return /* @__PURE__ */ jsx(BitkitTooltip, {
|
|
114
|
+
disabled: !hasReason,
|
|
115
|
+
text: disabledReason ?? "",
|
|
116
116
|
children: buttonElement
|
|
117
|
-
})
|
|
117
|
+
});
|
|
118
118
|
});
|
|
119
119
|
BitkitButton.displayName = "BitkitButton";
|
|
120
120
|
//#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.
|
|
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. Use it instead of wrapping a disabled\n * 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 // Always wrapped, like the icon button's label tooltip: adding the wrapper only with a reason would\n // remount the button whenever the reason or the state changes, and drop its focus.\n return (\n <BitkitTooltip disabled={!hasReason} text={disabledReason ?? ''}>\n {buttonElement}\n </BitkitTooltip>\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,OACE,oBAAC,eAAD;EAAe,UAAU,CAAC;EAAW,MAAM,kBAAkB;EAC1D,UAAA;CACY,CAAA;AAEnB,CAAC;AAED,aAAa,cAAc"}
|
|
@@ -20,8 +20,7 @@ interface BitkitIconButtonAsButtonProps extends BitkitIconButtonCommonProps, Omi
|
|
|
20
20
|
* Why the button is disabled, shown in its tooltip in place of the label while `state` is
|
|
21
21
|
* `"disabled"`. The button then stays focusable (`aria-disabled` instead of the native attribute),
|
|
22
22
|
* so the tooltip opens on hover and focus, and screen readers announce the reason after the
|
|
23
|
-
* label.
|
|
24
|
-
* disabled button in `BitkitTooltip`.
|
|
23
|
+
* label. Use it instead of wrapping a disabled button in `BitkitTooltip`.
|
|
25
24
|
*/
|
|
26
25
|
disabledReason?: string;
|
|
27
26
|
href?: undefined;
|
|
@@ -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.
|
|
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. 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\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":";;;;;;;;;AAiFA,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"}
|
|
@@ -40,7 +40,7 @@ var tourSlotRecipe = defineSlotRecipe({
|
|
|
40
40
|
flexDirection: "column",
|
|
41
41
|
gap: "8",
|
|
42
42
|
maxHeight: "max(var(--available-height, 100dvh), 18rem)",
|
|
43
|
-
maxWidth: "
|
|
43
|
+
maxWidth: "max(var(--available-width, 100vw), 18rem)",
|
|
44
44
|
outline: "none",
|
|
45
45
|
padding: "16",
|
|
46
46
|
position: "relative",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tour.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Tour.recipe.ts"],"sourcesContent":["// The Tour parts come from Ark UI, so the anatomy comes from there too — Chakra has no `tour`\n// counterpart yet (chakra-ui/chakra-ui#10410 is still open).\nimport { tourAnatomy } from '@ark-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nimport { rem } from '../themeUtils';\n\nconst tourSlotRecipe = defineSlotRecipe({\n className: 'tour',\n slots: [...tourAnatomy.keys(), 'actions', 'badge', 'header', 'media', 'titleGroup', 'titleRow'] as const,\n base: {\n backdrop: {\n background: 'utilities/overlay',\n zIndex: 'dialogOverlay',\n _open: {\n animationStyle: 'fade-in',\n animationDuration: 'moderate',\n },\n // No exit animation: Ark sets `hidden` on the backdrop and the content the moment the tour\n // closes, so the node is `display: none` in the same frame and an exit animation can never\n // paint — it only leaves the animation assigned, which flashes the card back in for a frame\n // on the next render. Most visible on steps without a backdrop, where nothing masks it.\n },\n // Sits on the same layer as the backdrop and after it in DOM order, so the ring around the\n // highlighted element paints over the dimmed page.\n spotlight: {\n borderRadius: '4',\n outline: '2px solid',\n outlineColor: 'interactive/highlight',\n zIndex: 'dialogOverlay',\n },\n // Zag positions and layers this one inline, writing\n // `z-index: calc(var(--tour-layer) + var(--tour-z-index))` and expecting us to supply the base.\n // Without it the calc is invalid, the positioner falls back to `auto` — and since it opens its\n // own stacking context (`isolation: isolate`), the card's own z-index can't reach past it, so\n // the whole card ends up under the backdrop and swallows every click. The plain `zIndex` is the\n // fallback for the day Zag stops writing that inline.\n positioner: {\n '--tour-z-index': 'zIndex.dialog',\n zIndex: 'dialog',\n },\n content: {\n background: 'background/primary',\n borderRadius: '8',\n boxShadow: 'elevation/lg',\n display: 'flex',\n flexDirection: 'column',\n gap: '8',\n // Floored, and the floor is what keeps the card from talking Popper out of flipping. Zag's\n // `size` middleware runs after `flip` and writes `--available-height` from the space on the\n // chosen side; the card is the same node from step to step, so it carries that cap into the\n // next step's first measurement. A step that needs 328 measured 193 — the cap left by a\n // shorter step — decided it fitted below, and never flipped to the side that had 474 free.\n // Below this floor the card stops shrinking, so it overflows instead, which is what `flip`\n // reads to move it.\n maxHeight: 'max(var(--available-height, 100dvh), 18rem)',\n maxWidth: '
|
|
1
|
+
{"version":3,"file":"Tour.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Tour.recipe.ts"],"sourcesContent":["// The Tour parts come from Ark UI, so the anatomy comes from there too — Chakra has no `tour`\n// counterpart yet (chakra-ui/chakra-ui#10410 is still open).\nimport { tourAnatomy } from '@ark-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nimport { rem } from '../themeUtils';\n\nconst tourSlotRecipe = defineSlotRecipe({\n className: 'tour',\n slots: [...tourAnatomy.keys(), 'actions', 'badge', 'header', 'media', 'titleGroup', 'titleRow'] as const,\n base: {\n backdrop: {\n background: 'utilities/overlay',\n zIndex: 'dialogOverlay',\n _open: {\n animationStyle: 'fade-in',\n animationDuration: 'moderate',\n },\n // No exit animation: Ark sets `hidden` on the backdrop and the content the moment the tour\n // closes, so the node is `display: none` in the same frame and an exit animation can never\n // paint — it only leaves the animation assigned, which flashes the card back in for a frame\n // on the next render. Most visible on steps without a backdrop, where nothing masks it.\n },\n // Sits on the same layer as the backdrop and after it in DOM order, so the ring around the\n // highlighted element paints over the dimmed page.\n spotlight: {\n borderRadius: '4',\n outline: '2px solid',\n outlineColor: 'interactive/highlight',\n zIndex: 'dialogOverlay',\n },\n // Zag positions and layers this one inline, writing\n // `z-index: calc(var(--tour-layer) + var(--tour-z-index))` and expecting us to supply the base.\n // Without it the calc is invalid, the positioner falls back to `auto` — and since it opens its\n // own stacking context (`isolation: isolate`), the card's own z-index can't reach past it, so\n // the whole card ends up under the backdrop and swallows every click. The plain `zIndex` is the\n // fallback for the day Zag stops writing that inline.\n positioner: {\n '--tour-z-index': 'zIndex.dialog',\n zIndex: 'dialog',\n },\n content: {\n background: 'background/primary',\n borderRadius: '8',\n boxShadow: 'elevation/lg',\n display: 'flex',\n flexDirection: 'column',\n gap: '8',\n // Floored, and the floor is what keeps the card from talking Popper out of flipping. Zag's\n // `size` middleware runs after `flip` and writes `--available-height` from the space on the\n // chosen side; the card is the same node from step to step, so it carries that cap into the\n // next step's first measurement. A step that needs 328 measured 193 — the cap left by a\n // shorter step — decided it fitted below, and never flipped to the side that had 474 free.\n // Below this floor the card stops shrinking, so it overflows instead, which is what `flip`\n // reads to move it.\n maxHeight: 'max(var(--available-height, 100dvh), 18rem)',\n // Same cap and floor as `maxHeight`; `100%` never capped, as the positioner sizes to the card.\n maxWidth: 'max(var(--available-width, 100vw), 18rem)',\n outline: 'none',\n padding: '16',\n position: 'relative',\n width: rem(360),\n zIndex: 'dialog',\n // Fade only, no scale: Zag puts `--transform-origin` at the arrow tip, so a `scale-in` grows\n // the card away from the highlight — a 360x300 card gains ~15 of height on the way in, which\n // reads as the card repositioning itself rather than appearing.\n _open: {\n animationStyle: 'fade-in',\n animationDuration: 'moderate',\n },\n },\n header: {\n alignItems: 'flex-start',\n display: 'flex',\n gap: '16',\n justifyContent: 'space-between',\n },\n titleGroup: {\n display: 'flex',\n flexDirection: 'column',\n gap: '8',\n },\n // flex-start, not center: the badge is as tall as one line of the title, so it lines up with\n // the first line instead of floating beside the middle of a title that wrapped.\n titleRow: {\n alignItems: 'flex-start',\n display: 'flex',\n gap: '8',\n },\n badge: {\n flexShrink: 0,\n },\n title: {\n color: 'text/primary',\n textStyle: 'heading/h4',\n },\n progressText: {\n color: 'text/secondary',\n textStyle: 'body/sm/regular',\n },\n description: {\n color: 'text/body',\n minHeight: 0,\n overflowY: 'auto',\n textStyle: 'body/md/regular',\n },\n // These two margins stack on the content's own 8 gap, which is how the layout spec builds the\n // spacing: 16 between the description and the visual, and 24 above the footer. The footer gets\n // nothing below it, so the card's own 16 padding is the whole gap under the buttons.\n media: {\n marginBlockStart: '8',\n },\n control: {\n alignItems: 'center',\n display: 'flex',\n gap: '16',\n justifyContent: 'space-between',\n marginBlockStart: '16',\n },\n actions: {\n display: 'flex',\n gap: '8',\n marginInlineStart: 'auto',\n },\n closeTrigger: {\n flexShrink: 0,\n },\n arrow: {\n // Popper draws the arrow as a square rotated 45°, so the visible triangle is `size * √2`\n // wide and `size / √2` tall — 11.3 gives the design's 16×8.\n '--arrow-size': rem(11.3),\n '--arrow-background': 'colors.background/primary',\n // Size and out-of-flow positioning are set here, not only by Popper's inline styles, because\n // the component keeps this element out of Zag's `hidden` (see BitkitTour): Popper derives the\n // card's distance from the anchor as `offset.mainAxis + arrowEl.clientHeight / 2`, read off\n // the DOM. A `display: none` arrow measures 0, which puts the card 6 short of the design's 12\n // on the first pass. `absolute` keeps it from adding its height to the card while it waits\n // for Popper's own inline placement.\n height: 'var(--arrow-size)',\n position: 'absolute',\n width: 'var(--arrow-size)',\n },\n arrowTip: {\n borderColor: 'border/minimal',\n borderInlineStartWidth: '1',\n borderTopWidth: '1',\n },\n },\n});\n\nexport default tourSlotRecipe;\n"],"mappings":";;;;AAOA,IAAM,iBAAiB,iBAAiB;CACtC,WAAW;CACX,OAAO;EAAC,GAAG,YAAY,KAAK;EAAG;EAAW;EAAS;EAAU;EAAS;EAAc;CAAU;CAC9F,MAAM;EACJ,UAAU;GACR,YAAY;GACZ,QAAQ;GACR,OAAO;IACL,gBAAgB;IAChB,mBAAmB;GACrB;EAKF;EAGA,WAAW;GACT,cAAc;GACd,SAAS;GACT,cAAc;GACd,QAAQ;EACV;EAOA,YAAY;GACV,kBAAkB;GAClB,QAAQ;EACV;EACA,SAAS;GACP,YAAY;GACZ,cAAc;GACd,WAAW;GACX,SAAS;GACT,eAAe;GACf,KAAK;GAQL,WAAW;GAEX,UAAU;GACV,SAAS;GACT,SAAS;GACT,UAAU;GACV,OAAO,IAAI,GAAG;GACd,QAAQ;GAIR,OAAO;IACL,gBAAgB;IAChB,mBAAmB;GACrB;EACF;EACA,QAAQ;GACN,YAAY;GACZ,SAAS;GACT,KAAK;GACL,gBAAgB;EAClB;EACA,YAAY;GACV,SAAS;GACT,eAAe;GACf,KAAK;EACP;EAGA,UAAU;GACR,YAAY;GACZ,SAAS;GACT,KAAK;EACP;EACA,OAAO,EACL,YAAY,EACd;EACA,OAAO;GACL,OAAO;GACP,WAAW;EACb;EACA,cAAc;GACZ,OAAO;GACP,WAAW;EACb;EACA,aAAa;GACX,OAAO;GACP,WAAW;GACX,WAAW;GACX,WAAW;EACb;EAIA,OAAO,EACL,kBAAkB,IACpB;EACA,SAAS;GACP,YAAY;GACZ,SAAS;GACT,KAAK;GACL,gBAAgB;GAChB,kBAAkB;EACpB;EACA,SAAS;GACP,SAAS;GACT,KAAK;GACL,mBAAmB;EACrB;EACA,cAAc,EACZ,YAAY,EACd;EACA,OAAO;GAGL,gBAAgB,IAAI,IAAI;GACxB,sBAAsB;GAOtB,QAAQ;GACR,UAAU;GACV,OAAO;EACT;EACA,UAAU;GACR,aAAa;GACb,wBAAwB;GACxB,gBAAgB;EAClB;CACF;AACF,CAAC"}
|