@bitrise/bitkit-v2 0.3.345 → 0.3.347

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.
@@ -5,7 +5,7 @@ import { default as semanticColors } from '../../theme/semantic-tokens/semanticC
5
5
  * Derived from the semantic token map, so new tokens are picked up automatically.
6
6
  */
7
7
  export type BitkitProgressBarRangeColor = Extract<keyof typeof semanticColors, `color/${string}`>;
8
- export interface BitkitProgressBarProps extends Omit<ProgressRootProps, 'aria-label' | 'children' | 'colorPalette' | 'roundedEdges' | 'size' | 'trackColor'> {
8
+ export interface BitkitProgressBarProps extends Omit<ProgressRootProps, 'aria-label' | 'children' | 'colorPalette' | 'roundedEdges' | 'size' | 'striped' | 'trackColor'> {
9
9
  /**
10
10
  * Accessible name for the bar — never rendered visually. Without it screen readers
11
11
  * announce the percentage alone, with no indication of what is being measured.
@@ -19,6 +19,11 @@ export interface BitkitProgressBarProps extends Omit<ProgressRootProps, 'aria-la
19
19
  roundedEdges?: 'both' | 'end' | 'none' | 'start';
20
20
  /** Height of the bar, in size tokens. */
21
21
  size?: '1' | '2' | '4' | '6' | '8' | '12' | '16';
22
+ /**
23
+ * Fills the range with animated diagonal stripes — for work that is running but whose
24
+ * remaining time is unknown, so the bar keeps moving between value changes.
25
+ */
26
+ striped?: boolean;
22
27
  /** Background of the unfilled part. */
23
28
  trackColor?: 'neutral' | 'transparent';
24
29
  }
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { Progress } from "@chakra-ui/react/progress";
4
4
  //#region lib/components/BitkitProgressBar/BitkitProgressBar.tsx
5
5
  var BitkitProgressBar = forwardRef((props, ref) => {
6
- const { label, rangeColor = "color/blue/base", roundedEdges = "both", size = "8", trackColor = "neutral", translations, ...rest } = props;
6
+ const { label, rangeColor = "color/blue/base", roundedEdges = "both", size = "8", striped = false, trackColor = "neutral", translations, ...rest } = props;
7
7
  const resolvedTranslations = useMemo(() => label ? {
8
8
  ...translations,
9
9
  value: () => label
@@ -12,10 +12,11 @@ var BitkitProgressBar = forwardRef((props, ref) => {
12
12
  ref,
13
13
  roundedEdges,
14
14
  size,
15
+ striped,
15
16
  trackColor,
16
17
  translations: resolvedTranslations,
17
18
  ...rest,
18
- children: /* @__PURE__ */ jsx(Progress.Track, { children: /* @__PURE__ */ jsx(Progress.Range, { background: rangeColor }) })
19
+ children: /* @__PURE__ */ jsx(Progress.Track, { children: /* @__PURE__ */ jsx(Progress.Range, { backgroundColor: rangeColor }) })
19
20
  });
20
21
  });
21
22
  BitkitProgressBar.displayName = "BitkitProgressBar";
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitProgressBar.js","names":[],"sources":["../../../lib/components/BitkitProgressBar/BitkitProgressBar.tsx"],"sourcesContent":["import { Progress, type ProgressRootProps } from '@chakra-ui/react/progress';\nimport { forwardRef, useMemo } from 'react';\n\nimport type semanticColors from '../../theme/semantic-tokens/semanticColors';\n\n/**\n * Every `color/*` semantic token — the only colors the range is allowed to use.\n * Derived from the semantic token map, so new tokens are picked up automatically.\n */\nexport type BitkitProgressBarRangeColor = Extract<keyof typeof semanticColors, `color/${string}`>;\n\nexport interface BitkitProgressBarProps extends Omit<\n ProgressRootProps,\n 'aria-label' | 'children' | 'colorPalette' | 'roundedEdges' | 'size' | 'trackColor'\n> {\n /**\n * Accessible name for the bar — never rendered visually. Without it screen readers\n * announce the percentage alone, with no indication of what is being measured.\n * The value itself stays exposed through `aria-valuenow`, so this should name the\n * thing (\"Monthly build limit\"), not repeat the number.\n */\n label?: string;\n /** Color of the filled part. Any `color/*` semantic token. */\n rangeColor?: BitkitProgressBarRangeColor;\n /** Which ends are rounded. Use `start`/`none`/`end` for bars sitting next to each other in a group. */\n roundedEdges?: 'both' | 'end' | 'none' | 'start';\n /** Height of the bar, in size tokens. */\n size?: '1' | '2' | '4' | '6' | '8' | '12' | '16';\n /** Background of the unfilled part. */\n trackColor?: 'neutral' | 'transparent';\n}\n\nconst BitkitProgressBar = forwardRef<HTMLDivElement, BitkitProgressBarProps>((props, ref) => {\n const {\n label,\n rangeColor = 'color/blue/base',\n roundedEdges = 'both',\n size = '8',\n trackColor = 'neutral',\n translations,\n ...rest\n } = props;\n\n // Zag puts role=\"progressbar\" and aria-label on the *track*, not the root, so an\n // `aria-label` prop would never reach the labelled element — hence `aria-label` is\n // omitted from the props and the name is routed through the machine's translations.\n // Merged rather than replaced: `IntlTranslations` has only `value` today, but Zag has\n // added keys to its internals before and a wholesale replace would drop them silently.\n const resolvedTranslations = useMemo(\n () => (label ? { ...translations, value: () => label } : translations),\n [label, translations],\n );\n\n return (\n <Progress.Root\n ref={ref}\n roundedEdges={roundedEdges}\n size={size}\n trackColor={trackColor}\n translations={resolvedTranslations}\n {...rest}\n >\n <Progress.Track>\n <Progress.Range background={rangeColor} />\n </Progress.Track>\n </Progress.Root>\n );\n});\n\nBitkitProgressBar.displayName = 'BitkitProgressBar';\n\nexport default BitkitProgressBar;\n"],"mappings":";;;;AAgCA,IAAM,oBAAoB,YAAoD,OAAO,QAAQ;CAC3F,MAAM,EACJ,OACA,aAAa,mBACb,eAAe,QACf,OAAO,KACP,aAAa,WACb,cACA,GAAG,SACD;CAOJ,MAAM,uBAAuB,cACpB,QAAQ;EAAE,GAAG;EAAc,aAAa;CAAM,IAAI,cACzD,CAAC,OAAO,YAAY,CACtB;CAEA,OACE,oBAAC,SAAS,MAAV;EACO;EACS;EACR;EACM;EACZ,cAAc;EACd,GAAI;EAEJ,UAAA,oBAAC,SAAS,OAAV,EAAA,UACE,oBAAC,SAAS,OAAV,EAAgB,YAAY,WAAa,CAAA,EAC3B,CAAA;CACH,CAAA;AAEnB,CAAC;AAED,kBAAkB,cAAc"}
1
+ {"version":3,"file":"BitkitProgressBar.js","names":[],"sources":["../../../lib/components/BitkitProgressBar/BitkitProgressBar.tsx"],"sourcesContent":["import { Progress, type ProgressRootProps } from '@chakra-ui/react/progress';\nimport { forwardRef, useMemo } from 'react';\n\nimport type semanticColors from '../../theme/semantic-tokens/semanticColors';\n\n/**\n * Every `color/*` semantic token — the only colors the range is allowed to use.\n * Derived from the semantic token map, so new tokens are picked up automatically.\n */\nexport type BitkitProgressBarRangeColor = Extract<keyof typeof semanticColors, `color/${string}`>;\n\nexport interface BitkitProgressBarProps extends Omit<\n ProgressRootProps,\n 'aria-label' | 'children' | 'colorPalette' | 'roundedEdges' | 'size' | 'striped' | 'trackColor'\n> {\n /**\n * Accessible name for the bar — never rendered visually. Without it screen readers\n * announce the percentage alone, with no indication of what is being measured.\n * The value itself stays exposed through `aria-valuenow`, so this should name the\n * thing (\"Monthly build limit\"), not repeat the number.\n */\n label?: string;\n /** Color of the filled part. Any `color/*` semantic token. */\n rangeColor?: BitkitProgressBarRangeColor;\n /** Which ends are rounded. Use `start`/`none`/`end` for bars sitting next to each other in a group. */\n roundedEdges?: 'both' | 'end' | 'none' | 'start';\n /** Height of the bar, in size tokens. */\n size?: '1' | '2' | '4' | '6' | '8' | '12' | '16';\n /**\n * Fills the range with animated diagonal stripes — for work that is running but whose\n * remaining time is unknown, so the bar keeps moving between value changes.\n */\n striped?: boolean;\n /** Background of the unfilled part. */\n trackColor?: 'neutral' | 'transparent';\n}\n\nconst BitkitProgressBar = forwardRef<HTMLDivElement, BitkitProgressBarProps>((props, ref) => {\n const {\n label,\n rangeColor = 'color/blue/base',\n roundedEdges = 'both',\n size = '8',\n striped = false,\n trackColor = 'neutral',\n translations,\n ...rest\n } = props;\n\n // Zag puts role=\"progressbar\" and aria-label on the *track*, not the root, so an\n // `aria-label` prop would never reach the labelled element — hence `aria-label` is\n // omitted from the props and the name is routed through the machine's translations.\n // Merged rather than replaced: `IntlTranslations` has only `value` today, but Zag has\n // added keys to its internals before and a wholesale replace would drop them silently.\n const resolvedTranslations = useMemo(\n () => (label ? { ...translations, value: () => label } : translations),\n [label, translations],\n );\n\n return (\n <Progress.Root\n ref={ref}\n roundedEdges={roundedEdges}\n size={size}\n striped={striped}\n trackColor={trackColor}\n translations={resolvedTranslations}\n {...rest}\n >\n <Progress.Track>\n <Progress.Range backgroundColor={rangeColor} />\n </Progress.Track>\n </Progress.Root>\n );\n});\n\nBitkitProgressBar.displayName = 'BitkitProgressBar';\n\nexport default BitkitProgressBar;\n"],"mappings":";;;;AAqCA,IAAM,oBAAoB,YAAoD,OAAO,QAAQ;CAC3F,MAAM,EACJ,OACA,aAAa,mBACb,eAAe,QACf,OAAO,KACP,UAAU,OACV,aAAa,WACb,cACA,GAAG,SACD;CAOJ,MAAM,uBAAuB,cACpB,QAAQ;EAAE,GAAG;EAAc,aAAa;CAAM,IAAI,cACzD,CAAC,OAAO,YAAY,CACtB;CAEA,OACE,oBAAC,SAAS,MAAV;EACO;EACS;EACR;EACG;EACG;EACZ,cAAc;EACd,GAAI;EAEJ,UAAA,oBAAC,SAAS,OAAV,EAAA,UACE,oBAAC,SAAS,OAAV,EAAgB,iBAAiB,WAAa,CAAA,EAChC,CAAA;CACH,CAAA;AAEnB,CAAC;AAED,kBAAkB,cAAc"}
@@ -126,7 +126,11 @@ var Trigger = forwardRef((props, ref) => {
126
126
  /* @__PURE__ */ jsxs(Box, {
127
127
  display: "flex",
128
128
  gap: "16",
129
- children: [children, Icon && /* @__PURE__ */ jsx(Icon, { size: "24" })]
129
+ children: [
130
+ children,
131
+ badge,
132
+ Icon && /* @__PURE__ */ jsx(Icon, { size: "24" })
133
+ ]
130
134
  }),
131
135
  !!secondaryText && /* @__PURE__ */ jsx(Text, {
132
136
  as: "span",
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitTabs.js","names":[],"sources":["../../../lib/components/BitkitTabs/BitkitTabs.tsx"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { createContext, mergeRefs, useSafeLayoutEffect } from '@chakra-ui/react';\nimport { Box } from '@chakra-ui/react/box';\nimport { Menu } from '@chakra-ui/react/menu';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { chakra, useRecipe } from '@chakra-ui/react/styled-system';\nimport { Tabs, useTabsContext, useTabsStyles } from '@chakra-ui/react/tabs';\nimport { Text } from '@chakra-ui/react/text';\nimport {\n Children,\n cloneElement,\n forwardRef,\n Fragment,\n isValidElement,\n type KeyboardEvent,\n type MouseEvent,\n type ReactElement,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { type BitkitIconComponent, IconCross, IconPlus } from '../../icons';\nimport BitkitControlButton, { type BitkitControlButtonProps } from '../BitkitControlButton/BitkitControlButton';\n\n/**\n * Root props. `onValueChange` is re-declared with a concrete `{ value }` arg so the callback\n * parameter is inferred at the call site: the base `Tabs.RootProps` intersection breaks\n * contextual typing, which otherwise leaves consumers' `({ value }) => …` flagged as an\n * implicit `any` under `noImplicitAny`.\n */\nexport type BitkitTabsRootProps = Omit<Tabs.RootProps, 'onValueChange'> & {\n onValueChange?: (details: { value: string }) => void;\n};\n\nexport const [RootPropsProvider, useRootPropsContext] = createContext<BitkitTabsRootProps>({\n name: 'RootPropsContext',\n hookName: 'useRootPropsContext',\n providerName: '<RootProps />',\n});\n\nconst Root = forwardRef<HTMLDivElement, BitkitTabsRootProps>((props, ref) => {\n return (\n <RootPropsProvider value={props}>\n <Tabs.Root ref={ref} {...props} />\n </RootPropsProvider>\n );\n});\n\n/** Status-dot colors for the `canvas` variant. */\nexport type BitkitTabsStatusColor = 'neutral' | 'purple' | 'red' | 'yellow';\n\nconst STATUS_COLOR_TOKEN: Record<BitkitTabsStatusColor, string> = {\n neutral: 'color/neutral/muted',\n purple: 'interactive/bold',\n red: 'color/red/base',\n yellow: 'color/yellow/bold',\n};\n\ntype TriggerProps = Tabs.TriggerProps & {\n badge?: ReactNode;\n children?: string;\n icon?: BitkitIconComponent;\n /** `canvas` variant only — when provided, a close button reveals on hover and calls this handler when clicked. */\n onClose?: () => void;\n secondaryText?: ReactNode;\n /** `canvas` variant only — shows a status indicator dot. When hovered (and `onClose` is set), the close button replaces it. */\n statusColor?: BitkitTabsStatusColor;\n};\n\nRoot.displayName = 'BitkitTabs.Root';\n\nconst Trigger = forwardRef<HTMLButtonElement, TriggerProps>((props, ref) => {\n const { badge, children, className, icon: Icon, onClose, onKeyDown, secondaryText, statusColor, ...rest } = props;\n // Mirror the recipe's defaultVariant so an unset `variant` renders the line layout (not the\n // contained one) — otherwise an undefined variant would fall through to the contained branch.\n const { variant = 'line' } = useRootPropsContext();\n const closeButtonRecipe = useRecipe({ key: 'closeButton' });\n\n // Close button and status dot are exclusive to the canvas variant.\n const isCanvas = variant === 'canvas';\n const closable = isCanvas && !!onClose && !rest.disabled;\n const hasStatus = isCanvas && !!statusColor && !rest.disabled;\n const showTrailing = closable || hasStatus;\n const mergedClassName = showTrailing ? (className ? `group ${className}` : 'group') : className;\n // The close button is always visible when there is no status dot; when the dot is shown it is\n // revealed on hover/focus instead (replacing the dot).\n const revealCloseOnInteraction = closable && hasStatus;\n const revealOnInteraction = { opacity: 1, pointerEvents: 'auto' } as const;\n\n const handleCloseClick = (event: MouseEvent<HTMLSpanElement>) => {\n event.stopPropagation();\n event.preventDefault();\n onClose?.();\n };\n\n // The close control is not in the tab order; keyboard users close the focused tab with Delete/Backspace.\n const handleTriggerKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (closable && (event.key === 'Delete' || event.key === 'Backspace')) {\n event.preventDefault();\n // Move focus to a sibling tab before removal so keyboard navigation can continue.\n const trigger = event.currentTarget;\n // Skip overflowed (`hidden`) tabs — they aren't focusable.\n const triggers = [\n ...(trigger.closest('[role=\"tablist\"]')?.querySelectorAll<HTMLElement>('[role=\"tab\"]:not([hidden])') ?? []),\n ];\n const index = triggers.indexOf(trigger);\n (triggers[index + 1] ?? triggers[index - 1])?.focus();\n onClose?.();\n }\n onKeyDown?.(event);\n };\n\n const trailing = showTrailing ? (\n <chakra.span\n position=\"relative\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n boxSize=\"24\"\n flexShrink={0}\n >\n {hasStatus && statusColor && (\n <chakra.span\n position=\"absolute\"\n inset={0}\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n pointerEvents=\"none\"\n transition=\"opacity 0.1s\"\n _groupHover={closable ? { opacity: 0 } : undefined}\n _groupFocusWithin={closable ? { opacity: 0 } : undefined}\n >\n <chakra.span boxSize=\"8\" borderRadius=\"full\" backgroundColor={STATUS_COLOR_TOKEN[statusColor]} />\n </chakra.span>\n )}\n {closable && (\n <chakra.span\n aria-hidden\n tabIndex={-1}\n onClick={handleCloseClick}\n css={closeButtonRecipe({ size: 'xs' })}\n position=\"absolute\"\n inset={0}\n color=\"icon/primary\"\n opacity={revealCloseOnInteraction ? 0 : 1}\n pointerEvents={revealCloseOnInteraction ? 'none' : 'auto'}\n transition=\"opacity 0.1s\"\n _groupHover={revealCloseOnInteraction ? revealOnInteraction : undefined}\n _groupFocusWithin={revealCloseOnInteraction ? revealOnInteraction : undefined}\n >\n <IconCross size=\"16\" />\n </chakra.span>\n )}\n </chakra.span>\n ) : null;\n\n return (\n <Tabs.Trigger\n ref={ref}\n {...rest}\n className={mergedClassName}\n gap={isCanvas ? '4' : showTrailing ? '12' : undefined}\n onKeyDown={handleTriggerKeyDown}\n paddingInlineEnd={isCanvas ? (showTrailing ? '8' : undefined) : showTrailing ? '12' : undefined}\n paddingInlineStart={isCanvas && Icon ? '8' : undefined}\n >\n {isCanvas ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {Icon && <Icon size=\"16\" />}\n {children}\n </Box>\n {trailing}\n </>\n ) : variant === 'line' ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {Icon && <Icon size=\"24\" />}\n {children}\n {badge}\n </Box>\n {trailing}\n </>\n ) : (\n <>\n <Box display=\"flex\" gap=\"16\">\n {children}\n {Icon && <Icon size=\"24\" />}\n </Box>\n {!!secondaryText && (\n <Text as=\"span\" textStyle=\"body/md/regular\" color={rest.disabled ? 'text/on-disabled' : 'text/secondary'}>\n {secondaryText}\n </Text>\n )}\n {trailing}\n </>\n )}\n </Tabs.Trigger>\n );\n});\n\nTrigger.displayName = 'BitkitTabs.Trigger';\n\ntype TabItem = ReactElement<TriggerProps>;\n\n// Flattens children to a list of elements, unwrapping fragments so a Trigger grouped inside a\n// fragment (e.g. conditional rendering) isn't silently dropped.\nconst flattenChildren = (nodes: ReactNode): ReactElement[] =>\n Children.toArray(nodes).flatMap((child) => {\n if (!isValidElement(child)) return [];\n if (child.type === Fragment) return flattenChildren((child.props as { children?: ReactNode }).children);\n return [child];\n });\n\n// Returns the set of visible tab indices for the overflow, always keeping the selected tab\n// visible: if it would overflow, it takes the last visible slot.\nconst getVisibleIndices = (total: number, count: number, selectedIndex: number) => {\n const visible = new Set<number>();\n for (let i = 0; i < count; i += 1) visible.add(i);\n if (selectedIndex >= 0 && selectedIndex < total && !visible.has(selectedIndex)) {\n visible.delete(count - 1);\n visible.add(selectedIndex);\n }\n return visible;\n};\n\nexport type BitkitTabsAddButtonProps = Omit<\n Extract<BitkitControlButtonProps, { href?: undefined }>,\n 'icon' | 'label' | 'size'\n> & {\n /** Accessible label / tooltip for the add button. */\n label?: string;\n};\n\n// \"+\" button to add a tab. Place it as a child of `List`; it always renders at the end of the row\n// (after the overflow menu). A compact `xxs` control button (16px icon, 4px padding) per the design.\nconst AddButton = forwardRef<HTMLButtonElement, BitkitTabsAddButtonProps>((props, ref) => {\n const { label = 'Add tab', ...rest } = props;\n return <BitkitControlButton ref={ref} icon={IconPlus} label={label} size=\"xxs\" {...rest} />;\n});\n\nAddButton.displayName = 'BitkitTabs.AddButton';\n\n// Overflow-aware List (all variants): measures the tabs and collapses the overflowing ones into a\n// \"N more…\" menu. The more-button look comes from the variant's `moreTrigger` recipe slot.\nconst List = forwardRef<HTMLDivElement, Tabs.ListProps>((props, ref) => {\n // Semantic/labelling attributes belong on the actual tablist, not the visual wrapper.\n const { children, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...rest } = props;\n const { variant = 'line' } = useRootPropsContext();\n const api = useTabsContext();\n const styles = useTabsStyles();\n\n // Split children: tab triggers participate in overflow; the optional add button always renders last.\n const allChildren = flattenChildren(children);\n const addButton = allChildren.find((child) => child.type === AddButton) ?? null;\n // Only Trigger elements are measured/overflowed; anything else (fragments, stray nodes) is ignored.\n const items = allChildren.filter((child) => child.type === Trigger) as TabItem[];\n // Re-measure whenever the tab set OR any width-affecting input changes — the variant, each tab's\n // value/label, icon/status/close/disabled state, and badge/secondaryText content (best-effort\n // string form, so width-changing text like \"3 errors\" → \"12 errors\" invalidates the cache).\n const measureKey =\n variant +\n items\n .map((item) => {\n const p = item.props;\n return [\n p.value,\n String(p.children),\n p.icon ? 'i' : '',\n p.badge ? String(p.badge) : '',\n p.secondaryText ? String(p.secondaryText) : '',\n p.statusColor ?? '',\n p.onClose ? 'c' : '',\n p.disabled ? 'd' : '',\n ].join(':');\n })\n .join('|');\n const selectedIndex = items.findIndex((item) => item.props.value === api.value);\n // Conservative width reservations: the more button (wider for the lg/semibold variants) and the\n // always-visible add button.\n const moreReserve = variant === 'canvas' ? 100 : 140;\n const addReserve = addButton ? 48 : 0;\n\n const listRef = useRef<HTMLDivElement | null>(null);\n const widthsRef = useRef<number[]>([]);\n // Horizontal padding of the list, cached during the measure pass so the resize hot path doesn't\n // call getComputedStyle on every tick.\n const paddingRef = useRef(0);\n // `measuredKey` is state (not a ref) so finishing the measure pass always re-renders and flips\n // `measuring` off — even when recompute keeps the same visibleCount (which would skip a re-render).\n const [measuredKey, setMeasuredKey] = useState<string | null>(null);\n const [visibleCount, setVisibleCount] = useState(items.length);\n\n const setListRef = useMemo(() => mergeRefs(listRef, ref), [ref]);\n\n const recompute = useCallback(() => {\n const el = listRef.current;\n const widths = widthsRef.current;\n if (!el || !widths.length) return;\n const available = el.clientWidth - paddingRef.current - addReserve;\n const totalWidth = widths.reduce((sum, w) => sum + w, 0);\n if (totalWidth <= available) {\n setVisibleCount(widths.length);\n return;\n }\n let used = moreReserve;\n let count = 0;\n for (let i = 0; i < widths.length; i += 1) {\n if (used + widths[i] > available) break;\n used += widths[i];\n count += 1;\n }\n setVisibleCount(Math.max(count, 1));\n }, [addReserve, moreReserve]);\n\n // Measure once per width-affecting change (all tabs are rendered visible during this pass).\n // Isomorphic layout effect: avoids the SSR \"useLayoutEffect does nothing on the server\" warning.\n const measuring = measuredKey !== measureKey;\n useSafeLayoutEffect(() => {\n const el = listRef.current;\n if (!el) return;\n if (measuredKey !== measureKey) {\n const tabEls = el.querySelectorAll<HTMLElement>('[data-tab-trigger]');\n widthsRef.current = Array.from(tabEls, (node) => node.offsetWidth);\n const cs = getComputedStyle(el);\n paddingRef.current = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);\n setMeasuredKey(measureKey);\n }\n recompute();\n }, [measureKey, measuredKey, recompute]);\n\n useEffect(() => {\n const el = listRef.current;\n if (!el || typeof ResizeObserver === 'undefined') return;\n // Coalesce resize bursts into one recompute per frame to avoid a read/write layout storm.\n let raf = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(raf);\n raf = requestAnimationFrame(recompute);\n });\n observer.observe(el);\n return () => {\n cancelAnimationFrame(raf);\n observer.disconnect();\n };\n }, [recompute]);\n\n const showAll = measuring || visibleCount >= items.length;\n const visible = showAll ? null : getVisibleIndices(items.length, visibleCount, selectedIndex);\n const hiddenItems = visible ? items.filter((_, i) => !visible.has(i)) : [];\n\n return (\n // The visual row carries the list styling (background, baseline border, leading padding) and is\n // measured for overflow. The tablist itself only wraps the tabs (`display: contents` keeps the\n // flex layout intact), so the more/add controls stay outside `role=\"tablist\"` per the ARIA spec.\n <Box ref={setListRef} css={styles.list} {...rest}>\n <Tabs.List display=\"contents\" id={id} aria-label={ariaLabel} aria-labelledby={ariaLabelledby}>\n {items.map((item, i) =>\n cloneElement(item, {\n key: item.props.value,\n 'data-tab-trigger': '',\n hidden: visible ? !visible.has(i) : undefined,\n } as Partial<TriggerProps>),\n )}\n </Tabs.List>\n {hiddenItems.length > 0 && (\n <Menu.Root size=\"md\" positioning={{ gutter: 0, shift: -4 }}>\n <Menu.Trigger asChild>\n <chakra.button type=\"button\" css={styles.moreTrigger}>\n {hiddenItems.length} more…\n </chakra.button>\n </Menu.Trigger>\n <Portal>\n <Menu.Positioner>\n <Menu.Content>\n {hiddenItems.map((item) => (\n <Menu.Item\n key={item.props.value}\n value={item.props.value}\n disabled={item.props.disabled}\n onSelect={() => api.setValue(item.props.value)}\n alignItems=\"center\"\n >\n <Text flex=\"1\" color={item.props.disabled ? 'text/on-disabled' : 'inherit'}>\n {item.props.children}\n </Text>\n {variant === 'canvas' && item.props.statusColor && (\n <chakra.span\n boxSize=\"8\"\n borderRadius=\"full\"\n flexShrink={0}\n backgroundColor={STATUS_COLOR_TOKEN[item.props.statusColor]}\n />\n )}\n </Menu.Item>\n ))}\n </Menu.Content>\n </Menu.Positioner>\n </Portal>\n </Menu.Root>\n )}\n {addButton && (\n <Box display=\"flex\" alignItems=\"center\" flexShrink={0} paddingInline=\"8\">\n {addButton}\n </Box>\n )}\n </Box>\n );\n});\n\nList.displayName = 'BitkitTabs.List';\n\nconst BitkitTabs: Omit<typeof Tabs, 'Root' | 'Trigger' | 'List'> & {\n AddButton: typeof AddButton;\n List: typeof List;\n Root: typeof Root;\n Trigger: typeof Trigger;\n} = {\n ...Tabs,\n AddButton,\n List,\n Root,\n Trigger,\n};\n\nBitkitTabs.Content.displayName = 'BitkitTabs.Content';\nBitkitTabs.ContentGroup.displayName = 'BitkitTabs.ContentGroup';\nBitkitTabs.Root.displayName = 'BitkitTabs.Root';\n\nexport default BitkitTabs;\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitTabs.js","names":[],"sources":["../../../lib/components/BitkitTabs/BitkitTabs.tsx"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { createContext, mergeRefs, useSafeLayoutEffect } from '@chakra-ui/react';\nimport { Box } from '@chakra-ui/react/box';\nimport { Menu } from '@chakra-ui/react/menu';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { chakra, useRecipe } from '@chakra-ui/react/styled-system';\nimport { Tabs, useTabsContext, useTabsStyles } from '@chakra-ui/react/tabs';\nimport { Text } from '@chakra-ui/react/text';\nimport {\n Children,\n cloneElement,\n forwardRef,\n Fragment,\n isValidElement,\n type KeyboardEvent,\n type MouseEvent,\n type ReactElement,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { type BitkitIconComponent, IconCross, IconPlus } from '../../icons';\nimport BitkitControlButton, { type BitkitControlButtonProps } from '../BitkitControlButton/BitkitControlButton';\n\n/**\n * Root props. `onValueChange` is re-declared with a concrete `{ value }` arg so the callback\n * parameter is inferred at the call site: the base `Tabs.RootProps` intersection breaks\n * contextual typing, which otherwise leaves consumers' `({ value }) => …` flagged as an\n * implicit `any` under `noImplicitAny`.\n */\nexport type BitkitTabsRootProps = Omit<Tabs.RootProps, 'onValueChange'> & {\n onValueChange?: (details: { value: string }) => void;\n};\n\nexport const [RootPropsProvider, useRootPropsContext] = createContext<BitkitTabsRootProps>({\n name: 'RootPropsContext',\n hookName: 'useRootPropsContext',\n providerName: '<RootProps />',\n});\n\nconst Root = forwardRef<HTMLDivElement, BitkitTabsRootProps>((props, ref) => {\n return (\n <RootPropsProvider value={props}>\n <Tabs.Root ref={ref} {...props} />\n </RootPropsProvider>\n );\n});\n\n/** Status-dot colors for the `canvas` variant. */\nexport type BitkitTabsStatusColor = 'neutral' | 'purple' | 'red' | 'yellow';\n\nconst STATUS_COLOR_TOKEN: Record<BitkitTabsStatusColor, string> = {\n neutral: 'color/neutral/muted',\n purple: 'interactive/bold',\n red: 'color/red/base',\n yellow: 'color/yellow/bold',\n};\n\ntype TriggerProps = Tabs.TriggerProps & {\n badge?: ReactNode;\n children?: string;\n icon?: BitkitIconComponent;\n /** `canvas` variant only — when provided, a close button reveals on hover and calls this handler when clicked. */\n onClose?: () => void;\n secondaryText?: ReactNode;\n /** `canvas` variant only — shows a status indicator dot. When hovered (and `onClose` is set), the close button replaces it. */\n statusColor?: BitkitTabsStatusColor;\n};\n\nRoot.displayName = 'BitkitTabs.Root';\n\nconst Trigger = forwardRef<HTMLButtonElement, TriggerProps>((props, ref) => {\n const { badge, children, className, icon: Icon, onClose, onKeyDown, secondaryText, statusColor, ...rest } = props;\n // Mirror the recipe's defaultVariant so an unset `variant` renders the line layout (not the\n // contained one) — otherwise an undefined variant would fall through to the contained branch.\n const { variant = 'line' } = useRootPropsContext();\n const closeButtonRecipe = useRecipe({ key: 'closeButton' });\n\n // Close button and status dot are exclusive to the canvas variant.\n const isCanvas = variant === 'canvas';\n const closable = isCanvas && !!onClose && !rest.disabled;\n const hasStatus = isCanvas && !!statusColor && !rest.disabled;\n const showTrailing = closable || hasStatus;\n const mergedClassName = showTrailing ? (className ? `group ${className}` : 'group') : className;\n // The close button is always visible when there is no status dot; when the dot is shown it is\n // revealed on hover/focus instead (replacing the dot).\n const revealCloseOnInteraction = closable && hasStatus;\n const revealOnInteraction = { opacity: 1, pointerEvents: 'auto' } as const;\n\n const handleCloseClick = (event: MouseEvent<HTMLSpanElement>) => {\n event.stopPropagation();\n event.preventDefault();\n onClose?.();\n };\n\n // The close control is not in the tab order; keyboard users close the focused tab with Delete/Backspace.\n const handleTriggerKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (closable && (event.key === 'Delete' || event.key === 'Backspace')) {\n event.preventDefault();\n // Move focus to a sibling tab before removal so keyboard navigation can continue.\n const trigger = event.currentTarget;\n // Skip overflowed (`hidden`) tabs — they aren't focusable.\n const triggers = [\n ...(trigger.closest('[role=\"tablist\"]')?.querySelectorAll<HTMLElement>('[role=\"tab\"]:not([hidden])') ?? []),\n ];\n const index = triggers.indexOf(trigger);\n (triggers[index + 1] ?? triggers[index - 1])?.focus();\n onClose?.();\n }\n onKeyDown?.(event);\n };\n\n const trailing = showTrailing ? (\n <chakra.span\n position=\"relative\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n boxSize=\"24\"\n flexShrink={0}\n >\n {hasStatus && statusColor && (\n <chakra.span\n position=\"absolute\"\n inset={0}\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n pointerEvents=\"none\"\n transition=\"opacity 0.1s\"\n _groupHover={closable ? { opacity: 0 } : undefined}\n _groupFocusWithin={closable ? { opacity: 0 } : undefined}\n >\n <chakra.span boxSize=\"8\" borderRadius=\"full\" backgroundColor={STATUS_COLOR_TOKEN[statusColor]} />\n </chakra.span>\n )}\n {closable && (\n <chakra.span\n aria-hidden\n tabIndex={-1}\n onClick={handleCloseClick}\n css={closeButtonRecipe({ size: 'xs' })}\n position=\"absolute\"\n inset={0}\n color=\"icon/primary\"\n opacity={revealCloseOnInteraction ? 0 : 1}\n pointerEvents={revealCloseOnInteraction ? 'none' : 'auto'}\n transition=\"opacity 0.1s\"\n _groupHover={revealCloseOnInteraction ? revealOnInteraction : undefined}\n _groupFocusWithin={revealCloseOnInteraction ? revealOnInteraction : undefined}\n >\n <IconCross size=\"16\" />\n </chakra.span>\n )}\n </chakra.span>\n ) : null;\n\n return (\n <Tabs.Trigger\n ref={ref}\n {...rest}\n className={mergedClassName}\n gap={isCanvas ? '4' : showTrailing ? '12' : undefined}\n onKeyDown={handleTriggerKeyDown}\n paddingInlineEnd={isCanvas ? (showTrailing ? '8' : undefined) : showTrailing ? '12' : undefined}\n paddingInlineStart={isCanvas && Icon ? '8' : undefined}\n >\n {isCanvas ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {Icon && <Icon size=\"16\" />}\n {children}\n </Box>\n {trailing}\n </>\n ) : variant === 'line' ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {Icon && <Icon size=\"24\" />}\n {children}\n {badge}\n </Box>\n {trailing}\n </>\n ) : (\n <>\n <Box display=\"flex\" gap=\"16\">\n {children}\n {badge}\n {Icon && <Icon size=\"24\" />}\n </Box>\n {!!secondaryText && (\n <Text as=\"span\" textStyle=\"body/md/regular\" color={rest.disabled ? 'text/on-disabled' : 'text/secondary'}>\n {secondaryText}\n </Text>\n )}\n {trailing}\n </>\n )}\n </Tabs.Trigger>\n );\n});\n\nTrigger.displayName = 'BitkitTabs.Trigger';\n\ntype TabItem = ReactElement<TriggerProps>;\n\n// Flattens children to a list of elements, unwrapping fragments so a Trigger grouped inside a\n// fragment (e.g. conditional rendering) isn't silently dropped.\nconst flattenChildren = (nodes: ReactNode): ReactElement[] =>\n Children.toArray(nodes).flatMap((child) => {\n if (!isValidElement(child)) return [];\n if (child.type === Fragment) return flattenChildren((child.props as { children?: ReactNode }).children);\n return [child];\n });\n\n// Returns the set of visible tab indices for the overflow, always keeping the selected tab\n// visible: if it would overflow, it takes the last visible slot.\nconst getVisibleIndices = (total: number, count: number, selectedIndex: number) => {\n const visible = new Set<number>();\n for (let i = 0; i < count; i += 1) visible.add(i);\n if (selectedIndex >= 0 && selectedIndex < total && !visible.has(selectedIndex)) {\n visible.delete(count - 1);\n visible.add(selectedIndex);\n }\n return visible;\n};\n\nexport type BitkitTabsAddButtonProps = Omit<\n Extract<BitkitControlButtonProps, { href?: undefined }>,\n 'icon' | 'label' | 'size'\n> & {\n /** Accessible label / tooltip for the add button. */\n label?: string;\n};\n\n// \"+\" button to add a tab. Place it as a child of `List`; it always renders at the end of the row\n// (after the overflow menu). A compact `xxs` control button (16px icon, 4px padding) per the design.\nconst AddButton = forwardRef<HTMLButtonElement, BitkitTabsAddButtonProps>((props, ref) => {\n const { label = 'Add tab', ...rest } = props;\n return <BitkitControlButton ref={ref} icon={IconPlus} label={label} size=\"xxs\" {...rest} />;\n});\n\nAddButton.displayName = 'BitkitTabs.AddButton';\n\n// Overflow-aware List (all variants): measures the tabs and collapses the overflowing ones into a\n// \"N more…\" menu. The more-button look comes from the variant's `moreTrigger` recipe slot.\nconst List = forwardRef<HTMLDivElement, Tabs.ListProps>((props, ref) => {\n // Semantic/labelling attributes belong on the actual tablist, not the visual wrapper.\n const { children, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...rest } = props;\n const { variant = 'line' } = useRootPropsContext();\n const api = useTabsContext();\n const styles = useTabsStyles();\n\n // Split children: tab triggers participate in overflow; the optional add button always renders last.\n const allChildren = flattenChildren(children);\n const addButton = allChildren.find((child) => child.type === AddButton) ?? null;\n // Only Trigger elements are measured/overflowed; anything else (fragments, stray nodes) is ignored.\n const items = allChildren.filter((child) => child.type === Trigger) as TabItem[];\n // Re-measure whenever the tab set OR any width-affecting input changes — the variant, each tab's\n // value/label, icon/status/close/disabled state, and badge/secondaryText content (best-effort\n // string form, so width-changing text like \"3 errors\" → \"12 errors\" invalidates the cache).\n const measureKey =\n variant +\n items\n .map((item) => {\n const p = item.props;\n return [\n p.value,\n String(p.children),\n p.icon ? 'i' : '',\n p.badge ? String(p.badge) : '',\n p.secondaryText ? String(p.secondaryText) : '',\n p.statusColor ?? '',\n p.onClose ? 'c' : '',\n p.disabled ? 'd' : '',\n ].join(':');\n })\n .join('|');\n const selectedIndex = items.findIndex((item) => item.props.value === api.value);\n // Conservative width reservations: the more button (wider for the lg/semibold variants) and the\n // always-visible add button.\n const moreReserve = variant === 'canvas' ? 100 : 140;\n const addReserve = addButton ? 48 : 0;\n\n const listRef = useRef<HTMLDivElement | null>(null);\n const widthsRef = useRef<number[]>([]);\n // Horizontal padding of the list, cached during the measure pass so the resize hot path doesn't\n // call getComputedStyle on every tick.\n const paddingRef = useRef(0);\n // `measuredKey` is state (not a ref) so finishing the measure pass always re-renders and flips\n // `measuring` off — even when recompute keeps the same visibleCount (which would skip a re-render).\n const [measuredKey, setMeasuredKey] = useState<string | null>(null);\n const [visibleCount, setVisibleCount] = useState(items.length);\n\n const setListRef = useMemo(() => mergeRefs(listRef, ref), [ref]);\n\n const recompute = useCallback(() => {\n const el = listRef.current;\n const widths = widthsRef.current;\n if (!el || !widths.length) return;\n const available = el.clientWidth - paddingRef.current - addReserve;\n const totalWidth = widths.reduce((sum, w) => sum + w, 0);\n if (totalWidth <= available) {\n setVisibleCount(widths.length);\n return;\n }\n let used = moreReserve;\n let count = 0;\n for (let i = 0; i < widths.length; i += 1) {\n if (used + widths[i] > available) break;\n used += widths[i];\n count += 1;\n }\n setVisibleCount(Math.max(count, 1));\n }, [addReserve, moreReserve]);\n\n // Measure once per width-affecting change (all tabs are rendered visible during this pass).\n // Isomorphic layout effect: avoids the SSR \"useLayoutEffect does nothing on the server\" warning.\n const measuring = measuredKey !== measureKey;\n useSafeLayoutEffect(() => {\n const el = listRef.current;\n if (!el) return;\n if (measuredKey !== measureKey) {\n const tabEls = el.querySelectorAll<HTMLElement>('[data-tab-trigger]');\n widthsRef.current = Array.from(tabEls, (node) => node.offsetWidth);\n const cs = getComputedStyle(el);\n paddingRef.current = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);\n setMeasuredKey(measureKey);\n }\n recompute();\n }, [measureKey, measuredKey, recompute]);\n\n useEffect(() => {\n const el = listRef.current;\n if (!el || typeof ResizeObserver === 'undefined') return;\n // Coalesce resize bursts into one recompute per frame to avoid a read/write layout storm.\n let raf = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(raf);\n raf = requestAnimationFrame(recompute);\n });\n observer.observe(el);\n return () => {\n cancelAnimationFrame(raf);\n observer.disconnect();\n };\n }, [recompute]);\n\n const showAll = measuring || visibleCount >= items.length;\n const visible = showAll ? null : getVisibleIndices(items.length, visibleCount, selectedIndex);\n const hiddenItems = visible ? items.filter((_, i) => !visible.has(i)) : [];\n\n return (\n // The visual row carries the list styling (background, baseline border, leading padding) and is\n // measured for overflow. The tablist itself only wraps the tabs (`display: contents` keeps the\n // flex layout intact), so the more/add controls stay outside `role=\"tablist\"` per the ARIA spec.\n <Box ref={setListRef} css={styles.list} {...rest}>\n <Tabs.List display=\"contents\" id={id} aria-label={ariaLabel} aria-labelledby={ariaLabelledby}>\n {items.map((item, i) =>\n cloneElement(item, {\n key: item.props.value,\n 'data-tab-trigger': '',\n hidden: visible ? !visible.has(i) : undefined,\n } as Partial<TriggerProps>),\n )}\n </Tabs.List>\n {hiddenItems.length > 0 && (\n <Menu.Root size=\"md\" positioning={{ gutter: 0, shift: -4 }}>\n <Menu.Trigger asChild>\n <chakra.button type=\"button\" css={styles.moreTrigger}>\n {hiddenItems.length} more…\n </chakra.button>\n </Menu.Trigger>\n <Portal>\n <Menu.Positioner>\n <Menu.Content>\n {hiddenItems.map((item) => (\n <Menu.Item\n key={item.props.value}\n value={item.props.value}\n disabled={item.props.disabled}\n onSelect={() => api.setValue(item.props.value)}\n alignItems=\"center\"\n >\n <Text flex=\"1\" color={item.props.disabled ? 'text/on-disabled' : 'inherit'}>\n {item.props.children}\n </Text>\n {variant === 'canvas' && item.props.statusColor && (\n <chakra.span\n boxSize=\"8\"\n borderRadius=\"full\"\n flexShrink={0}\n backgroundColor={STATUS_COLOR_TOKEN[item.props.statusColor]}\n />\n )}\n </Menu.Item>\n ))}\n </Menu.Content>\n </Menu.Positioner>\n </Portal>\n </Menu.Root>\n )}\n {addButton && (\n <Box display=\"flex\" alignItems=\"center\" flexShrink={0} paddingInline=\"8\">\n {addButton}\n </Box>\n )}\n </Box>\n );\n});\n\nList.displayName = 'BitkitTabs.List';\n\nconst BitkitTabs: Omit<typeof Tabs, 'Root' | 'Trigger' | 'List'> & {\n AddButton: typeof AddButton;\n List: typeof List;\n Root: typeof Root;\n Trigger: typeof Trigger;\n} = {\n ...Tabs,\n AddButton,\n List,\n Root,\n Trigger,\n};\n\nBitkitTabs.Content.displayName = 'BitkitTabs.Content';\nBitkitTabs.ContentGroup.displayName = 'BitkitTabs.ContentGroup';\nBitkitTabs.Root.displayName = 'BitkitTabs.Root';\n\nexport default BitkitTabs;\n"],"mappings":"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"}
@@ -56,6 +56,20 @@ declare const progressSlotRecipe: import('@chakra-ui/react').SlotRecipeDefinitio
56
56
  };
57
57
  };
58
58
  };
59
+ striped: {
60
+ false: {};
61
+ true: {
62
+ range: {
63
+ '--animate-from': "45px";
64
+ animation: "bg-position 1.5s linear infinite";
65
+ backgroundImage: string;
66
+ backgroundSize: "45px 45px";
67
+ '@media (prefers-reduced-motion: reduce)': {
68
+ animation: "none";
69
+ };
70
+ };
71
+ };
72
+ };
59
73
  trackColor: {
60
74
  neutral: {
61
75
  track: {
@@ -1,6 +1,9 @@
1
1
  import { defineSlotRecipe } from "@chakra-ui/react/styled-system";
2
2
  import { progressAnatomy } from "@chakra-ui/react/anatomy";
3
3
  //#region lib/theme/slot-recipes/Progress.recipe.ts
4
+ var STRIPE_TILE_PX = 45;
5
+ var STRIPE_COLOR = "var(--colors-color\\/white\\/30)";
6
+ var STRIPE_GRADIENT = `linear-gradient(45deg, ${STRIPE_COLOR} 25%, transparent 25%, transparent 50%, ${STRIPE_COLOR} 50%, ${STRIPE_COLOR} 75%, transparent 75%, transparent)`;
4
7
  var progressSlotRecipe = defineSlotRecipe({
5
8
  className: "progress",
6
9
  slots: progressAnatomy.keys(),
@@ -44,6 +47,16 @@ var progressSlotRecipe = defineSlotRecipe({
44
47
  "12": { track: { height: "12" } },
45
48
  "16": { track: { height: "16" } }
46
49
  },
50
+ striped: {
51
+ false: {},
52
+ true: { range: {
53
+ "--animate-from": `${STRIPE_TILE_PX}px`,
54
+ animation: "bg-position 1.5s linear infinite",
55
+ backgroundImage: STRIPE_GRADIENT,
56
+ backgroundSize: `${STRIPE_TILE_PX}px ${STRIPE_TILE_PX}px`,
57
+ "@media (prefers-reduced-motion: reduce)": { animation: "none" }
58
+ } }
59
+ },
47
60
  trackColor: {
48
61
  neutral: { track: { background: "color/neutral/moderate" } },
49
62
  transparent: { track: { background: "color/transparent" } }
@@ -75,6 +88,7 @@ var progressSlotRecipe = defineSlotRecipe({
75
88
  defaultVariants: {
76
89
  roundedEdges: "both",
77
90
  size: "8",
91
+ striped: false,
78
92
  trackColor: "neutral"
79
93
  }
80
94
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Progress.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Progress.recipe.ts"],"sourcesContent":["import { progressAnatomy } from '@chakra-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nconst progressSlotRecipe = defineSlotRecipe({\n className: 'progress',\n slots: progressAnatomy.keys(),\n base: {\n // The radius and the clipping live on the root so a consumer-supplied\n // `borderRadius` style prop lands on the element that actually rounds.\n root: {\n overflow: 'hidden',\n width: 'full',\n },\n track: {\n position: 'relative',\n width: 'full',\n },\n range: {\n height: 'full',\n // Keeps a sliver of the range visible at very low values (Figma: min-width 4px)…\n minWidth: '4',\n // …but exactly 0 has to render nothing. A coloured stub at 0% reads as progress\n // that hasn't happened yet — wrong on a usage meter sitting next to \"0 / 3,000\".\n '[data-scope=\"progress\"][data-part=\"root\"][data-value=\"0\"] &': {\n minWidth: 0,\n },\n transitionDuration: 'moderate',\n transitionProperty: 'width',\n transitionTimingFunction: 'ease-out',\n },\n },\n variants: {\n roundedEdges: {\n both: {\n root: { borderRadius: '2' },\n },\n end: {\n root: { borderEndEndRadius: '2', borderStartEndRadius: '2' },\n },\n none: {},\n start: {\n root: { borderEndStartRadius: '2', borderStartStartRadius: '2' },\n },\n },\n size: {\n '1': {\n track: { height: '1' },\n },\n '2': {\n track: { height: '2' },\n },\n '4': {\n track: { height: '4' },\n },\n '6': {\n track: { height: '6' },\n },\n '8': {\n track: { height: '8' },\n },\n '12': {\n track: { height: '12' },\n },\n '16': {\n track: { height: '16' },\n },\n },\n trackColor: {\n neutral: {\n track: { background: 'color/neutral/moderate' },\n },\n transparent: {\n track: { background: 'color/transparent' },\n },\n },\n },\n // With a neutral track the range's trailing edge stays square — the track behind it\n // carries the shape, and the root clips the leading edge. With a transparent track\n // the range *is* the visible bar, so it has to reproduce the root's rounding itself.\n compoundVariants: [\n {\n roundedEdges: 'both',\n trackColor: 'transparent',\n css: { range: { borderRadius: '2' } },\n },\n {\n roundedEdges: 'start',\n trackColor: 'transparent',\n css: { range: { borderEndStartRadius: '2', borderStartStartRadius: '2' } },\n },\n {\n roundedEdges: 'end',\n trackColor: 'transparent',\n css: { range: { borderEndEndRadius: '2', borderStartEndRadius: '2' } },\n },\n ],\n defaultVariants: {\n roundedEdges: 'both',\n size: '8',\n trackColor: 'neutral',\n },\n});\n\nexport default progressSlotRecipe;\n"],"mappings":";;;AAGA,IAAM,qBAAqB,iBAAiB;CAC1C,WAAW;CACX,OAAO,gBAAgB,KAAK;CAC5B,MAAM;EAGJ,MAAM;GACJ,UAAU;GACV,OAAO;EACT;EACA,OAAO;GACL,UAAU;GACV,OAAO;EACT;EACA,OAAO;GACL,QAAQ;GAER,UAAU;GAGV,qEAA+D,EAC7D,UAAU,EACZ;GACA,oBAAoB;GACpB,oBAAoB;GACpB,0BAA0B;EAC5B;CACF;CACA,UAAU;EACR,cAAc;GACZ,MAAM,EACJ,MAAM,EAAE,cAAc,IAAI,EAC5B;GACA,KAAK,EACH,MAAM;IAAE,oBAAoB;IAAK,sBAAsB;GAAI,EAC7D;GACA,MAAM,CAAC;GACP,OAAO,EACL,MAAM;IAAE,sBAAsB;IAAK,wBAAwB;GAAI,EACjE;EACF;EACA,MAAM;GACJ,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,MAAM,EACJ,OAAO,EAAE,QAAQ,KAAK,EACxB;GACA,MAAM,EACJ,OAAO,EAAE,QAAQ,KAAK,EACxB;EACF;EACA,YAAY;GACV,SAAS,EACP,OAAO,EAAE,YAAY,yBAAyB,EAChD;GACA,aAAa,EACX,OAAO,EAAE,YAAY,oBAAoB,EAC3C;EACF;CACF;CAIA,kBAAkB;EAChB;GACE,cAAc;GACd,YAAY;GACZ,KAAK,EAAE,OAAO,EAAE,cAAc,IAAI,EAAE;EACtC;EACA;GACE,cAAc;GACd,YAAY;GACZ,KAAK,EAAE,OAAO;IAAE,sBAAsB;IAAK,wBAAwB;GAAI,EAAE;EAC3E;EACA;GACE,cAAc;GACd,YAAY;GACZ,KAAK,EAAE,OAAO;IAAE,oBAAoB;IAAK,sBAAsB;GAAI,EAAE;EACvE;CACF;CACA,iBAAiB;EACf,cAAc;EACd,MAAM;EACN,YAAY;CACd;AACF,CAAC"}
1
+ {"version":3,"file":"Progress.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Progress.recipe.ts"],"sourcesContent":["import { progressAnatomy } from '@chakra-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nconst STRIPE_TILE_PX = 45;\n\n// A raw, slash-escaped CSS variable for `color/white/30`: inside a `{colors.…}` reference\n// Chakra reads the first `/` as its color-mix opacity modifier.\nconst STRIPE_COLOR = 'var(--colors-color\\\\/white\\\\/30)';\n\nconst STRIPE_GRADIENT =\n `linear-gradient(45deg, ${STRIPE_COLOR} 25%, transparent 25%, transparent 50%, ` +\n `${STRIPE_COLOR} 50%, ${STRIPE_COLOR} 75%, transparent 75%, transparent)`;\n\nconst progressSlotRecipe = defineSlotRecipe({\n className: 'progress',\n slots: progressAnatomy.keys(),\n base: {\n // The radius and the clipping live on the root so a consumer-supplied\n // `borderRadius` style prop lands on the element that actually rounds.\n root: {\n overflow: 'hidden',\n width: 'full',\n },\n track: {\n position: 'relative',\n width: 'full',\n },\n range: {\n height: 'full',\n // Keeps a sliver of the range visible at very low values (Figma: min-width 4px)…\n minWidth: '4',\n // …but exactly 0 has to render nothing. A coloured stub at 0% reads as progress\n // that hasn't happened yet — wrong on a usage meter sitting next to \"0 / 3,000\".\n '[data-scope=\"progress\"][data-part=\"root\"][data-value=\"0\"] &': {\n minWidth: 0,\n },\n transitionDuration: 'moderate',\n transitionProperty: 'width',\n transitionTimingFunction: 'ease-out',\n },\n },\n variants: {\n roundedEdges: {\n both: {\n root: { borderRadius: '2' },\n },\n end: {\n root: { borderEndEndRadius: '2', borderStartEndRadius: '2' },\n },\n none: {},\n start: {\n root: { borderEndStartRadius: '2', borderStartStartRadius: '2' },\n },\n },\n size: {\n '1': {\n track: { height: '1' },\n },\n '2': {\n track: { height: '2' },\n },\n '4': {\n track: { height: '4' },\n },\n '6': {\n track: { height: '6' },\n },\n '8': {\n track: { height: '8' },\n },\n '12': {\n track: { height: '12' },\n },\n '16': {\n track: { height: '16' },\n },\n },\n striped: {\n false: {},\n true: {\n range: {\n '--animate-from': `${STRIPE_TILE_PX}px`,\n animation: 'bg-position 1.5s linear infinite',\n backgroundImage: STRIPE_GRADIENT,\n backgroundSize: `${STRIPE_TILE_PX}px ${STRIPE_TILE_PX}px`,\n '@media (prefers-reduced-motion: reduce)': {\n animation: 'none',\n },\n },\n },\n },\n trackColor: {\n neutral: {\n track: { background: 'color/neutral/moderate' },\n },\n transparent: {\n track: { background: 'color/transparent' },\n },\n },\n },\n // With a neutral track the range's trailing edge stays square — the track behind it\n // carries the shape, and the root clips the leading edge. With a transparent track\n // the range *is* the visible bar, so it has to reproduce the root's rounding itself.\n compoundVariants: [\n {\n roundedEdges: 'both',\n trackColor: 'transparent',\n css: { range: { borderRadius: '2' } },\n },\n {\n roundedEdges: 'start',\n trackColor: 'transparent',\n css: { range: { borderEndStartRadius: '2', borderStartStartRadius: '2' } },\n },\n {\n roundedEdges: 'end',\n trackColor: 'transparent',\n css: { range: { borderEndEndRadius: '2', borderStartEndRadius: '2' } },\n },\n ],\n defaultVariants: {\n roundedEdges: 'both',\n size: '8',\n striped: false,\n trackColor: 'neutral',\n },\n});\n\nexport default progressSlotRecipe;\n"],"mappings":";;;AAGA,IAAM,iBAAiB;AAIvB,IAAM,eAAe;AAErB,IAAM,kBACJ,0BAA0B,aAAa,0CACpC,aAAa,QAAQ,aAAa;AAEvC,IAAM,qBAAqB,iBAAiB;CAC1C,WAAW;CACX,OAAO,gBAAgB,KAAK;CAC5B,MAAM;EAGJ,MAAM;GACJ,UAAU;GACV,OAAO;EACT;EACA,OAAO;GACL,UAAU;GACV,OAAO;EACT;EACA,OAAO;GACL,QAAQ;GAER,UAAU;GAGV,qEAA+D,EAC7D,UAAU,EACZ;GACA,oBAAoB;GACpB,oBAAoB;GACpB,0BAA0B;EAC5B;CACF;CACA,UAAU;EACR,cAAc;GACZ,MAAM,EACJ,MAAM,EAAE,cAAc,IAAI,EAC5B;GACA,KAAK,EACH,MAAM;IAAE,oBAAoB;IAAK,sBAAsB;GAAI,EAC7D;GACA,MAAM,CAAC;GACP,OAAO,EACL,MAAM;IAAE,sBAAsB;IAAK,wBAAwB;GAAI,EACjE;EACF;EACA,MAAM;GACJ,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,KAAK,EACH,OAAO,EAAE,QAAQ,IAAI,EACvB;GACA,MAAM,EACJ,OAAO,EAAE,QAAQ,KAAK,EACxB;GACA,MAAM,EACJ,OAAO,EAAE,QAAQ,KAAK,EACxB;EACF;EACA,SAAS;GACP,OAAO,CAAC;GACR,MAAM,EACJ,OAAO;IACL,kBAAkB,GAAG,eAAe;IACpC,WAAW;IACX,iBAAiB;IACjB,gBAAgB,GAAG,eAAe,KAAK,eAAe;IACtD,2CAA2C,EACzC,WAAW,OACb;GACF,EACF;EACF;EACA,YAAY;GACV,SAAS,EACP,OAAO,EAAE,YAAY,yBAAyB,EAChD;GACA,aAAa,EACX,OAAO,EAAE,YAAY,oBAAoB,EAC3C;EACF;CACF;CAIA,kBAAkB;EAChB;GACE,cAAc;GACd,YAAY;GACZ,KAAK,EAAE,OAAO,EAAE,cAAc,IAAI,EAAE;EACtC;EACA;GACE,cAAc;GACd,YAAY;GACZ,KAAK,EAAE,OAAO;IAAE,sBAAsB;IAAK,wBAAwB;GAAI,EAAE;EAC3E;EACA;GACE,cAAc;GACd,YAAY;GACZ,KAAK,EAAE,OAAO;IAAE,oBAAoB;IAAK,sBAAsB;GAAI,EAAE;EACvE;CACF;CACA,iBAAiB;EACf,cAAc;EACd,MAAM;EACN,SAAS;EACT,YAAY;CACd;AACF,CAAC"}
@@ -1564,6 +1564,20 @@ declare const slotRecipes: {
1564
1564
  };
1565
1565
  };
1566
1566
  };
1567
+ striped: {
1568
+ false: {};
1569
+ true: {
1570
+ range: {
1571
+ '--animate-from': "45px";
1572
+ animation: "bg-position 1.5s linear infinite";
1573
+ backgroundImage: string;
1574
+ backgroundSize: "45px 45px";
1575
+ '@media (prefers-reduced-motion: reduce)': {
1576
+ animation: "none";
1577
+ };
1578
+ };
1579
+ };
1580
+ };
1567
1581
  trackColor: {
1568
1582
  neutral: {
1569
1583
  track: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit-v2",
3
3
  "private": false,
4
- "version": "0.3.345",
4
+ "version": "0.3.347",
5
5
  "description": "Bitrise Design System Components built with Chakra UI V3",
6
6
  "keywords": [
7
7
  "react",