@bitrise/bitkit-v2 0.3.355 → 0.3.357

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
@@ -139,6 +139,36 @@ Bitkit components use consistent prop names across the library:
139
139
  <BitkitDialog open={open} onExitComplete={() => navigate('/next')} onOpenChange={…} title="…">…</BitkitDialog>
140
140
  ```
141
141
 
142
+ - **`BitkitTour` is a hook plus one element** — `useBitkitTour` owns the steps and the flow, `<BitkitTour>` renders the card; render it once, anywhere in the tree. Every step is a card anchored to an element, so `target` is required and there is no step `type` to choose. `open` drives it from a value you already have (`dismiss()` and `start()` are there for the imperative side), and each step carries its own `actions` — the last one renders as the primary button:
143
+ ```tsx
144
+ const startBuild = useRef<HTMLElement>(null);
145
+ const tour = useBitkitTour({
146
+ onStatusChange: ({ status }) => status === 'dismissed' && persistDismissal(),
147
+ open: shouldShowTour,
148
+ steps: [
149
+ {
150
+ actions: [{ action: 'dismiss', label: 'Got it' }],
151
+ badge: <BitkitBadge colorVariant="yellow">BETA</BitkitBadge>,
152
+ content: <img alt="" src="/tour/start-build.gif" />,
153
+ description: 'Kick off a build for any branch or pull request.',
154
+ id: 'start-build',
155
+ secondaryAction: { href: '…', label: 'Learn more' },
156
+ target: () => startBuild.current,
157
+ title: 'Start a build',
158
+ },
159
+ ],
160
+ });
161
+ ```
162
+ A single-step tour drops the `n of m` label, which is what turns the card into the single-feature presentation. Clicking outside never closes it — the close button, the step's own actions and Escape do. It dims the page and cuts the highlight out of the backdrop itself, so don't add your own overlay, ring or z-index around the target.
163
+ - **Testing a page that renders `BitkitTour` in jsdom needs a `visualViewport` stub** — Zag measures its boundary from that global, which jsdom doesn't implement, and it throws whether the tour is open or not. In the jest setup:
164
+ ```ts
165
+ Object.defineProperty(window, 'visualViewport', {
166
+ value: Object.assign(new EventTarget(), { height: window.innerHeight, width: window.innerWidth }),
167
+ writable: true,
168
+ });
169
+ ```
170
+ In specs, query the card by its role (`getByRole('alertdialog')`) rather than a test id.
171
+
142
172
  ## Coding rules
143
173
 
144
174
  - **Only use tokens that exist** — never guess token names. Verify via the Storybook MCP `Docs/Colors - *`, `Docs/Shadows`, `Docs/Text Styles`, etc. pages.
@@ -26,24 +26,43 @@ var useIsContentOverflowing = (collapsedHeight) => {
26
26
  const resizeObserver = new ResizeObserver(checkOverflow);
27
27
  resizeObserver.observe(contentEl);
28
28
  return () => resizeObserver.disconnect();
29
- }, [collapsedHeight]);
29
+ }, [collapsedHeight, isOverflowing]);
30
30
  return {
31
31
  contentRef,
32
32
  isOverflowing
33
33
  };
34
34
  };
35
35
  var BitkitCollapsible = forwardRef((props, ref) => {
36
- const { children, closedLabel = "Show less", collapsedHeight = "48px", defaultOpen, label, labelProps, onOpenChange, open, openLabel = "Show more", ...rest } = props;
36
+ const { children, closedLabel = "Show less", collapsedHeight = "48px", collapsedWidth, defaultOpen, disabled, hideMode, ids, label, labelProps, lazyMount, onExitComplete, onOpenChange, open, openLabel = "Show more", unmountOnExit, ...rest } = props;
37
37
  const styles = useSlotRecipe({ key: "collapsible" })();
38
38
  const { contentRef, isOverflowing } = useIsContentOverflowing(collapsedHeight);
39
39
  const handleOpenChange = onOpenChange ? (details) => onOpenChange(details.open) : void 0;
40
+ if (!isOverflowing) return /* @__PURE__ */ jsxs(Box, {
41
+ ref,
42
+ css: styles.root,
43
+ ...rest,
44
+ children: [label && /* @__PURE__ */ jsx(BitkitLabel, {
45
+ ...labelProps,
46
+ children: label
47
+ }), /* @__PURE__ */ jsx(Box, {
48
+ ref: contentRef,
49
+ children
50
+ })]
51
+ });
40
52
  return /* @__PURE__ */ jsxs(Collapsible.Root, {
41
53
  ref,
42
54
  collapsedHeight,
55
+ collapsedWidth,
43
56
  css: styles.root,
44
57
  defaultOpen,
58
+ disabled,
59
+ hideMode,
60
+ ids,
61
+ lazyMount,
62
+ onExitComplete,
45
63
  onOpenChange: handleOpenChange,
46
64
  open,
65
+ unmountOnExit,
47
66
  ...rest,
48
67
  children: [
49
68
  label && /* @__PURE__ */ jsx(BitkitLabel, {
@@ -57,7 +76,7 @@ var BitkitCollapsible = forwardRef((props, ref) => {
57
76
  children
58
77
  })
59
78
  }),
60
- isOverflowing && /* @__PURE__ */ jsx(Collapsible.Trigger, {
79
+ /* @__PURE__ */ jsx(Collapsible.Trigger, {
61
80
  css: styles.trigger,
62
81
  children: /* @__PURE__ */ jsx(Collapsible.Context, { children: (api) => /* @__PURE__ */ jsxs(Fragment$1, { children: [api.open ? closedLabel : openLabel, api.open ? /* @__PURE__ */ jsx(IconChevronUp, { size: "16" }) : /* @__PURE__ */ jsx(IconChevronDown, { size: "16" })] }) })
63
82
  })
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitCollapsible.js","names":[],"sources":["../../../lib/components/BitkitCollapsible/BitkitCollapsible.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { Collapsible, type CollapsibleRootProps } from '@chakra-ui/react/collapsible';\nimport { useSafeLayoutEffect } from '@chakra-ui/react/hooks';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useRef, useState } from 'react';\n\n// TODO: Investigate using AssetSelectChevron instead of IconChevronDown/IconChevronUp\nimport { IconChevronDown, IconChevronUp } from '../../icons';\nimport BitkitLabel, { type BitkitLabelProps } from '../BitkitLabel/BitkitLabel';\n\ntype UncontrolledProps = {\n defaultOpen?: boolean;\n open?: never;\n onOpenChange?: never;\n};\n\ntype ControlledProps = {\n defaultOpen?: never;\n onOpenChange: (open: boolean) => void;\n open: boolean;\n};\n\nexport type BitkitCollapsibleProps = Omit<\n CollapsibleRootProps,\n 'collapsedHeight' | 'defaultOpen' | 'onOpenChange' | 'open'\n> & {\n children: ReactNode;\n closedLabel?: string;\n collapsedHeight?: string;\n label?: string;\n labelProps?: Omit<BitkitLabelProps, 'children'>;\n openLabel?: string;\n} & (UncontrolledProps | ControlledProps);\n\n// contentRef sits on an unclamped wrapper *inside* Collapsible.Content, not on Content itself: when\n// closed, Content's own box is pinned to collapsedHeight (max-height + overflow), so a ResizeObserver\n// on Content can't see content that grows without changing Content's own (clamped) box - e.g. an image\n// or async child rendering more after mount. scrollHeight still reports the wrapper's true height\n// either way, clamped or not, so the read doesn't change - only where we watch for changes does.\n// collapsedHeight is the only effect dependency: the wrapper being unclamped means the observer itself\n// sees every content-driven size change, so re-running the whole effect just because `children`'s\n// identity changed (which it does on every parent render, once children stop being a plain string)\n// would only add a redundant probe/layout/observer teardown-and-rebuild on top of what the observer\n// already covers.\nconst useIsContentOverflowing = (collapsedHeight: string) => {\n const contentRef = useRef<HTMLDivElement>(null);\n const [isOverflowing, setIsOverflowing] = useState(true);\n\n useSafeLayoutEffect(() => {\n const contentEl = contentRef.current;\n const measureScope = contentEl?.parentElement;\n if (!contentEl || !measureScope) {\n return undefined;\n }\n\n // Appended as a sibling of contentEl (not document.body) so it inherits the same font-size and\n // CSS custom properties contentEl actually sees, resolving em/var(...) heights correctly - not\n // just px/rem, which don't depend on where in the tree they're resolved.\n const probe = document.createElement('div');\n probe.style.cssText = `position: absolute; visibility: hidden; pointer-events: none; height: ${collapsedHeight};`;\n measureScope.appendChild(probe);\n const collapsedHeightPx = probe.getBoundingClientRect().height;\n measureScope.removeChild(probe);\n\n const checkOverflow = () => setIsOverflowing(contentEl.scrollHeight > collapsedHeightPx);\n checkOverflow();\n\n if (typeof ResizeObserver === 'undefined') {\n return undefined;\n }\n\n const resizeObserver = new ResizeObserver(checkOverflow);\n resizeObserver.observe(contentEl);\n return () => resizeObserver.disconnect();\n }, [collapsedHeight]);\n\n return { contentRef, isOverflowing };\n};\n\nconst BitkitCollapsible = forwardRef<HTMLDivElement, BitkitCollapsibleProps>((props, ref) => {\n const {\n children,\n closedLabel = 'Show less',\n collapsedHeight = '48px',\n defaultOpen,\n label,\n labelProps,\n onOpenChange,\n open,\n openLabel = 'Show more',\n ...rest\n } = props;\n\n const recipe = useSlotRecipe({ key: 'collapsible' });\n const styles = recipe();\n\n const { contentRef, isOverflowing } = useIsContentOverflowing(collapsedHeight);\n\n const handleOpenChange = onOpenChange ? (details: { open: boolean }) => onOpenChange(details.open) : undefined;\n\n return (\n <Collapsible.Root\n ref={ref}\n collapsedHeight={collapsedHeight}\n css={styles.root}\n defaultOpen={defaultOpen}\n onOpenChange={handleOpenChange}\n open={open}\n {...rest}\n >\n {label && <BitkitLabel {...labelProps}>{label}</BitkitLabel>}\n <Collapsible.Content css={styles.content}>\n <Box ref={contentRef}>{children}</Box>\n </Collapsible.Content>\n {isOverflowing && (\n <Collapsible.Trigger css={styles.trigger}>\n <Collapsible.Context>\n {(api) => (\n <>\n {api.open ? closedLabel : openLabel}\n {api.open ? <IconChevronUp size=\"16\" /> : <IconChevronDown size=\"16\" />}\n </>\n )}\n </Collapsible.Context>\n </Collapsible.Trigger>\n )}\n </Collapsible.Root>\n );\n});\n\nBitkitCollapsible.displayName = 'BitkitCollapsible';\n\nexport default BitkitCollapsible;\n"],"mappings":";;;;;;;;;;AA4CA,IAAM,2BAA2B,oBAA4B;CAC3D,MAAM,aAAa,OAAuB,IAAI;CAC9C,MAAM,CAAC,eAAe,oBAAoB,SAAS,IAAI;CAEvD,0BAA0B;EACxB,MAAM,YAAY,WAAW;EAC7B,MAAM,eAAe,WAAW;EAChC,IAAI,CAAC,aAAa,CAAC,cACjB;EAMF,MAAM,QAAQ,SAAS,cAAc,KAAK;EAC1C,MAAM,MAAM,UAAU,yEAAyE,gBAAgB;EAC/G,aAAa,YAAY,KAAK;EAC9B,MAAM,oBAAoB,MAAM,sBAAsB,CAAC,CAAC;EACxD,aAAa,YAAY,KAAK;EAE9B,MAAM,sBAAsB,iBAAiB,UAAU,eAAe,iBAAiB;EACvF,cAAc;EAEd,IAAI,OAAO,mBAAmB,aAC5B;EAGF,MAAM,iBAAiB,IAAI,eAAe,aAAa;EACvD,eAAe,QAAQ,SAAS;EAChC,aAAa,eAAe,WAAW;CACzC,GAAG,CAAC,eAAe,CAAC;CAEpB,OAAO;EAAE;EAAY;CAAc;AACrC;AAEA,IAAM,oBAAoB,YAAoD,OAAO,QAAQ;CAC3F,MAAM,EACJ,UACA,cAAc,aACd,kBAAkB,QAClB,aACA,OACA,YACA,cACA,MACA,YAAY,aACZ,GAAG,SACD;CAGJ,MAAM,SADS,cAAc,EAAE,KAAK,cAAc,CACnC,CAAA,CAAO;CAEtB,MAAM,EAAE,YAAY,kBAAkB,wBAAwB,eAAe;CAE7E,MAAM,mBAAmB,gBAAgB,YAA+B,aAAa,QAAQ,IAAI,IAAI,KAAA;CAErG,OACE,qBAAC,YAAY,MAAb;EACO;EACY;EACjB,KAAK,OAAO;EACC;EACb,cAAc;EACR;EACN,GAAI;EAPN,UAAA;GASG,SAAS,oBAAC,aAAD;IAAa,GAAI;IAAa,UAAA;GAAmB,CAAA;GAC3D,oBAAC,YAAY,SAAb;IAAqB,KAAK,OAAO;IAC/B,UAAA,oBAAC,KAAD;KAAK,KAAK;KAAa;IAAc,CAAA;GAClB,CAAA;GACpB,iBACC,oBAAC,YAAY,SAAb;IAAqB,KAAK,OAAO;IAC/B,UAAA,oBAAC,YAAY,SAAb,EAAA,WACI,QACA,qBAAA,YAAA,EAAA,UAAA,CACG,IAAI,OAAO,cAAc,WACzB,IAAI,OAAO,oBAAC,eAAD,EAAe,MAAK,KAAM,CAAA,IAAI,oBAAC,iBAAD,EAAiB,MAAK,KAAM,CAAA,CACtE,EAAA,CAAA,EAEe,CAAA;GACF,CAAA;EAEP;;AAEtB,CAAC;AAED,kBAAkB,cAAc"}
1
+ {"version":3,"file":"BitkitCollapsible.js","names":[],"sources":["../../../lib/components/BitkitCollapsible/BitkitCollapsible.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { Collapsible, type CollapsibleRootProps } from '@chakra-ui/react/collapsible';\nimport { useSafeLayoutEffect } from '@chakra-ui/react/hooks';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useRef, useState } from 'react';\n\n// TODO: Investigate using AssetSelectChevron instead of IconChevronDown/IconChevronUp\nimport { IconChevronDown, IconChevronUp } from '../../icons';\nimport BitkitLabel, { type BitkitLabelProps } from '../BitkitLabel/BitkitLabel';\n\ntype UncontrolledProps = {\n defaultOpen?: boolean;\n open?: never;\n onOpenChange?: never;\n};\n\ntype ControlledProps = {\n defaultOpen?: never;\n onOpenChange: (open: boolean) => void;\n open: boolean;\n};\n\nexport type BitkitCollapsibleProps = Omit<\n CollapsibleRootProps,\n 'collapsedHeight' | 'defaultOpen' | 'onOpenChange' | 'open'\n> & {\n children: ReactNode;\n closedLabel?: string;\n collapsedHeight?: string;\n label?: string;\n labelProps?: Omit<BitkitLabelProps, 'children'>;\n openLabel?: string;\n} & (UncontrolledProps | ControlledProps);\n\n// contentRef sits on an unclamped wrapper *inside* Collapsible.Content, not on Content itself: when\n// closed, Content's own box is pinned to collapsedHeight (max-height + overflow), so a ResizeObserver\n// on Content can't see content that grows without changing Content's own (clamped) box - e.g. an image\n// or async child rendering more after mount. scrollHeight still reports the wrapper's true height\n// either way, clamped or not, so the read doesn't change - only where we watch for changes does.\n// isOverflowing and collapsedHeight are the effect's only dependencies. isOverflowing: BitkitCollapsible\n// renders a different tree depending on it (see below), moving contentRef to a different DOM node each\n// time it flips, so the effect must re-attach then. children is deliberately not a dependency: the\n// wrapper being unclamped means the observer already sees every content-driven size change on its own.\nconst useIsContentOverflowing = (collapsedHeight: string) => {\n const contentRef = useRef<HTMLDivElement>(null);\n const [isOverflowing, setIsOverflowing] = useState(true);\n\n useSafeLayoutEffect(() => {\n const contentEl = contentRef.current;\n const measureScope = contentEl?.parentElement;\n if (!contentEl || !measureScope) {\n return undefined;\n }\n\n // Appended as a sibling of contentEl (not document.body) so it inherits the same font-size and\n // CSS custom properties contentEl actually sees, resolving em/var(...) heights correctly - not\n // just px/rem, which don't depend on where in the tree they're resolved.\n const probe = document.createElement('div');\n probe.style.cssText = `position: absolute; visibility: hidden; pointer-events: none; height: ${collapsedHeight};`;\n measureScope.appendChild(probe);\n const collapsedHeightPx = probe.getBoundingClientRect().height;\n measureScope.removeChild(probe);\n\n const checkOverflow = () => setIsOverflowing(contentEl.scrollHeight > collapsedHeightPx);\n checkOverflow();\n\n if (typeof ResizeObserver === 'undefined') {\n return undefined;\n }\n\n const resizeObserver = new ResizeObserver(checkOverflow);\n resizeObserver.observe(contentEl);\n return () => resizeObserver.disconnect();\n }, [collapsedHeight, isOverflowing]);\n\n return { contentRef, isOverflowing };\n};\n\nconst BitkitCollapsible = forwardRef<HTMLDivElement, BitkitCollapsibleProps>((props, ref) => {\n const {\n children,\n closedLabel = 'Show less',\n collapsedHeight = '48px',\n collapsedWidth,\n defaultOpen,\n disabled,\n hideMode,\n ids,\n label,\n labelProps,\n lazyMount,\n onExitComplete,\n onOpenChange,\n open,\n openLabel = 'Show more',\n unmountOnExit,\n ...rest\n } = props;\n\n const recipe = useSlotRecipe({ key: 'collapsible' });\n const styles = recipe();\n\n const { contentRef, isOverflowing } = useIsContentOverflowing(collapsedHeight);\n\n const handleOpenChange = onOpenChange ? (details: { open: boolean }) => onOpenChange(details.open) : undefined;\n\n // zag's closed-state styling (min/max-height clamp, the fade-out pseudo-element) is driven purely by\n // Collapsible.Root defaulting to \"closed\" - it doesn't know or care whether collapsedHeight would\n // ever actually clip anything. Passing collapsedHeight and hiding only the trigger still leaves\n // content that fits pinned to that height and faded, so when it doesn't overflow we skip\n // Collapsible.Root/Content entirely and render plain markup instead of fighting the closed-state\n // styling with overrides. Only styles.root applies here - it carries the inherited color/textStyle,\n // which is all a non-collapsing block needs; styles.content's overflow/position/fade rules are\n // collapse-only and would clip content (a focus ring, an absolutely-positioned child) that isn't\n // meant to be clipped. contentRef sits directly under styles.root rather than under a styles.content\n // box, so measureScope (its parent) is styles.root in this branch and styles.content in the\n // overflowing one below - both carry the same inherited font-size, since textStyle now lives on\n // root and cascades down, so collapsedHeight's em/var(...) units resolve the same way in either\n // branch. disabled/ids/collapsedWidth/onExitComplete/lazyMount/unmountOnExit/hideMode\n // are zag/Ark machine props with no meaning on plain markup, so they're destructured out above and\n // only forwarded to Collapsible.Root below - otherwise they'd reach the DOM as unknown attributes.\n //\n // isOverflowing starts true, so every mount (even of content that turns out to fit) renders through\n // this Collapsible.Root tree first and flips to the plain tree below once measured - a real\n // remount of children, since the two trees don't share a component type at that position. Same\n // going the other way on a runtime resize. Accepted tradeoff: measuring requires a real layout\n // pass, and we don't have the content's size before that first render.\n if (!isOverflowing) {\n return (\n <Box ref={ref} css={styles.root} {...rest}>\n {label && <BitkitLabel {...labelProps}>{label}</BitkitLabel>}\n <Box ref={contentRef}>{children}</Box>\n </Box>\n );\n }\n\n return (\n <Collapsible.Root\n ref={ref}\n collapsedHeight={collapsedHeight}\n collapsedWidth={collapsedWidth}\n css={styles.root}\n defaultOpen={defaultOpen}\n disabled={disabled}\n hideMode={hideMode}\n ids={ids}\n lazyMount={lazyMount}\n onExitComplete={onExitComplete}\n onOpenChange={handleOpenChange}\n open={open}\n unmountOnExit={unmountOnExit}\n {...rest}\n >\n {label && <BitkitLabel {...labelProps}>{label}</BitkitLabel>}\n <Collapsible.Content css={styles.content}>\n <Box ref={contentRef}>{children}</Box>\n </Collapsible.Content>\n <Collapsible.Trigger css={styles.trigger}>\n <Collapsible.Context>\n {(api) => (\n <>\n {api.open ? closedLabel : openLabel}\n {api.open ? <IconChevronUp size=\"16\" /> : <IconChevronDown size=\"16\" />}\n </>\n )}\n </Collapsible.Context>\n </Collapsible.Trigger>\n </Collapsible.Root>\n );\n});\n\nBitkitCollapsible.displayName = 'BitkitCollapsible';\n\nexport default BitkitCollapsible;\n"],"mappings":";;;;;;;;;;AA2CA,IAAM,2BAA2B,oBAA4B;CAC3D,MAAM,aAAa,OAAuB,IAAI;CAC9C,MAAM,CAAC,eAAe,oBAAoB,SAAS,IAAI;CAEvD,0BAA0B;EACxB,MAAM,YAAY,WAAW;EAC7B,MAAM,eAAe,WAAW;EAChC,IAAI,CAAC,aAAa,CAAC,cACjB;EAMF,MAAM,QAAQ,SAAS,cAAc,KAAK;EAC1C,MAAM,MAAM,UAAU,yEAAyE,gBAAgB;EAC/G,aAAa,YAAY,KAAK;EAC9B,MAAM,oBAAoB,MAAM,sBAAsB,CAAC,CAAC;EACxD,aAAa,YAAY,KAAK;EAE9B,MAAM,sBAAsB,iBAAiB,UAAU,eAAe,iBAAiB;EACvF,cAAc;EAEd,IAAI,OAAO,mBAAmB,aAC5B;EAGF,MAAM,iBAAiB,IAAI,eAAe,aAAa;EACvD,eAAe,QAAQ,SAAS;EAChC,aAAa,eAAe,WAAW;CACzC,GAAG,CAAC,iBAAiB,aAAa,CAAC;CAEnC,OAAO;EAAE;EAAY;CAAc;AACrC;AAEA,IAAM,oBAAoB,YAAoD,OAAO,QAAQ;CAC3F,MAAM,EACJ,UACA,cAAc,aACd,kBAAkB,QAClB,gBACA,aACA,UACA,UACA,KACA,OACA,YACA,WACA,gBACA,cACA,MACA,YAAY,aACZ,eACA,GAAG,SACD;CAGJ,MAAM,SADS,cAAc,EAAE,KAAK,cAAc,CACnC,CAAA,CAAO;CAEtB,MAAM,EAAE,YAAY,kBAAkB,wBAAwB,eAAe;CAE7E,MAAM,mBAAmB,gBAAgB,YAA+B,aAAa,QAAQ,IAAI,IAAI,KAAA;CAuBrG,IAAI,CAAC,eACH,OACE,qBAAC,KAAD;EAAU;EAAK,KAAK,OAAO;EAAM,GAAI;EAArC,UAAA,CACG,SAAS,oBAAC,aAAD;GAAa,GAAI;GAAa,UAAA;EAAmB,CAAA,GAC3D,oBAAC,KAAD;GAAK,KAAK;GAAa;EAAc,CAAA,CAClC;;CAIT,OACE,qBAAC,YAAY,MAAb;EACO;EACY;EACD;EAChB,KAAK,OAAO;EACC;EACH;EACA;EACL;EACM;EACK;EAChB,cAAc;EACR;EACS;EACf,GAAI;EAdN,UAAA;GAgBG,SAAS,oBAAC,aAAD;IAAa,GAAI;IAAa,UAAA;GAAmB,CAAA;GAC3D,oBAAC,YAAY,SAAb;IAAqB,KAAK,OAAO;IAC/B,UAAA,oBAAC,KAAD;KAAK,KAAK;KAAa;IAAc,CAAA;GAClB,CAAA;GACrB,oBAAC,YAAY,SAAb;IAAqB,KAAK,OAAO;IAC/B,UAAA,oBAAC,YAAY,SAAb,EAAA,WACI,QACA,qBAAA,YAAA,EAAA,UAAA,CACG,IAAI,OAAO,cAAc,WACzB,IAAI,OAAO,oBAAC,eAAD,EAAe,MAAK,KAAM,CAAA,IAAI,oBAAC,iBAAD,EAAiB,MAAK,KAAM,CAAA,CACtE,EAAA,CAAA,EAEe,CAAA;GACF,CAAA;EACL;;AAEtB,CAAC;AAED,kBAAkB,cAAc"}
@@ -46,6 +46,11 @@ export interface BitkitTourOptions extends Omit<UseTourProps, 'closeOnInteractOu
46
46
  onStepsChange?: (details: {
47
47
  steps: BitkitTourStep[];
48
48
  }) => void;
49
+ /**
50
+ * Drives the tour from a value the caller already has — it starts when this turns true and closes
51
+ * when it turns false. Omit it to drive the tour with `start()` and `dismiss()` instead.
52
+ */
53
+ open?: boolean;
49
54
  steps: BitkitTourStep[];
50
55
  }
51
56
  /** The tour api returned by `useBitkitTour` — pass it around to control the tour from anywhere. */
@@ -54,6 +59,8 @@ export type BitkitTourInstance = Omit<UseTourReturn, 'addStep' | 'setSteps' | 's
54
59
  setSteps: (steps: BitkitTourStep[]) => void;
55
60
  /** The current step, with the Bitkit slots (`badge`, `content`, `secondaryAction`) still typed on it. */
56
61
  step: BitkitTourStep | null;
62
+ /** Closes the tour, the same way its close button does. */
63
+ dismiss: () => void;
57
64
  updateStep: (id: string, stepOverrides: Partial<BitkitTourStep>) => void;
58
65
  };
59
66
  export type BitkitTourProps = {
@@ -2,6 +2,7 @@ import BitkitCloseButton from "../BitkitCloseButton/BitkitCloseButton.js";
2
2
  import BitkitButton from "../BitkitButton/BitkitButton.js";
3
3
  import { Box } from "@chakra-ui/react/box";
4
4
  import { chakra, useSlotRecipe } from "@chakra-ui/react/styled-system";
5
+ import { useCallback, useEffect } from "react";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import { Portal } from "@chakra-ui/react/portal";
7
8
  import { Tour, useTour } from "@ark-ui/react/tour";
@@ -32,8 +33,23 @@ var useBitkitTour = (options) => {
32
33
  type: "tooltip"
33
34
  }))
34
35
  });
36
+ const dismiss = useCallback(() => {
37
+ const closeTriggerClick = tour.getCloseTriggerProps().onClick;
38
+ closeTriggerClick?.();
39
+ }, [tour]);
40
+ const { open } = options;
41
+ useEffect(() => {
42
+ if (open === void 0 || open === tour.open) return;
43
+ if (open) tour.start();
44
+ else dismiss();
45
+ }, [
46
+ dismiss,
47
+ open,
48
+ tour.open
49
+ ]);
35
50
  return {
36
51
  ...tour,
52
+ dismiss,
37
53
  step: tour.step
38
54
  };
39
55
  };
@@ -62,6 +78,7 @@ var SecondaryAction = ({ secondaryAction }) => {
62
78
  action,
63
79
  label
64
80
  },
81
+ "aria-label": label,
65
82
  children: /* @__PURE__ */ jsx(BitkitButton, {
66
83
  size: "sm",
67
84
  variant: "tertiary",
@@ -129,6 +146,7 @@ var BitkitTour = ({ tour }) => {
129
146
  children: actions.map((action, index) => /* @__PURE__ */ jsx(Tour.ActionTrigger, {
130
147
  asChild: true,
131
148
  action,
149
+ "aria-label": action.label,
132
150
  children: /* @__PURE__ */ jsx(BitkitButton, {
133
151
  size: "sm",
134
152
  variant: index === actions.length - 1 ? "primary" : "secondary",
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitTour.js","names":[],"sources":["../../../lib/components/BitkitTour/BitkitTour.tsx"],"sourcesContent":["import {\n Tour as ArkTour,\n type TourStepDetails,\n useTour,\n type UseTourProps,\n type UseTourReturn,\n} from '@ark-ui/react/tour';\nimport { Box } from '@chakra-ui/react/box';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { type ReactNode } from 'react';\n\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\n\ntype BitkitTourStepAction = NonNullable<TourStepDetails['actions']>[number];\n\n/**\n * The link-style action in the footer, e.g. `Skip tour` or `Learn more`. It either drives the tour\n * (`action`) or leaves the page (`href`) — the union makes passing both, or neither, a type error.\n */\nexport type BitkitTourSecondaryAction =\n | {\n /**\n * A tour action — `'skip'`, `'dismiss'`, `'next'`, `'prev'`, or a function that receives the\n * action map (`{ next, prev, dismiss, skip, goto }`) to do something else first.\n */\n action: NonNullable<BitkitTourStepAction['action']>;\n href?: never;\n label: string;\n }\n | {\n action?: never;\n /** Renders the action as a link out. */\n href: string;\n label: string;\n };\n\n/**\n * A single step of the tour. On top of Ark's step details it carries the three Bitkit slots —\n * `badge`, `content` and `secondaryAction`. Zag spreads the whole step object when it normalises\n * it, so these reach `tour.step` untouched. (Zag also offers a `meta` bag for this; flat fields\n * read better at the call site, at the cost of a name clash if Zag ever adds one of these.)\n *\n * Ark's step `type` is not part of this: every step is a card anchored to an element, so the hook\n * sets it. `target` is required for the same reason — Zag turns a step without one into a centred\n * dialog, which is a presentation this component doesn't have.\n */\nexport type BitkitTourStep = Omit<TourStepDetails, 'type'> & {\n /**\n * Rendered next to the title — a `BitkitBadge` in practice, e.g. `BETA` or `NEW`. Nothing about\n * the badge is fixed here beyond its placement: the element itself comes from the caller.\n */\n badge?: ReactNode;\n /** Rendered between the description and the footer — an image, a video, a table, anything. */\n content?: ReactNode;\n secondaryAction?: BitkitTourSecondaryAction;\n /** The element the step points at. */\n target: NonNullable<TourStepDetails['target']>;\n};\n\nexport interface BitkitTourOptions extends Omit<UseTourProps, 'closeOnInteractOutside' | 'onStepsChange' | 'steps'> {\n /** Refined so the callback reports the Bitkit slots too, like the rest of the api does. */\n onStepsChange?: (details: { steps: BitkitTourStep[] }) => void;\n steps: BitkitTourStep[];\n}\n/** The tour api returned by `useBitkitTour` — pass it around to control the tour from anywhere. */\nexport type BitkitTourInstance = Omit<UseTourReturn, 'addStep' | 'setSteps' | 'step' | 'updateStep'> & {\n addStep: (step: BitkitTourStep) => void;\n setSteps: (steps: BitkitTourStep[]) => void;\n /** The current step, with the Bitkit slots (`badge`, `content`, `secondaryAction`) still typed on it. */\n step: BitkitTourStep | null;\n updateStep: (id: string, stepOverrides: Partial<BitkitTourStep>) => void;\n};\nexport type BitkitTourProps = {\n /** The tour instance returned by `useBitkitTour`. Start it with `tour.start()`. */\n tour: BitkitTourInstance;\n};\n\n/**\n * Creates the tour instance that drives `BitkitTour`. Takes the steps plus the tour-level options\n * (`onStepChange`, `onStatusChange`, `closeOnEscape`, `preventInteraction`, `translations`, …) and\n * returns the api used to control it: `start()`, `next()`, `prev()`, `setStep()`, `step`,\n * `stepIndex`, `totalSteps`. Closing on a click outside isn't among them — see below.\n */\nexport const useBitkitTour = (options: BitkitTourOptions): BitkitTourInstance => {\n const tour = useTour({\n // The highlight sits 8 around the target, and the card 12 from the highlight — the 8 the arrow\n // takes plus the 4 gap the design asks for. Both are measured from the highlight, since Zag\n // grows the anchor rect by `spotlightOffset` before positioning. `offset.mainAxis` replaces\n // Zag's own gutter, and Popper adds half the arrow's box on top of it: 6 + 5.65 ≈ the 12.\n spotlightOffset: { x: 8, y: 8 },\n // 4 is also Zag's current default, but the design asks for it and a third-party default is not\n // ours to rely on — the z-index contract already moved under us once between two minors.\n spotlightRadius: 4,\n ...options,\n // Not part of the options, and set after the spread so it can't be turned back on: a tour is a\n // guided flow with its own ways out — the close button, `Skip tour`, `Done` — and Zag would\n // otherwise end it on any click outside the card, losing the whole thing to a stray click on\n // the dimmed page. Escape is a deliberate keystroke rather than a stray click, so it keeps\n // closing the tour by default — and unlike this one, `closeOnEscape` stays in the options for\n // callers who want it off.\n closeOnInteractOutside: false,\n // Same reason as the cast on `Tour.Root` below: our step type isn't a supertype of Ark's, so a\n // callback taking ours doesn't match the signature Ark declares. The payload is Ark's.\n onStepsChange: options.onStepsChange as UseTourProps['onStepsChange'],\n // Zag's default is `close tour`, and this one is visible rather than screen-reader only: the\n // close button shows its own aria-label in a tooltip. The rest of Zag's translations\n // (`nextStep`, `prevStep`, `skip`) stay as they are — those only ever reach assistive tech.\n translations: { close: 'Close tour', ...options.translations },\n // `type` last: there is one presentation, so it isn't the caller's to pick.\n steps: options.steps.map((step) => ({ offset: { mainAxis: 6 }, ...step, type: 'tooltip' as const })),\n });\n\n return { ...tour, step: tour.step as BitkitTourStep | null };\n};\n\n// The Ark parts aren't Chakra components, so they're wrapped with the `chakra()` factory to accept\n// `css`. They're deliberately NOT wrapped with `asChild`: each Ark part falls back to the current\n// step's content (title, description, progress text, action label) only when it renders its own\n// children, and `asChild` would replace those children with the wrapper element.\nconst TourBackdrop = chakra(ArkTour.Backdrop);\nconst TourSpotlight = chakra(ArkTour.Spotlight);\nconst TourPositioner = chakra(ArkTour.Positioner);\nconst TourContent = chakra(ArkTour.Content);\nconst TourArrow = chakra(ArkTour.Arrow);\nconst TourArrowTip = chakra(ArkTour.ArrowTip);\nconst TourTitle = chakra(ArkTour.Title);\nconst TourDescription = chakra(ArkTour.Description);\nconst TourControl = chakra(ArkTour.Control);\nconst TourProgressText = chakra(ArkTour.ProgressText);\n\nconst SecondaryAction = ({ secondaryAction }: { secondaryAction: BitkitTourSecondaryAction }) => {\n const { action, href, label } = secondaryAction;\n\n if (href !== undefined) {\n return (\n <BitkitButton href={href} size=\"sm\" variant=\"tertiary\">\n {label}\n </BitkitButton>\n );\n }\n\n if (!action) return null;\n\n return (\n <ArkTour.ActionTrigger asChild action={{ action, label }}>\n <BitkitButton size=\"sm\" variant=\"tertiary\">\n {label}\n </BitkitButton>\n </ArkTour.ActionTrigger>\n );\n};\n\n/**\n * A guided product tour: a step card anchored to an element on the page, with a spotlight cut out\n * of the dimmed backdrop around that element.\n *\n * The steps and the tour behaviour live on the instance — create it with `useBitkitTour` and render\n * `BitkitTour` once, anywhere in the tree. Each step declares its own action buttons; the last one\n * is rendered as the primary button. A single-step tour drops the `1 of n` label, which is what\n * turns the card into the single-feature presentation.\n */\nconst BitkitTour = ({ tour }: BitkitTourProps) => {\n const recipe = useSlotRecipe({ key: 'tour' });\n const styles = recipe();\n const step = tour.step;\n const actions = step?.actions ?? [];\n\n return (\n // `unmountOnExit` isn't only tidiness: Ark's backdrop applies `hidden: !tour.step?.backdrop`\n // after merging the presence props, which overrides the presence's own `hidden` — and since the\n // step is kept after the tour closes, the dimming would stay on screen for good. Unmounting on\n // exit takes the node out before that can happen.\n // The cast is the price of refining `step` and the step-mutating methods on the instance: our\n // step type isn't a supertype of Ark's, so the methods no longer match its api signature. The\n // object is Ark's api — only the types are ours.\n <ArkTour.Root unmountOnExit tour={tour as UseTourReturn}>\n <Portal>\n <TourBackdrop css={styles.backdrop} />\n <TourSpotlight css={styles.spotlight} />\n <TourPositioner css={styles.positioner}>\n <TourContent css={styles.content}>\n {/* `hidden={false}` overrides Zag, which hides the arrow until the card has been\n positioned — while Popper computes that position from the arrow's `clientHeight`, so\n a hidden arrow measures 0 and the card lands 6 short of the design's 12, then drops\n by that 6 as soon as anything recomputes. Nothing shows in the meantime: until it is\n positioned the card itself sits off-screen at `-100vh`. */}\n <TourArrow css={styles.arrow} hidden={false}>\n <TourArrowTip css={styles.arrowTip} />\n </TourArrow>\n <Box css={styles.header}>\n <Box css={styles.titleGroup}>\n {tour.totalSteps > 1 ? <TourProgressText css={styles.progressText} /> : null}\n <Box css={styles.titleRow}>\n <TourTitle css={styles.title} />\n {step?.badge ? <Box css={styles.badge}>{step.badge}</Box> : null}\n </Box>\n </Box>\n <ArkTour.CloseTrigger asChild>\n <BitkitCloseButton css={styles.closeTrigger} size=\"xs\" />\n </ArkTour.CloseTrigger>\n </Box>\n <TourDescription css={styles.description} />\n {step?.content ? <Box css={styles.media}>{step.content}</Box> : null}\n <TourControl css={styles.control}>\n {step?.secondaryAction ? <SecondaryAction secondaryAction={step.secondaryAction} /> : null}\n <Box css={styles.actions}>\n {actions.map((action, index) => (\n <ArkTour.ActionTrigger key={action.label} asChild action={action}>\n <BitkitButton size=\"sm\" variant={index === actions.length - 1 ? 'primary' : 'secondary'}>\n {action.label}\n </BitkitButton>\n </ArkTour.ActionTrigger>\n ))}\n </Box>\n </TourControl>\n </TourContent>\n </TourPositioner>\n </Portal>\n </ArkTour.Root>\n );\n};\n\nBitkitTour.displayName = 'BitkitTour';\n\nexport default BitkitTour;\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitTour.js","names":[],"sources":["../../../lib/components/BitkitTour/BitkitTour.tsx"],"sourcesContent":["import {\n Tour as ArkTour,\n type TourStepDetails,\n useTour,\n type UseTourProps,\n type UseTourReturn,\n} from '@ark-ui/react/tour';\nimport { Box } from '@chakra-ui/react/box';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { type ReactNode, useCallback, useEffect } from 'react';\n\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\n\ntype BitkitTourStepAction = NonNullable<TourStepDetails['actions']>[number];\n\n/**\n * The link-style action in the footer, e.g. `Skip tour` or `Learn more`. It either drives the tour\n * (`action`) or leaves the page (`href`) — the union makes passing both, or neither, a type error.\n */\nexport type BitkitTourSecondaryAction =\n | {\n /**\n * A tour action — `'skip'`, `'dismiss'`, `'next'`, `'prev'`, or a function that receives the\n * action map (`{ next, prev, dismiss, skip, goto }`) to do something else first.\n */\n action: NonNullable<BitkitTourStepAction['action']>;\n href?: never;\n label: string;\n }\n | {\n action?: never;\n /** Renders the action as a link out. */\n href: string;\n label: string;\n };\n\n/**\n * A single step of the tour. On top of Ark's step details it carries the three Bitkit slots —\n * `badge`, `content` and `secondaryAction`. Zag spreads the whole step object when it normalises\n * it, so these reach `tour.step` untouched. (Zag also offers a `meta` bag for this; flat fields\n * read better at the call site, at the cost of a name clash if Zag ever adds one of these.)\n *\n * Ark's step `type` is not part of this: every step is a card anchored to an element, so the hook\n * sets it. `target` is required for the same reason — Zag turns a step without one into a centred\n * dialog, which is a presentation this component doesn't have.\n */\nexport type BitkitTourStep = Omit<TourStepDetails, 'type'> & {\n /**\n * Rendered next to the title — a `BitkitBadge` in practice, e.g. `BETA` or `NEW`. Nothing about\n * the badge is fixed here beyond its placement: the element itself comes from the caller.\n */\n badge?: ReactNode;\n /** Rendered between the description and the footer — an image, a video, a table, anything. */\n content?: ReactNode;\n secondaryAction?: BitkitTourSecondaryAction;\n /** The element the step points at. */\n target: NonNullable<TourStepDetails['target']>;\n};\n\nexport interface BitkitTourOptions extends Omit<UseTourProps, 'closeOnInteractOutside' | 'onStepsChange' | 'steps'> {\n /** Refined so the callback reports the Bitkit slots too, like the rest of the api does. */\n onStepsChange?: (details: { steps: BitkitTourStep[] }) => void;\n /**\n * Drives the tour from a value the caller already has — it starts when this turns true and closes\n * when it turns false. Omit it to drive the tour with `start()` and `dismiss()` instead.\n */\n open?: boolean;\n steps: BitkitTourStep[];\n}\n/** The tour api returned by `useBitkitTour` — pass it around to control the tour from anywhere. */\nexport type BitkitTourInstance = Omit<UseTourReturn, 'addStep' | 'setSteps' | 'step' | 'updateStep'> & {\n addStep: (step: BitkitTourStep) => void;\n setSteps: (steps: BitkitTourStep[]) => void;\n /** The current step, with the Bitkit slots (`badge`, `content`, `secondaryAction`) still typed on it. */\n step: BitkitTourStep | null;\n /** Closes the tour, the same way its close button does. */\n dismiss: () => void;\n updateStep: (id: string, stepOverrides: Partial<BitkitTourStep>) => void;\n};\nexport type BitkitTourProps = {\n /** The tour instance returned by `useBitkitTour`. Start it with `tour.start()`. */\n tour: BitkitTourInstance;\n};\n\n/**\n * Creates the tour instance that drives `BitkitTour`. Takes the steps plus the tour-level options\n * (`onStepChange`, `onStatusChange`, `closeOnEscape`, `preventInteraction`, `translations`, …) and\n * returns the api used to control it: `start()`, `next()`, `prev()`, `setStep()`, `step`,\n * `stepIndex`, `totalSteps`. Closing on a click outside isn't among them — see below.\n */\nexport const useBitkitTour = (options: BitkitTourOptions): BitkitTourInstance => {\n const tour = useTour({\n // The highlight sits 8 around the target, and the card 12 from the highlight — the 8 the arrow\n // takes plus the 4 gap the design asks for. Both are measured from the highlight, since Zag\n // grows the anchor rect by `spotlightOffset` before positioning. `offset.mainAxis` replaces\n // Zag's own gutter, and Popper adds half the arrow's box on top of it: 6 + 5.65 ≈ the 12.\n spotlightOffset: { x: 8, y: 8 },\n // 4 is also Zag's current default, but the design asks for it and a third-party default is not\n // ours to rely on — the z-index contract already moved under us once between two minors.\n spotlightRadius: 4,\n ...options,\n // Not part of the options, and set after the spread so it can't be turned back on: a tour is a\n // guided flow with its own ways out — the close button, `Skip tour`, `Done` — and Zag would\n // otherwise end it on any click outside the card, losing the whole thing to a stray click on\n // the dimmed page. Escape is a deliberate keystroke rather than a stray click, so it keeps\n // closing the tour by default — and unlike this one, `closeOnEscape` stays in the options for\n // callers who want it off.\n closeOnInteractOutside: false,\n // Same reason as the cast on `Tour.Root` below: our step type isn't a supertype of Ark's, so a\n // callback taking ours doesn't match the signature Ark declares. The payload is Ark's.\n onStepsChange: options.onStepsChange as UseTourProps['onStepsChange'],\n // Zag's default is `close tour`, and this one is visible rather than screen-reader only: the\n // close button shows its own aria-label in a tooltip. The rest of Zag's translations\n // (`nextStep`, `prevStep`, `skip`) stay as they are — those only ever reach assistive tech.\n translations: { close: 'Close tour', ...options.translations },\n // `type` last: there is one presentation, so it isn't the caller's to pick.\n steps: options.steps.map((step) => ({ offset: { mainAxis: 6 }, ...step, type: 'tooltip' as const })),\n });\n\n // Zag's api can start a tour but not stop one. Its close trigger's click handler *is* the\n // machine's dismiss, so this borrows that instead of reaching into the machine. The cast says\n // what the handler is rather than manufacturing an event for it: `actionMap.dismiss` takes none.\n const dismiss = useCallback(() => {\n const closeTriggerClick = tour.getCloseTriggerProps().onClick as (() => void) | undefined;\n\n closeTriggerClick?.();\n }, [tour]);\n\n const { open } = options;\n useEffect(() => {\n if (open === undefined || open === tour.open) {\n return;\n }\n\n if (open) {\n tour.start();\n } else {\n dismiss();\n }\n // `tour` is a new object on every render, so the machine's own `open` is the dependency that\n // matters here.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [dismiss, open, tour.open]);\n\n return { ...tour, dismiss, step: tour.step as BitkitTourStep | null };\n};\n\n// The Ark parts aren't Chakra components, so they're wrapped with the `chakra()` factory to accept\n// `css`. They're deliberately NOT wrapped with `asChild`: each Ark part falls back to the current\n// step's content (title, description, progress text, action label) only when it renders its own\n// children, and `asChild` would replace those children with the wrapper element.\nconst TourBackdrop = chakra(ArkTour.Backdrop);\nconst TourSpotlight = chakra(ArkTour.Spotlight);\nconst TourPositioner = chakra(ArkTour.Positioner);\nconst TourContent = chakra(ArkTour.Content);\nconst TourArrow = chakra(ArkTour.Arrow);\nconst TourArrowTip = chakra(ArkTour.ArrowTip);\nconst TourTitle = chakra(ArkTour.Title);\nconst TourDescription = chakra(ArkTour.Description);\nconst TourControl = chakra(ArkTour.Control);\nconst TourProgressText = chakra(ArkTour.ProgressText);\n\nconst SecondaryAction = ({ secondaryAction }: { secondaryAction: BitkitTourSecondaryAction }) => {\n const { action, href, label } = secondaryAction;\n\n if (href !== undefined) {\n return (\n <BitkitButton href={href} size=\"sm\" variant=\"tertiary\">\n {label}\n </BitkitButton>\n );\n }\n\n if (!action) return null;\n\n return (\n <ArkTour.ActionTrigger asChild action={{ action, label }} aria-label={label}>\n <BitkitButton size=\"sm\" variant=\"tertiary\">\n {label}\n </BitkitButton>\n </ArkTour.ActionTrigger>\n );\n};\n\n/**\n * A guided product tour: a step card anchored to an element on the page, with a spotlight cut out\n * of the dimmed backdrop around that element.\n *\n * The steps and the tour behaviour live on the instance — create it with `useBitkitTour` and render\n * `BitkitTour` once, anywhere in the tree. Each step declares its own action buttons; the last one\n * is rendered as the primary button. A single-step tour drops the `1 of n` label, which is what\n * turns the card into the single-feature presentation.\n */\nconst BitkitTour = ({ tour }: BitkitTourProps) => {\n const recipe = useSlotRecipe({ key: 'tour' });\n const styles = recipe();\n const step = tour.step;\n const actions = step?.actions ?? [];\n\n return (\n // `unmountOnExit` isn't only tidiness: Ark's backdrop applies `hidden: !tour.step?.backdrop`\n // after merging the presence props, which overrides the presence's own `hidden` — and since the\n // step is kept after the tour closes, the dimming would stay on screen for good. Unmounting on\n // exit takes the node out before that can happen.\n // The cast is the price of refining `step` and the step-mutating methods on the instance: our\n // step type isn't a supertype of Ark's, so the methods no longer match its api signature. The\n // object is Ark's api — only the types are ours.\n <ArkTour.Root unmountOnExit tour={tour as UseTourReturn}>\n <Portal>\n <TourBackdrop css={styles.backdrop} />\n <TourSpotlight css={styles.spotlight} />\n <TourPositioner css={styles.positioner}>\n <TourContent css={styles.content}>\n {/* `hidden={false}` overrides Zag, which hides the arrow until the card has been\n positioned — while Popper computes that position from the arrow's `clientHeight`, so\n a hidden arrow measures 0 and the card lands 6 short of the design's 12, then drops\n by that 6 as soon as anything recomputes. Nothing shows in the meantime: until it is\n positioned the card itself sits off-screen at `-100vh`. */}\n <TourArrow css={styles.arrow} hidden={false}>\n <TourArrowTip css={styles.arrowTip} />\n </TourArrow>\n <Box css={styles.header}>\n <Box css={styles.titleGroup}>\n {tour.totalSteps > 1 ? <TourProgressText css={styles.progressText} /> : null}\n <Box css={styles.titleRow}>\n <TourTitle css={styles.title} />\n {step?.badge ? <Box css={styles.badge}>{step.badge}</Box> : null}\n </Box>\n </Box>\n <ArkTour.CloseTrigger asChild>\n <BitkitCloseButton css={styles.closeTrigger} size=\"xs\" />\n </ArkTour.CloseTrigger>\n </Box>\n <TourDescription css={styles.description} />\n {step?.content ? <Box css={styles.media}>{step.content}</Box> : null}\n <TourControl css={styles.control}>\n {step?.secondaryAction ? <SecondaryAction secondaryAction={step.secondaryAction} /> : null}\n <Box css={styles.actions}>\n {actions.map((action, index) => (\n // Zag labels the trigger from its own translations (`close tour`, `next step`),\n // which would override the visible text as the accessible name — and put two\n // `Close tour` buttons on a card whose dismiss action reads `Got it`.\n <ArkTour.ActionTrigger key={action.label} asChild action={action} aria-label={action.label}>\n <BitkitButton size=\"sm\" variant={index === actions.length - 1 ? 'primary' : 'secondary'}>\n {action.label}\n </BitkitButton>\n </ArkTour.ActionTrigger>\n ))}\n </Box>\n </TourControl>\n </TourContent>\n </TourPositioner>\n </Portal>\n </ArkTour.Root>\n );\n};\n\nBitkitTour.displayName = 'BitkitTour';\n\nexport default BitkitTour;\n"],"mappings":"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"}
@@ -6,14 +6,12 @@ var collapsibleSlotRecipe = defineSlotRecipe({
6
6
  slots: [...collapsibleAnatomy.keys()],
7
7
  base: {
8
8
  root: {
9
- display: "flex",
10
- flexDirection: "column",
11
- width: "100%"
9
+ width: "100%",
10
+ color: "text/body",
11
+ textStyle: "body/lg/regular"
12
12
  },
13
13
  content: {
14
14
  overflow: "hidden",
15
- color: "text/body",
16
- textStyle: "body/lg/regular",
17
15
  position: "relative",
18
16
  _open: {
19
17
  animationName: "expand-height",
@@ -35,7 +33,7 @@ var collapsibleSlotRecipe = defineSlotRecipe({
35
33
  }
36
34
  },
37
35
  trigger: {
38
- display: "inline-flex",
36
+ display: "flex",
39
37
  alignItems: "center",
40
38
  color: "text/link",
41
39
  cursor: "pointer",
@@ -1 +1 @@
1
- {"version":3,"file":"Collapsible.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Collapsible.recipe.ts"],"sourcesContent":["import { collapsibleAnatomy } from '@chakra-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nconst collapsibleSlotRecipe = defineSlotRecipe({\n className: 'collapsible',\n slots: [...collapsibleAnatomy.keys()],\n base: {\n root: {\n display: 'flex',\n flexDirection: 'column',\n width: '100%',\n },\n content: {\n overflow: 'hidden',\n color: 'text/body',\n textStyle: 'body/lg/regular',\n position: 'relative',\n _open: {\n animationName: 'expand-height',\n animationDuration: 'moderate',\n },\n _closed: {\n animationName: 'collapse-height',\n animationDuration: 'moderate',\n _after: {\n content: '\"\"',\n position: 'absolute',\n insetInlineStart: 0,\n insetInlineEnd: 0,\n bottom: 0,\n height: '32',\n background: 'linear-gradient(to top, {colors.neutral.100}, transparent)',\n pointerEvents: 'none',\n },\n },\n },\n trigger: {\n display: 'inline-flex',\n alignItems: 'center',\n color: 'text/link',\n cursor: 'pointer',\n gap: '4',\n textStyle: 'body/md/regular',\n paddingBlockStart: '8',\n width: 'fit-content',\n _hover: {\n color: 'text/link-hover',\n _active: {\n color: 'text/primary',\n },\n },\n _active: {\n color: 'text/primary',\n },\n },\n indicator: {},\n },\n});\n\nexport default collapsibleSlotRecipe;\n"],"mappings":";;;AAGA,IAAM,wBAAwB,iBAAiB;CAC7C,WAAW;CACX,OAAO,CAAC,GAAG,mBAAmB,KAAK,CAAC;CACpC,MAAM;EACJ,MAAM;GACJ,SAAS;GACT,eAAe;GACf,OAAO;EACT;EACA,SAAS;GACP,UAAU;GACV,OAAO;GACP,WAAW;GACX,UAAU;GACV,OAAO;IACL,eAAe;IACf,mBAAmB;GACrB;GACA,SAAS;IACP,eAAe;IACf,mBAAmB;IACnB,QAAQ;KACN,SAAS;KACT,UAAU;KACV,kBAAkB;KAClB,gBAAgB;KAChB,QAAQ;KACR,QAAQ;KACR,YAAY;KACZ,eAAe;IACjB;GACF;EACF;EACA,SAAS;GACP,SAAS;GACT,YAAY;GACZ,OAAO;GACP,QAAQ;GACR,KAAK;GACL,WAAW;GACX,mBAAmB;GACnB,OAAO;GACP,QAAQ;IACN,OAAO;IACP,SAAS,EACP,OAAO,eACT;GACF;GACA,SAAS,EACP,OAAO,eACT;EACF;EACA,WAAW,CAAC;CACd;AACF,CAAC"}
1
+ {"version":3,"file":"Collapsible.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Collapsible.recipe.ts"],"sourcesContent":["import { collapsibleAnatomy } from '@chakra-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nconst collapsibleSlotRecipe = defineSlotRecipe({\n className: 'collapsible',\n slots: [...collapsibleAnatomy.keys()],\n base: {\n root: {\n width: '100%',\n color: 'text/body',\n textStyle: 'body/lg/regular',\n },\n content: {\n overflow: 'hidden',\n position: 'relative',\n _open: {\n animationName: 'expand-height',\n animationDuration: 'moderate',\n },\n _closed: {\n animationName: 'collapse-height',\n animationDuration: 'moderate',\n _after: {\n content: '\"\"',\n position: 'absolute',\n insetInlineStart: 0,\n insetInlineEnd: 0,\n bottom: 0,\n height: '32',\n background: 'linear-gradient(to top, {colors.neutral.100}, transparent)',\n pointerEvents: 'none',\n },\n },\n },\n trigger: {\n display: 'flex',\n alignItems: 'center',\n color: 'text/link',\n cursor: 'pointer',\n gap: '4',\n textStyle: 'body/md/regular',\n paddingBlockStart: '8',\n width: 'fit-content',\n _hover: {\n color: 'text/link-hover',\n _active: {\n color: 'text/primary',\n },\n },\n _active: {\n color: 'text/primary',\n },\n },\n indicator: {},\n },\n});\n\nexport default collapsibleSlotRecipe;\n"],"mappings":";;;AAGA,IAAM,wBAAwB,iBAAiB;CAC7C,WAAW;CACX,OAAO,CAAC,GAAG,mBAAmB,KAAK,CAAC;CACpC,MAAM;EACJ,MAAM;GACJ,OAAO;GACP,OAAO;GACP,WAAW;EACb;EACA,SAAS;GACP,UAAU;GACV,UAAU;GACV,OAAO;IACL,eAAe;IACf,mBAAmB;GACrB;GACA,SAAS;IACP,eAAe;IACf,mBAAmB;IACnB,QAAQ;KACN,SAAS;KACT,UAAU;KACV,kBAAkB;KAClB,gBAAgB;KAChB,QAAQ;KACR,QAAQ;KACR,YAAY;KACZ,eAAe;IACjB;GACF;EACF;EACA,SAAS;GACP,SAAS;GACT,YAAY;GACZ,OAAO;GACP,QAAQ;GACR,KAAK;GACL,WAAW;GACX,mBAAmB;GACnB,OAAO;GACP,QAAQ;IACN,OAAO;IACP,SAAS,EACP,OAAO,eACT;GACF;GACA,SAAS,EACP,OAAO,eACT;EACF;EACA,WAAW,CAAC;CACd;AACF,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit-v2",
3
3
  "private": false,
4
- "version": "0.3.355",
4
+ "version": "0.3.357",
5
5
  "description": "Bitrise Design System Components built with Chakra UI V3",
6
6
  "keywords": [
7
7
  "react",