@bitrise/bitkit-v2 0.3.498 → 0.3.500

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.
@@ -1,4 +1,5 @@
1
1
  import { AvatarRootProps } from '@chakra-ui/react/avatar';
2
+ import { ReactNode } from 'react';
2
3
  import { BitkitIconComponent } from '../../icons';
3
4
  export type BitkitAvatarProps = Omit<AvatarRootProps, 'variant' | 'backgroundColor' | 'background' | 'bg' | 'colorPalette'> & ({
4
5
  variant: 'image';
@@ -7,6 +8,8 @@ export type BitkitAvatarProps = Omit<AvatarRootProps, 'variant' | 'backgroundCol
7
8
  icon?: never;
8
9
  iconColor?: never;
9
10
  backgroundColor?: never;
11
+ /** Replaces the initials when there is no image to show. Rendered as decoration (`aria-hidden`): give it no accessible name of its own, the avatar's `name` is announced next to it. */
12
+ fallback?: ReactNode;
10
13
  } | {
11
14
  variant: 'image+icon';
12
15
  src: string;
@@ -14,6 +17,8 @@ export type BitkitAvatarProps = Omit<AvatarRootProps, 'variant' | 'backgroundCol
14
17
  icon: BitkitIconComponent;
15
18
  iconColor?: never;
16
19
  backgroundColor?: never;
20
+ /** Replaces the initials when there is no image to show. Rendered as decoration (`aria-hidden`): give it no accessible name of its own, the avatar's `name` is announced next to it. */
21
+ fallback?: ReactNode;
17
22
  } | {
18
23
  variant: 'icon';
19
24
  src?: never;
@@ -21,6 +26,7 @@ export type BitkitAvatarProps = Omit<AvatarRootProps, 'variant' | 'backgroundCol
21
26
  icon: BitkitIconComponent;
22
27
  iconColor?: AvatarRootProps['color'];
23
28
  backgroundColor?: AvatarRootProps['color'];
29
+ fallback?: never;
24
30
  } | {
25
31
  variant: 'letters';
26
32
  src?: never;
@@ -28,6 +34,7 @@ export type BitkitAvatarProps = Omit<AvatarRootProps, 'variant' | 'backgroundCol
28
34
  icon?: never;
29
35
  iconColor?: never;
30
36
  backgroundColor?: never;
37
+ fallback?: never;
31
38
  } | {
32
39
  variant: 'user';
33
40
  src: string;
@@ -35,6 +42,8 @@ export type BitkitAvatarProps = Omit<AvatarRootProps, 'variant' | 'backgroundCol
35
42
  icon?: never;
36
43
  iconColor?: never;
37
44
  backgroundColor?: never;
45
+ /** Replaces the initials when there is no image to show. Rendered as decoration (`aria-hidden`): give it no accessible name of its own, the avatar's `name` is announced next to it. */
46
+ fallback?: ReactNode;
38
47
  });
39
48
  declare const BitkitAvatar: import('react').ForwardRefExoticComponent<BitkitAvatarProps & import('react').RefAttributes<HTMLDivElement>>;
40
49
  export default BitkitAvatar;
@@ -16,7 +16,7 @@ var processFallbackName = (name) => {
16
16
  return str && str.length >= 2 ? `${str[0]} ${str[str.length - 1]}` : str;
17
17
  };
18
18
  var BitkitAvatar = forwardRef((props, ref) => {
19
- const { size, variant, src, name, icon: Icon, colorVariant = pickPalette(name), iconColor, backgroundColor, ...rest } = props;
19
+ const { size, variant, src, name, icon: Icon, colorVariant = pickPalette(name), iconColor, backgroundColor, fallback, ...rest } = props;
20
20
  const recipe = useSlotRecipe({ key: "avatar" });
21
21
  const styleVariant = variant === "user" && [
22
22
  "24",
@@ -37,7 +37,10 @@ var BitkitAvatar = forwardRef((props, ref) => {
37
37
  backgroundColor,
38
38
  ...rest,
39
39
  children: [
40
- !!fallbackName && /* @__PURE__ */ jsx(Avatar.Fallback, { name: fallbackName }),
40
+ fallback ? /* @__PURE__ */ jsx(Avatar.Fallback, {
41
+ "aria-hidden": true,
42
+ children: fallback
43
+ }) : !!fallbackName && /* @__PURE__ */ jsx(Avatar.Fallback, { name: fallbackName }),
41
44
  !!src && /* @__PURE__ */ jsx(Avatar.Image, { src }),
42
45
  !!Icon && variant === "icon" && /* @__PURE__ */ jsx(Icon, {
43
46
  css: styles.icon,
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitAvatar.js","names":[],"sources":["../../../lib/components/BitkitAvatar/BitkitAvatar.tsx"],"sourcesContent":["import { Avatar, type AvatarRootProps } from '@chakra-ui/react/avatar';\nimport { Box } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef } from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport avatarSlotRecipe from '../../theme/slot-recipes/Avatar.recipe';\n\nexport type BitkitAvatarProps = Omit<\n AvatarRootProps,\n 'variant' | 'backgroundColor' | 'background' | 'bg' | 'colorPalette'\n> &\n (\n | {\n variant: 'image';\n src: string;\n name: string;\n icon?: never;\n iconColor?: never;\n backgroundColor?: never;\n }\n | {\n variant: 'image+icon';\n src: string;\n name: string;\n icon: BitkitIconComponent;\n iconColor?: never;\n backgroundColor?: never;\n }\n | {\n variant: 'icon';\n src?: never;\n name?: never;\n icon: BitkitIconComponent;\n iconColor?: AvatarRootProps['color'];\n backgroundColor?: AvatarRootProps['color'];\n }\n | {\n variant: 'letters';\n src?: never;\n name: string;\n icon?: never;\n iconColor?: never;\n backgroundColor?: never;\n }\n | {\n variant: 'user';\n src: string;\n name: string;\n icon?: never;\n iconColor?: never;\n backgroundColor?: never;\n }\n );\n\ntype AvatarColorVariant =\n 'neutral' | 'purple' | 'indigo' | 'blue' | 'green' | 'yellow' | 'red' | 'orange' | 'turquoise';\n\nconst COLORS = Object.keys(avatarSlotRecipe.variants?.colorVariant || {}) as AvatarColorVariant[];\n\nconst pickPalette = (name?: string): AvatarColorVariant => {\n if (!name || name.length === 0) return 'neutral';\n const str = name.trim();\n return COLORS[(str.charCodeAt(0) + str.charCodeAt(str.length - 1)) % COLORS.length];\n};\n\nconst processFallbackName = (name?: string) => {\n const str = name?.trim() || '';\n return str && str.length >= 2 ? `${str[0]} ${str[str.length - 1]}` : str;\n};\n\nconst BitkitAvatar = forwardRef<HTMLDivElement, BitkitAvatarProps>((props, ref) => {\n const {\n size,\n variant,\n src,\n name,\n icon: Icon,\n colorVariant = pickPalette(name),\n iconColor,\n backgroundColor,\n ...rest\n } = props;\n\n const recipe = useSlotRecipe({ key: 'avatar' });\n const styleVariant = variant === 'user' && ['24', '32', '40', '48'].includes(size as string) ? 'circle' : 'rounded';\n const styles = recipe({ size, variant: styleVariant });\n const fallbackName = processFallbackName(name);\n\n return (\n <Avatar.Root\n ref={ref}\n size={size}\n variant={styleVariant}\n colorVariant={colorVariant}\n backgroundColor={backgroundColor}\n {...rest}\n >\n {!!fallbackName && <Avatar.Fallback name={fallbackName} />}\n {!!src && <Avatar.Image src={src} />}\n {!!Icon && variant === 'icon' && <Icon css={styles.icon} color={iconColor} size=\"24\" />}\n {!!Icon && variant === 'image+icon' && (\n <Box css={styles.badgeContainer}>\n <Icon css={styles.badge} size=\"16\" />\n </Box>\n )}\n </Avatar.Root>\n );\n});\n\nBitkitAvatar.displayName = 'BitkitAvatar';\n\nexport default BitkitAvatar;\n"],"mappings":";;;;;;;AA0DA,IAAM,SAAS,OAAO,KAAK,iBAAiB,UAAU,gBAAgB,CAAC,CAAC;AAExE,IAAM,eAAe,SAAsC;CACzD,IAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,OAAO;CACvC,MAAM,MAAM,KAAK,KAAK;CACtB,OAAO,QAAQ,IAAI,WAAW,CAAC,IAAI,IAAI,WAAW,IAAI,SAAS,CAAC,KAAK,OAAO;AAC9E;AAEA,IAAM,uBAAuB,SAAkB;CAC7C,MAAM,MAAM,MAAM,KAAK,KAAK;CAC5B,OAAO,OAAO,IAAI,UAAU,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,SAAS,OAAO;AACvE;AAEA,IAAM,eAAe,YAA+C,OAAO,QAAQ;CACjF,MAAM,EACJ,MACA,SACA,KACA,MACA,MAAM,MACN,eAAe,YAAY,IAAI,GAC/B,WACA,iBACA,GAAG,SACD;CAEJ,MAAM,SAAS,cAAc,EAAE,KAAK,SAAS,CAAC;CAC9C,MAAM,eAAe,YAAY,UAAU;EAAC;EAAM;EAAM;EAAM;CAAI,CAAC,CAAC,SAAS,IAAc,IAAI,WAAW;CAC1G,MAAM,SAAS,OAAO;EAAE;EAAM,SAAS;CAAa,CAAC;CACrD,MAAM,eAAe,oBAAoB,IAAI;CAE7C,OACE,qBAAC,OAAO,MAAR;EACO;EACC;EACN,SAAS;EACK;EACG;EACjB,GAAI;EANN,UAAA;GAQG,CAAC,CAAC,gBAAgB,oBAAC,OAAO,UAAR,EAAiB,MAAM,aAAe,CAAA;GACxD,CAAC,CAAC,OAAO,oBAAC,OAAO,OAAR,EAAmB,IAAM,CAAA;GAClC,CAAC,CAAC,QAAQ,YAAY,UAAU,oBAAC,MAAD;IAAM,KAAK,OAAO;IAAM,OAAO;IAAW,MAAK;GAAM,CAAA;GACrF,CAAC,CAAC,QAAQ,YAAY,gBACrB,oBAAC,KAAD;IAAK,KAAK,OAAO;IACf,UAAA,oBAAC,MAAD;KAAM,KAAK,OAAO;KAAO,MAAK;IAAM,CAAA;GACjC,CAAA;EAEI;;AAEjB,CAAC;AAED,aAAa,cAAc"}
1
+ {"version":3,"file":"BitkitAvatar.js","names":[],"sources":["../../../lib/components/BitkitAvatar/BitkitAvatar.tsx"],"sourcesContent":["import { Avatar, type AvatarRootProps } from '@chakra-ui/react/avatar';\nimport { Box } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport { type BitkitIconComponent } from '../../icons';\nimport avatarSlotRecipe from '../../theme/slot-recipes/Avatar.recipe';\n\nexport type BitkitAvatarProps = Omit<\n AvatarRootProps,\n 'variant' | 'backgroundColor' | 'background' | 'bg' | 'colorPalette'\n> &\n (\n | {\n variant: 'image';\n src: string;\n name: string;\n icon?: never;\n iconColor?: never;\n backgroundColor?: never;\n /** Replaces the initials when there is no image to show. Rendered as decoration (`aria-hidden`): give it no accessible name of its own, the avatar's `name` is announced next to it. */\n fallback?: ReactNode;\n }\n | {\n variant: 'image+icon';\n src: string;\n name: string;\n icon: BitkitIconComponent;\n iconColor?: never;\n backgroundColor?: never;\n /** Replaces the initials when there is no image to show. Rendered as decoration (`aria-hidden`): give it no accessible name of its own, the avatar's `name` is announced next to it. */\n fallback?: ReactNode;\n }\n | {\n variant: 'icon';\n src?: never;\n name?: never;\n icon: BitkitIconComponent;\n iconColor?: AvatarRootProps['color'];\n backgroundColor?: AvatarRootProps['color'];\n fallback?: never;\n }\n | {\n variant: 'letters';\n src?: never;\n name: string;\n icon?: never;\n iconColor?: never;\n backgroundColor?: never;\n fallback?: never;\n }\n | {\n variant: 'user';\n src: string;\n name: string;\n icon?: never;\n iconColor?: never;\n backgroundColor?: never;\n /** Replaces the initials when there is no image to show. Rendered as decoration (`aria-hidden`): give it no accessible name of its own, the avatar's `name` is announced next to it. */\n fallback?: ReactNode;\n }\n );\n\ntype AvatarColorVariant =\n 'neutral' | 'purple' | 'indigo' | 'blue' | 'green' | 'yellow' | 'red' | 'orange' | 'turquoise';\n\nconst COLORS = Object.keys(avatarSlotRecipe.variants?.colorVariant || {}) as AvatarColorVariant[];\n\nconst pickPalette = (name?: string): AvatarColorVariant => {\n if (!name || name.length === 0) return 'neutral';\n const str = name.trim();\n return COLORS[(str.charCodeAt(0) + str.charCodeAt(str.length - 1)) % COLORS.length];\n};\n\nconst processFallbackName = (name?: string) => {\n const str = name?.trim() || '';\n return str && str.length >= 2 ? `${str[0]} ${str[str.length - 1]}` : str;\n};\n\nconst BitkitAvatar = forwardRef<HTMLDivElement, BitkitAvatarProps>((props, ref) => {\n const {\n size,\n variant,\n src,\n name,\n icon: Icon,\n colorVariant = pickPalette(name),\n iconColor,\n backgroundColor,\n fallback,\n ...rest\n } = props;\n\n const recipe = useSlotRecipe({ key: 'avatar' });\n const styleVariant = variant === 'user' && ['24', '32', '40', '48'].includes(size as string) ? 'circle' : 'rounded';\n const styles = recipe({ size, variant: styleVariant });\n const fallbackName = processFallbackName(name);\n\n return (\n <Avatar.Root\n ref={ref}\n size={size}\n variant={styleVariant}\n colorVariant={colorVariant}\n backgroundColor={backgroundColor}\n {...rest}\n >\n {fallback ? (\n <Avatar.Fallback aria-hidden>{fallback}</Avatar.Fallback>\n ) : (\n !!fallbackName && <Avatar.Fallback name={fallbackName} />\n )}\n {!!src && <Avatar.Image src={src} />}\n {!!Icon && variant === 'icon' && <Icon css={styles.icon} color={iconColor} size=\"24\" />}\n {!!Icon && variant === 'image+icon' && (\n <Box css={styles.badgeContainer}>\n <Icon css={styles.badge} size=\"16\" />\n </Box>\n )}\n </Avatar.Root>\n );\n});\n\nBitkitAvatar.displayName = 'BitkitAvatar';\n\nexport default BitkitAvatar;\n"],"mappings":";;;;;;;AAkEA,IAAM,SAAS,OAAO,KAAK,iBAAiB,UAAU,gBAAgB,CAAC,CAAC;AAExE,IAAM,eAAe,SAAsC;CACzD,IAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,OAAO;CACvC,MAAM,MAAM,KAAK,KAAK;CACtB,OAAO,QAAQ,IAAI,WAAW,CAAC,IAAI,IAAI,WAAW,IAAI,SAAS,CAAC,KAAK,OAAO;AAC9E;AAEA,IAAM,uBAAuB,SAAkB;CAC7C,MAAM,MAAM,MAAM,KAAK,KAAK;CAC5B,OAAO,OAAO,IAAI,UAAU,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,SAAS,OAAO;AACvE;AAEA,IAAM,eAAe,YAA+C,OAAO,QAAQ;CACjF,MAAM,EACJ,MACA,SACA,KACA,MACA,MAAM,MACN,eAAe,YAAY,IAAI,GAC/B,WACA,iBACA,UACA,GAAG,SACD;CAEJ,MAAM,SAAS,cAAc,EAAE,KAAK,SAAS,CAAC;CAC9C,MAAM,eAAe,YAAY,UAAU;EAAC;EAAM;EAAM;EAAM;CAAI,CAAC,CAAC,SAAS,IAAc,IAAI,WAAW;CAC1G,MAAM,SAAS,OAAO;EAAE;EAAM,SAAS;CAAa,CAAC;CACrD,MAAM,eAAe,oBAAoB,IAAI;CAE7C,OACE,qBAAC,OAAO,MAAR;EACO;EACC;EACN,SAAS;EACK;EACG;EACjB,GAAI;EANN,UAAA;GAQG,WACC,oBAAC,OAAO,UAAR;IAAiB,eAAA;IAAa,UAAA;GAA0B,CAAA,IAExD,CAAC,CAAC,gBAAgB,oBAAC,OAAO,UAAR,EAAiB,MAAM,aAAe,CAAA;GAEzD,CAAC,CAAC,OAAO,oBAAC,OAAO,OAAR,EAAmB,IAAM,CAAA;GAClC,CAAC,CAAC,QAAQ,YAAY,UAAU,oBAAC,MAAD;IAAM,KAAK,OAAO;IAAM,OAAO;IAAW,MAAK;GAAM,CAAA;GACrF,CAAC,CAAC,QAAQ,YAAY,gBACrB,oBAAC,KAAD;IAAK,KAAK,OAAO;IACf,UAAA,oBAAC,MAAD;KAAM,KAAK,OAAO;KAAO,MAAK;IAAM,CAAA;GACjC,CAAA;EAEI;;AAEjB,CAAC;AAED,aAAa,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitDatePicker.js","names":[],"sources":["../../../lib/components/BitkitDatePicker/BitkitDatePicker.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useFieldContext } from '@chakra-ui/react/field';\nimport { useSafeLayoutEffect } from '@chakra-ui/react/hooks';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { type SystemStyleObject, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useContext, useMemo, useRef } from 'react';\n\nimport { DatePicker, type DatePickerValueChangeDetails, useDatePickerContext } from '../../atoms/DatePicker/DatePicker';\nimport IconCalendar from '../../icons/IconCalendar';\nimport IconErrorCircleFilled from '../../icons/IconErrorCircleFilled';\nimport IconWarningYellow from '../../icons/IconWarningYellow';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { calendarDateToDate, dateToCalendarDate } from '../BitkitCalendar/Calendar.utils';\nimport CalendarContent from '../BitkitCalendar/components/CalendarContent';\nimport BitkitControlButton from '../BitkitControlButton/BitkitControlButton';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\n\nexport type BitkitDatePickerControlProps = BoxProps;\n\nexport interface BitkitDatePickerProps extends Omit<BitkitFieldProps, 'children' | 'defaultValue' | 'state' | 'value'> {\n controlProps?: BitkitDatePickerControlProps;\n /** Text for a day's tooltip, e.g. why it can't be picked. Return `undefined` for no tooltip. */\n dayTooltip?: (date: Date) => string | undefined;\n defaultValue?: Date | null;\n disablePortal?: boolean;\n max?: Date;\n min?: Date;\n onValueChange?: (value: Date | null) => void;\n placeholder?: string;\n showOutsideDays?: boolean;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n value?: Date | null;\n}\n\n/**\n * Ark offers `openOnClick` but nothing for focus, so the calendar is opened from the input's own\n * focus event — that covers tabbing in as well as clicking.\n */\nconst DatePickerInput = ({ css }: { css: SystemStyleObject }) => {\n const { disabled, open, readOnly, setOpen } = useDatePickerContext();\n // Ark's DatePicker.Input has no notion of Chakra's Field — without this, `errorText`/`helperText`\n // render visually but are never announced, since nothing points the input's aria-describedby at them.\n const ariaDescribedby = useFieldContext()?.ariaDescribedby;\n const reopenBlockedRef = useRef(false);\n const wasOpenRef = useRef(false);\n\n // Zag's `focusInputElement` puts focus back on the input a frame after the calendar closes on\n // selection, which would bounce it straight back open. Ignore focus that lands right after a\n // close — this has to be a layout effect so the flag is set before Zag's rAF runs. Isomorphic\n // layout effect: avoids the SSR \"useLayoutEffect does nothing on the server\" warning.\n useSafeLayoutEffect(() => {\n const justClosed = wasOpenRef.current && !open;\n wasOpenRef.current = open;\n\n // Only a real close can trigger Zag's refocus, so mount (never-opened) must not arm the block\n // — otherwise an autoFocus or a programmatic focus right after mount gets swallowed.\n if (!justClosed) {\n return undefined;\n }\n\n reopenBlockedRef.current = true;\n const timeout = setTimeout(() => {\n reopenBlockedRef.current = false;\n }, 100);\n\n return () => clearTimeout(timeout);\n }, [open]);\n\n // Ark gates its own trigger handlers on the machine's interactive state, but `setOpen` is not —\n // it would happily open the calendar on a read-only or disabled field. A disabled input still\n // needs the check: Chrome fires focus events for a programmatic `.focus()` on one, even though\n // focus never actually lands there.\n const handleFocus = () => {\n if (!reopenBlockedRef.current && !readOnly && !disabled) {\n setOpen(true);\n }\n };\n\n return <DatePicker.Input aria-describedby={ariaDescribedby} css={css} index={0} onFocus={handleFocus} unstyled />;\n};\n\nconst BitkitDatePicker = forwardRef<HTMLDivElement, BitkitDatePickerProps>((props, ref) => {\n const {\n controlProps,\n dayTooltip,\n defaultValue,\n disablePortal,\n max,\n min,\n onValueChange,\n placeholder,\n showOutsideDays,\n size = 'lg',\n state,\n value,\n ...fieldProps\n } = props;\n\n const recipe = useSlotRecipe({ key: 'textInput' });\n const styles = recipe({ size });\n\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n\n const minCalendarDate = useMemo(() => (min ? dateToCalendarDate(min) : undefined), [min]);\n const maxCalendarDate = useMemo(() => (max ? dateToCalendarDate(max) : undefined), [max]);\n\n const iconSize = size === 'md' ? '16' : '24';\n const hasWarning = state === 'warning' || !!fieldProps.warningText;\n const isInvalid = state === 'error' || !!fieldProps.errorText;\n\n let statusIcon: ReactNode = null;\n if (isInvalid) {\n statusIcon = <IconErrorCircleFilled color=\"icon/negative\" size={iconSize} />;\n } else if (hasWarning) {\n statusIcon = <IconWarningYellow size={iconSize} />;\n }\n\n const stateAttrs = {\n 'data-disabled': state === 'disabled' ? '' : undefined,\n 'data-invalid': isInvalid ? '' : undefined,\n 'data-readonly': state === 'readOnly' ? '' : undefined,\n };\n\n const isInteractive = state !== 'disabled' && state !== 'readOnly';\n\n const calendarButton = (\n <BitkitControlButton\n icon={IconCalendar}\n label=\"Open calendar\"\n // Pulled out by its own padding so the glyph lands where a plain end icon would sit in\n // BitkitTextInput — 16px (lg) / 12px (md) from the field's outer edge.\n marginInlineEnd={size === 'md' ? '-8' : '-4'}\n size={size === 'md' ? 'xs' : 'sm'}\n state={isInteractive ? undefined : 'disabled'}\n />\n );\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <DatePicker.Root\n defaultValue={defaultValue ? [dateToCalendarDate(defaultValue)] : undefined}\n device=\"desktop\"\n disabled={state === 'disabled'}\n invalid={isInvalid}\n layout=\"1-month\"\n max={maxCalendarDate}\n min={minCalendarDate}\n numOfMonths={1}\n onValueChange={\n onValueChange\n ? (details: DatePickerValueChangeDetails) =>\n onValueChange(details.value?.[0] ? calendarDateToDate(details.value[0]) : null)\n : undefined\n }\n placeholder={state === 'readOnly' ? '(not set)' : placeholder}\n positioning={{ placement: 'bottom-start' }}\n readOnly={state === 'readOnly'}\n selectionMode=\"single\"\n showOutsideDays={showOutsideDays ?? true}\n startOfWeek={1}\n // Stay controlled whenever `value` is provided — even for `null` (\"nothing selected\").\n // Passing `undefined` would flip Root to uncontrolled. See BitkitCombobox.\n value={value === undefined ? undefined : value ? [dateToCalendarDate(value)] : []}\n >\n <DatePicker.Control asChild>\n <Box {...controlProps} css={[styles.field, controlProps?.css]} {...stateAttrs}>\n <DatePickerInput css={styles.input} />\n {statusIcon != null && (\n <Box css={styles.element} {...stateAttrs}>\n {statusIcon}\n </Box>\n )}\n {/* Ark keeps the trigger interactive in read-only mode and forces `disabled={false}`\n onto whatever it wraps, so the button is left unwrapped there to stay inert. */}\n {state === 'readOnly' ? calendarButton : <DatePicker.Trigger asChild>{calendarButton}</DatePicker.Trigger>}\n </Box>\n </DatePicker.Control>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <DatePicker.Positioner>\n <CalendarContent\n dayTooltip={dayTooltip}\n layout=\"1-month\"\n max={maxCalendarDate}\n min={minCalendarDate}\n selectionMode=\"day\"\n />\n </DatePicker.Positioner>\n </Portal>\n </DatePicker.Root>\n </BitkitField>\n );\n});\n\nBitkitDatePicker.displayName = 'BitkitDatePicker';\n\nexport default BitkitDatePicker;\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitDatePicker.js","names":[],"sources":["../../../lib/components/BitkitDatePicker/BitkitDatePicker.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useFieldContext } from '@chakra-ui/react/field';\nimport { useSafeLayoutEffect } from '@chakra-ui/react/hooks';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { type SystemStyleObject, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useContext, useMemo, useRef } from 'react';\n\nimport { DatePicker, type DatePickerValueChangeDetails, useDatePickerContext } from '../../atoms/DatePicker/DatePicker';\nimport IconCalendar from '../../icons/IconCalendar';\nimport IconErrorCircleFilled from '../../icons/IconErrorCircleFilled';\nimport IconWarningYellow from '../../icons/IconWarningYellow';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { calendarDateToDate, dateToCalendarDate } from '../BitkitCalendar/Calendar.utils';\nimport CalendarContent from '../BitkitCalendar/components/CalendarContent';\nimport BitkitControlButton from '../BitkitControlButton/BitkitControlButton';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\n\nexport type BitkitDatePickerControlProps = BoxProps;\n\nexport interface BitkitDatePickerProps extends Omit<BitkitFieldProps, 'children' | 'defaultValue' | 'state' | 'value'> {\n controlProps?: BitkitDatePickerControlProps;\n /** Text for a day's tooltip, e.g. why it can't be picked. Return `undefined` for no tooltip. */\n dayTooltip?: (date: Date) => string | undefined;\n defaultValue?: Date | null;\n disablePortal?: boolean;\n max?: Date;\n min?: Date;\n onValueChange?: (value: Date | null) => void;\n placeholder?: string;\n showOutsideDays?: boolean;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n value?: Date | null;\n}\n\n/**\n * Ark offers `openOnClick` but nothing for focus, so the calendar is opened from the input's own\n * focus event — that covers tabbing in as well as clicking.\n */\nconst DatePickerInput = ({ css }: { css: SystemStyleObject }) => {\n const { disabled, open, readOnly, setOpen } = useDatePickerContext();\n // Ark's DatePicker.Input has no notion of Chakra's Field — without this, `errorText`/`helperText`\n // render visually but are never announced, since nothing points the input's aria-describedby at them.\n const ariaDescribedby = useFieldContext()?.ariaDescribedby;\n const reopenBlockedRef = useRef(false);\n const wasOpenRef = useRef(false);\n\n // Zag's `focusInputElement` puts focus back on the input a frame after the calendar closes on\n // selection, which would bounce it straight back open. Ignore focus that lands right after a\n // close — this has to be a layout effect so the flag is set before Zag's rAF runs. Isomorphic\n // layout effect: avoids the SSR \"useLayoutEffect does nothing on the server\" warning.\n useSafeLayoutEffect(() => {\n const justClosed = wasOpenRef.current && !open;\n wasOpenRef.current = open;\n\n // Only a real close can trigger Zag's refocus, so mount (never-opened) must not arm the block\n // — otherwise an autoFocus or a programmatic focus right after mount gets swallowed.\n if (!justClosed) {\n return undefined;\n }\n\n reopenBlockedRef.current = true;\n const timeout = setTimeout(() => {\n reopenBlockedRef.current = false;\n }, 100);\n\n return () => clearTimeout(timeout);\n }, [open]);\n\n // Ark gates its own trigger handlers on the machine's interactive state, but `setOpen` is not —\n // it would happily open the calendar on a read-only or disabled field. A disabled input still\n // needs the check: Chrome fires focus events for a programmatic `.focus()` on one, even though\n // focus never actually lands there.\n const handleFocus = () => {\n if (!reopenBlockedRef.current && !readOnly && !disabled) {\n setOpen(true);\n }\n };\n\n return <DatePicker.Input aria-describedby={ariaDescribedby} css={css} index={0} onFocus={handleFocus} unstyled />;\n};\n\nconst BitkitDatePicker = forwardRef<HTMLDivElement, BitkitDatePickerProps>((props, ref) => {\n const {\n controlProps,\n dayTooltip,\n defaultValue,\n disablePortal,\n max,\n min,\n onValueChange,\n placeholder,\n showOutsideDays,\n size = 'lg',\n state,\n value,\n ...fieldProps\n } = props;\n\n const recipe = useSlotRecipe({ key: 'textInput' });\n const styles = recipe({ size });\n\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n\n const minCalendarDate = useMemo(() => (min ? dateToCalendarDate(min) : undefined), [min]);\n const maxCalendarDate = useMemo(() => (max ? dateToCalendarDate(max) : undefined), [max]);\n\n const iconSize = size === 'md' ? '16' : '24';\n const hasWarning = state === 'warning' || !!fieldProps.warningText;\n const isInvalid = state === 'error' || !!fieldProps.errorText;\n\n let statusIcon: ReactNode = null;\n if (isInvalid) {\n statusIcon = <IconErrorCircleFilled color=\"icon/negative\" size={iconSize} />;\n } else if (hasWarning) {\n statusIcon = <IconWarningYellow size={iconSize} />;\n }\n\n const stateAttrs = {\n 'data-disabled': state === 'disabled' ? '' : undefined,\n 'data-invalid': isInvalid ? '' : undefined,\n 'data-readonly': state === 'readOnly' ? '' : undefined,\n };\n\n const isInteractive = state !== 'disabled' && state !== 'readOnly';\n\n const calendarButton = (\n <BitkitControlButton\n icon={IconCalendar}\n label=\"Open calendar\"\n // Pulled out by the space around its icon so the glyph lands where a plain end icon would sit in\n // BitkitTextInput — 16px (lg) / 12px (md) from the field's outer edge.\n marginInlineEnd={size === 'md' ? '-8' : '-4'}\n size={size === 'md' ? 'xs' : 'sm'}\n state={isInteractive ? undefined : 'disabled'}\n />\n );\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <DatePicker.Root\n defaultValue={defaultValue ? [dateToCalendarDate(defaultValue)] : undefined}\n device=\"desktop\"\n disabled={state === 'disabled'}\n invalid={isInvalid}\n layout=\"1-month\"\n max={maxCalendarDate}\n min={minCalendarDate}\n numOfMonths={1}\n onValueChange={\n onValueChange\n ? (details: DatePickerValueChangeDetails) =>\n onValueChange(details.value?.[0] ? calendarDateToDate(details.value[0]) : null)\n : undefined\n }\n placeholder={state === 'readOnly' ? '(not set)' : placeholder}\n positioning={{ placement: 'bottom-start' }}\n readOnly={state === 'readOnly'}\n selectionMode=\"single\"\n showOutsideDays={showOutsideDays ?? true}\n startOfWeek={1}\n // Stay controlled whenever `value` is provided — even for `null` (\"nothing selected\").\n // Passing `undefined` would flip Root to uncontrolled. See BitkitCombobox.\n value={value === undefined ? undefined : value ? [dateToCalendarDate(value)] : []}\n >\n <DatePicker.Control asChild>\n <Box {...controlProps} css={[styles.field, controlProps?.css]} {...stateAttrs}>\n <DatePickerInput css={styles.input} />\n {statusIcon != null && (\n <Box css={styles.element} {...stateAttrs}>\n {statusIcon}\n </Box>\n )}\n {/* Ark keeps the trigger interactive in read-only mode and forces `disabled={false}`\n onto whatever it wraps, so the button is left unwrapped there to stay inert. */}\n {state === 'readOnly' ? calendarButton : <DatePicker.Trigger asChild>{calendarButton}</DatePicker.Trigger>}\n </Box>\n </DatePicker.Control>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <DatePicker.Positioner>\n <CalendarContent\n dayTooltip={dayTooltip}\n layout=\"1-month\"\n max={maxCalendarDate}\n min={minCalendarDate}\n selectionMode=\"day\"\n />\n </DatePicker.Positioner>\n </Portal>\n </DatePicker.Root>\n </BitkitField>\n );\n});\n\nBitkitDatePicker.displayName = 'BitkitDatePicker';\n\nexport default BitkitDatePicker;\n"],"mappings":"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"}
@@ -21,6 +21,7 @@ var SelectValue = ({ placeholder, state, size }) => {
21
21
  const items = useSelectContext().selectedItems;
22
22
  const Icon = items[0]?.icon;
23
23
  const avatar = items[0]?.avatar;
24
+ const avatarFallback = items[0]?.avatarFallback;
24
25
  const label = items[0]?.label;
25
26
  const showAvatar = avatar !== void 0;
26
27
  const { avatarSize, iconSize } = selectSizeMap[size];
@@ -34,7 +35,8 @@ var SelectValue = ({ placeholder, state, size }) => {
34
35
  variant: "image",
35
36
  src: avatar ?? "",
36
37
  name: label,
37
- size: avatarSize
38
+ size: avatarSize,
39
+ fallback: avatarFallback
38
40
  })
39
41
  }) : Icon && /* @__PURE__ */ jsx(Icon, {
40
42
  css: styles.valueIcon,
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitSelect.js","names":[],"sources":["../../../lib/components/BitkitSelect/BitkitSelect.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Select, type SelectRootProps, type SelectTriggerProps, useSelectContext } from '@chakra-ui/react/select';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, useContext } from 'react';\n\nimport { IconErrorCircleFilled, IconWarningYellow } from '../../icons';\nimport AssetSelectChevron from '../../utilities/AssetSelectChevron.tsx';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { withSubComponents } from '../../utilities/withSubComponents.ts';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField.tsx';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuErrorStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n type BitkitSelectMenuSearchProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu.tsx';\nimport BitkitSelectMenuAction, {\n type BitkitSelectMenuActionChild,\n} from '../BitkitSelectMenu/BitkitSelectMenuAction.tsx';\nimport { selectSizeMap } from '../BitkitSelectMenu/SelectMenuShell.tsx';\n\nexport type BitkitSelectTriggerProps = SelectTriggerProps;\n\nexport type BitkitSelectProps = Omit<BitkitFieldProps, 'children' | 'onChange' | 'state'> & {\n children?: BitkitSelectMenuActionChild;\n defaultValue?: string;\n isLoading?: boolean;\n items: Array<BitkitSelectMenuItemProps>;\n onValueChange?: (newVal: string) => void;\n placeholder?: string;\n disablePortal?: boolean;\n selectProps?: Omit<SelectRootProps, 'collection' | 'defaultValue' | 'onValueChange' | 'size' | 'value'>;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n triggerProps?: BitkitSelectTriggerProps;\n value?: string;\n} & BitkitSelectMenuSearchProps &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps &\n BitkitSelectMenuErrorStateProps;\n\ntype SelectValueProps = {\n placeholder?: string;\n size: NonNullable<BitkitSelectProps['size']>;\n state?: BitkitSelectProps['state'];\n};\n\nconst SelectValue = ({ placeholder, state, size }: SelectValueProps) => {\n const select = useSelectContext();\n const items = select.selectedItems as Array<BitkitSelectMenuItemProps>;\n\n const Icon = items[0]?.icon;\n const avatar = items[0]?.avatar;\n const label = items[0]?.label;\n const showAvatar = avatar !== undefined;\n\n const { avatarSize, iconSize } = selectSizeMap[size];\n\n const recipe = useSlotRecipe({ key: 'select' });\n const styles = recipe({ size });\n\n return items[0] ? (\n <Select.ValueText placeholder={placeholder ?? (state === 'readOnly' ? '(not selected)' : 'Select an option')}>\n {showAvatar ? (\n // Same avatar handling as the menu item (see BitkitSelectMenu.tsx). The `data-slot`\n // lets the trigger recipe shrink its vertical padding so the avatar row keeps the\n // 48/40 trigger height (see the `&:has([data-slot=\"avatar\"])` rule in Select.recipe.ts).\n <Box data-slot=\"avatar\" css={styles.avatar}>\n <BitkitAvatar variant=\"image\" src={avatar ?? ''} name={label} size={avatarSize} />\n </Box>\n ) : (\n Icon && <Icon css={styles.valueIcon} size={iconSize} flexShrink={0} />\n )}\n <Text as=\"span\" overflow=\"hidden\" textOverflow=\"ellipsis\" whiteSpace=\"nowrap\" minWidth=\"0\">\n {label}\n </Text>\n </Select.ValueText>\n ) : (\n <Select.ValueText\n placeholder={placeholder ?? (state === 'readOnly' ? '(not selected)' : 'Select an option')}\n display=\"block\"\n overflow=\"hidden\"\n textOverflow=\"ellipsis\"\n whiteSpace=\"nowrap\"\n />\n );\n};\n\nconst BitkitSelect = forwardRef<HTMLDivElement, BitkitSelectProps>((props: BitkitSelectProps, ref) => {\n const {\n children,\n defaultValue,\n emptyHelperText,\n emptyLabel,\n errorHelperText,\n errorLabel,\n isLoading,\n items,\n onScrolledToBottom,\n onSearchChange,\n onValueChange,\n placeholder,\n disablePortal,\n searchPlaceholder,\n searchValue,\n selectProps,\n size = 'lg',\n state,\n triggerProps,\n value,\n ...fieldProps\n } = props;\n\n const collection = createListCollection({\n items,\n groupBy: (item) => item.group || '',\n isItemDisabled: (item) => !!item.disabled,\n });\n\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n const isInvalid = state === 'error' || !!fieldProps.errorText;\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <Select.Root\n collection={collection}\n // Chakra pins the list to the trigger's width; ours grows with its options, up to the recipe's cap.\n positioning={{ sameWidth: false }}\n size={size}\n {...selectProps}\n defaultValue={defaultValue ? [defaultValue] : undefined}\n disabled={state === 'disabled'}\n hasStatusIcon={state === 'error' || state === 'warning'}\n invalid={isInvalid}\n onValueChange={(newVal) => onValueChange?.(newVal.value[0])}\n readOnly={state === 'readOnly'}\n // Bypass Zag's isScrollable(contentEl) gate — our Content is overflow:hidden flex\n // column, so the real scroll container is itemList. See BitkitMultiselect for why.\n scrollToIndexFn={({ getElement }) => getElement()?.scrollIntoView({ block: 'nearest' })}\n // Stay controlled whenever `value` is provided — including `''` (\"nothing selected\"). A\n // truthiness check (`value ? … : undefined`) passes `undefined` for `''`, which flips\n // Select.Root to uncontrolled: it then ignores `value`, so resets don't take and\n // onValueChange stops firing when the same option is re-selected.\n value={value === undefined ? undefined : value ? [value] : []}\n >\n <Select.Control className=\"group\">\n <Select.HiddenSelect />\n <Select.Trigger {...triggerProps} aria-readonly={state === 'readOnly' || undefined}>\n <SelectValue placeholder={placeholder} size={size} state={state} />\n </Select.Trigger>\n <Select.IndicatorGroup>\n {state === 'error' && (\n <Select.Indicator>\n <IconErrorCircleFilled size={size === 'lg' ? '24' : '16'} color=\"icon/negative\" />\n </Select.Indicator>\n )}\n {state === 'warning' && (\n <Select.Indicator>\n <IconWarningYellow size={size === 'lg' ? '24' : '16'} />\n </Select.Indicator>\n )}\n <Select.Indicator asChild>\n <AssetSelectChevron />\n </Select.Indicator>\n </Select.IndicatorGroup>\n </Select.Control>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <Select.Positioner>\n <BitkitSelectMenu\n collection={collection}\n emptyHelperText={emptyHelperText}\n emptyLabel={emptyLabel}\n errorHelperText={errorHelperText}\n errorLabel={errorLabel}\n isLoading={isLoading}\n onScrolledToBottom={onScrolledToBottom}\n onSearchChange={onSearchChange}\n searchPlaceholder={searchPlaceholder}\n searchValue={searchValue}\n size={size}\n >\n {children}\n </BitkitSelectMenu>\n </Select.Positioner>\n </Portal>\n </Select.Root>\n </BitkitField>\n );\n});\n\nBitkitSelect.displayName = 'BitkitSelect';\n\nexport default withSubComponents(BitkitSelect, { Action: BitkitSelectMenuAction });\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitSelect.js","names":[],"sources":["../../../lib/components/BitkitSelect/BitkitSelect.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Select, type SelectRootProps, type SelectTriggerProps, useSelectContext } from '@chakra-ui/react/select';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, useContext } from 'react';\n\nimport { IconErrorCircleFilled, IconWarningYellow } from '../../icons';\nimport AssetSelectChevron from '../../utilities/AssetSelectChevron.tsx';\nimport BitkitPortalContext from '../../utilities/BitkitPortalContext';\nimport { withSubComponents } from '../../utilities/withSubComponents.ts';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField.tsx';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuErrorStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n type BitkitSelectMenuSearchProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu.tsx';\nimport BitkitSelectMenuAction, {\n type BitkitSelectMenuActionChild,\n} from '../BitkitSelectMenu/BitkitSelectMenuAction.tsx';\nimport { selectSizeMap } from '../BitkitSelectMenu/SelectMenuShell.tsx';\n\nexport type BitkitSelectTriggerProps = SelectTriggerProps;\n\nexport type BitkitSelectProps = Omit<BitkitFieldProps, 'children' | 'onChange' | 'state'> & {\n children?: BitkitSelectMenuActionChild;\n defaultValue?: string;\n isLoading?: boolean;\n items: Array<BitkitSelectMenuItemProps>;\n onValueChange?: (newVal: string) => void;\n placeholder?: string;\n disablePortal?: boolean;\n selectProps?: Omit<SelectRootProps, 'collection' | 'defaultValue' | 'onValueChange' | 'size' | 'value'>;\n size?: 'md' | 'lg';\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n triggerProps?: BitkitSelectTriggerProps;\n value?: string;\n} & BitkitSelectMenuSearchProps &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps &\n BitkitSelectMenuErrorStateProps;\n\ntype SelectValueProps = {\n placeholder?: string;\n size: NonNullable<BitkitSelectProps['size']>;\n state?: BitkitSelectProps['state'];\n};\n\nconst SelectValue = ({ placeholder, state, size }: SelectValueProps) => {\n const select = useSelectContext();\n const items = select.selectedItems as Array<BitkitSelectMenuItemProps>;\n\n const Icon = items[0]?.icon;\n const avatar = items[0]?.avatar;\n const avatarFallback = items[0]?.avatarFallback;\n const label = items[0]?.label;\n const showAvatar = avatar !== undefined;\n\n const { avatarSize, iconSize } = selectSizeMap[size];\n\n const recipe = useSlotRecipe({ key: 'select' });\n const styles = recipe({ size });\n\n return items[0] ? (\n <Select.ValueText placeholder={placeholder ?? (state === 'readOnly' ? '(not selected)' : 'Select an option')}>\n {showAvatar ? (\n // Same avatar handling as the menu item (see BitkitSelectMenu.tsx). The `data-slot`\n // lets the trigger recipe shrink its vertical padding so the avatar row keeps the\n // 48/40 trigger height (see the `&:has([data-slot=\"avatar\"])` rule in Select.recipe.ts).\n <Box data-slot=\"avatar\" css={styles.avatar}>\n <BitkitAvatar variant=\"image\" src={avatar ?? ''} name={label} size={avatarSize} fallback={avatarFallback} />\n </Box>\n ) : (\n Icon && <Icon css={styles.valueIcon} size={iconSize} flexShrink={0} />\n )}\n <Text as=\"span\" overflow=\"hidden\" textOverflow=\"ellipsis\" whiteSpace=\"nowrap\" minWidth=\"0\">\n {label}\n </Text>\n </Select.ValueText>\n ) : (\n <Select.ValueText\n placeholder={placeholder ?? (state === 'readOnly' ? '(not selected)' : 'Select an option')}\n display=\"block\"\n overflow=\"hidden\"\n textOverflow=\"ellipsis\"\n whiteSpace=\"nowrap\"\n />\n );\n};\n\nconst BitkitSelect = forwardRef<HTMLDivElement, BitkitSelectProps>((props: BitkitSelectProps, ref) => {\n const {\n children,\n defaultValue,\n emptyHelperText,\n emptyLabel,\n errorHelperText,\n errorLabel,\n isLoading,\n items,\n onScrolledToBottom,\n onSearchChange,\n onValueChange,\n placeholder,\n disablePortal,\n searchPlaceholder,\n searchValue,\n selectProps,\n size = 'lg',\n state,\n triggerProps,\n value,\n ...fieldProps\n } = props;\n\n const collection = createListCollection({\n items,\n groupBy: (item) => item.group || '',\n isItemDisabled: (item) => !!item.disabled,\n });\n\n const { disablePortal: disablePortalFromContext } = useContext(BitkitPortalContext);\n const isInvalid = state === 'error' || !!fieldProps.errorText;\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <Select.Root\n collection={collection}\n // Chakra pins the list to the trigger's width; ours grows with its options, up to the recipe's cap.\n positioning={{ sameWidth: false }}\n size={size}\n {...selectProps}\n defaultValue={defaultValue ? [defaultValue] : undefined}\n disabled={state === 'disabled'}\n hasStatusIcon={state === 'error' || state === 'warning'}\n invalid={isInvalid}\n onValueChange={(newVal) => onValueChange?.(newVal.value[0])}\n readOnly={state === 'readOnly'}\n // Bypass Zag's isScrollable(contentEl) gate — our Content is overflow:hidden flex\n // column, so the real scroll container is itemList. See BitkitMultiselect for why.\n scrollToIndexFn={({ getElement }) => getElement()?.scrollIntoView({ block: 'nearest' })}\n // Stay controlled whenever `value` is provided — including `''` (\"nothing selected\"). A\n // truthiness check (`value ? … : undefined`) passes `undefined` for `''`, which flips\n // Select.Root to uncontrolled: it then ignores `value`, so resets don't take and\n // onValueChange stops firing when the same option is re-selected.\n value={value === undefined ? undefined : value ? [value] : []}\n >\n <Select.Control className=\"group\">\n <Select.HiddenSelect />\n <Select.Trigger {...triggerProps} aria-readonly={state === 'readOnly' || undefined}>\n <SelectValue placeholder={placeholder} size={size} state={state} />\n </Select.Trigger>\n <Select.IndicatorGroup>\n {state === 'error' && (\n <Select.Indicator>\n <IconErrorCircleFilled size={size === 'lg' ? '24' : '16'} color=\"icon/negative\" />\n </Select.Indicator>\n )}\n {state === 'warning' && (\n <Select.Indicator>\n <IconWarningYellow size={size === 'lg' ? '24' : '16'} />\n </Select.Indicator>\n )}\n <Select.Indicator asChild>\n <AssetSelectChevron />\n </Select.Indicator>\n </Select.IndicatorGroup>\n </Select.Control>\n <Portal disabled={disablePortal || disablePortalFromContext}>\n <Select.Positioner>\n <BitkitSelectMenu\n collection={collection}\n emptyHelperText={emptyHelperText}\n emptyLabel={emptyLabel}\n errorHelperText={errorHelperText}\n errorLabel={errorLabel}\n isLoading={isLoading}\n onScrolledToBottom={onScrolledToBottom}\n onSearchChange={onSearchChange}\n searchPlaceholder={searchPlaceholder}\n searchValue={searchValue}\n size={size}\n >\n {children}\n </BitkitSelectMenu>\n </Select.Positioner>\n </Portal>\n </Select.Root>\n </BitkitField>\n );\n});\n\nBitkitSelect.displayName = 'BitkitSelect';\n\nexport default withSubComponents(BitkitSelect, { Action: BitkitSelectMenuAction });\n"],"mappings":"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"}
@@ -24,6 +24,8 @@ export type BitkitSelectMenuItemProps = {
24
24
  * of `label`; omitting the prop renders no avatar and leaves the row's leading slot to
25
25
  * `icon`. */
26
26
  avatar?: string | null;
27
+ /** Replaces the initials when `avatar` is `null` or its image fails to load. Decorative (`aria-hidden`): `label` names the row. */
28
+ avatarFallback?: ReactNode;
27
29
  helperText?: ReactNode;
28
30
  /** A `BitkitBadge` after the label. Forced to `colorVariant="neutral"` on a disabled item. */
29
31
  badge?: ReactElement<BitkitBadgeProps>;
@@ -60,7 +60,8 @@ var SelectMenuItem = ({ NS, avatarSize, item, iconSize, spinnerSize, styles }) =
60
60
  variant: "image",
61
61
  src: item.avatar ?? "",
62
62
  name: item.label,
63
- size: avatarSize
63
+ size: avatarSize,
64
+ fallback: item.avatarFallback
64
65
  })
65
66
  }),
66
67
  /* @__PURE__ */ jsxs(Box, {
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitSelectMenu.js","names":[],"sources":["../../../lib/components/BitkitSelectMenu/BitkitSelectMenu.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { type ListCollection } from '@chakra-ui/react/collection';\nimport { Combobox } from '@chakra-ui/react/combobox';\nimport { Select, type SelectContentProps } from '@chakra-ui/react/select';\nimport { Spinner } from '@chakra-ui/react/spinner';\nimport { type SystemStyleObject, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { cloneElement, forwardRef, type ReactElement, type ReactNode } from 'react';\n\nimport { type BitkitIconComponent, IconCheck } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport { type BitkitBadgeProps } from '../BitkitBadge/BitkitBadge';\nimport { type BitkitSelectMenuActionChild } from './BitkitSelectMenuAction';\nimport { SelectMenuShell, selectSizeMap, type SelectSizes } from './SelectMenuShell';\n\nexport { type BitkitSelectMenuActionProps } from './BitkitSelectMenuAction';\n\nexport type BitkitSelectMenuSearchProps = {\n /** When provided, a search input is rendered above the menu items. `searchValue` is required alongside. */\n onSearchChange?: (searchText: string) => void;\n searchPlaceholder?: string;\n searchValue?: string;\n};\n\nexport type BitkitSelectMenuScrollProps = {\n /** Fires when the item list reaches its end. Position only: keep your own loading and end-of-list guards. */\n onScrolledToBottom?: () => void;\n};\n\nexport type BitkitSelectMenuItemProps = {\n value: string;\n label: string;\n group?: string;\n icon?: BitkitIconComponent;\n /** The item's avatar. A URL renders that image; `null` renders the avatar with the initials\n * of `label`; omitting the prop renders no avatar and leaves the row's leading slot to\n * `icon`. */\n avatar?: string | null;\n helperText?: ReactNode;\n /** A `BitkitBadge` after the label. Forced to `colorVariant=\"neutral\"` on a disabled item. */\n badge?: ReactElement<BitkitBadgeProps>;\n disabled?: boolean;\n loading?: boolean;\n};\n\nexport type BitkitSelectMenuEmptyStateProps = {\n emptyLabel?: string;\n emptyHelperText?: string;\n};\n\nexport type BitkitSelectMenuErrorStateProps = {\n errorLabel?: string;\n errorHelperText?: string;\n};\n\nexport type BitkitSelectMenuProps = {\n children?: BitkitSelectMenuActionChild;\n collection: ListCollection<BitkitSelectMenuItemProps>;\n /** Forwarded to the underlying Select/Combobox.Content. Kept for internal callers\n * (e.g. BitkitCalendar's in-grid selects that need to tweak max-height / width). The\n * public components (BitkitSelect, BitkitCombobox) do not expose this escape hatch. */\n contentProps?: SelectContentProps;\n isLoading?: boolean;\n size?: 'md' | 'lg';\n variant?: 'select' | 'combobox';\n} & BitkitSelectMenuSearchProps &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps &\n BitkitSelectMenuErrorStateProps;\n\nconst BitkitSelectMenu = forwardRef<HTMLDivElement, BitkitSelectMenuProps>((props, ref) => {\n const { children, collection, variant = 'select', size, ...shellProps } = props;\n const NS = variant === 'combobox' ? Combobox : Select;\n const recipe = useSlotRecipe({ key: 'select' });\n const styles = recipe({ size });\n const { avatarSize, iconSize, spinnerSize } = selectSizeMap[size ?? 'lg'];\n\n return (\n <SelectMenuShell\n NS={NS}\n collection={collection}\n contentRef={ref}\n iconSize={iconSize}\n renderItem={(item) => (\n <SelectMenuItem\n key={item.value}\n NS={NS}\n avatarSize={avatarSize}\n item={item}\n iconSize={iconSize}\n spinnerSize={spinnerSize}\n styles={styles}\n />\n )}\n spinnerSize={spinnerSize}\n styles={styles}\n {...shellProps}\n >\n {children}\n </SelectMenuShell>\n );\n});\n\nBitkitSelectMenu.displayName = 'BitkitSelectMenu';\n\ntype SelectMenuItemRenderProps = {\n NS: typeof Select | typeof Combobox;\n avatarSize: SelectSizes['avatarSize'];\n item: BitkitSelectMenuItemProps;\n iconSize: SelectSizes['iconSize'];\n spinnerSize: SelectSizes['spinnerSize'];\n styles: Record<string, SystemStyleObject>;\n};\n\nconst SelectMenuItem = ({ NS, avatarSize, item, iconSize, spinnerSize, styles }: SelectMenuItemRenderProps) => {\n const Icon = item.icon;\n const showAvatar = item.avatar !== undefined;\n\n if (item.loading) {\n // Rendered as a plain Box, not NS.Item — Zag's state machine won't track it as\n // an option, so keyboard nav skips it and it can't be selected.\n return (\n <Box css={styles.item}>\n <Spinner variant=\"purple\" size={spinnerSize} css={styles.itemLoading} />\n <Text css={styles.itemLoadingLabel}>Loading...</Text>\n </Box>\n );\n }\n\n return (\n <NS.Item css={styles.item} item={item}>\n {showAvatar && (\n <Box data-slot=\"avatar\" css={styles.avatar}>\n <BitkitAvatar variant=\"image\" src={item.avatar ?? ''} name={item.label} size={avatarSize} />\n </Box>\n )}\n <Box css={styles.itemContent}>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {Icon && !showAvatar && <Icon css={styles.itemIcon} size={iconSize} />}\n <Text css={styles.itemLabel}>{item.label}</Text>\n </Box>\n {item.helperText && <Text css={styles.itemHelperText}>{item.helperText}</Text>}\n </Box>\n {item.badge && (\n <Box css={styles.itemBadge}>\n {item.disabled ? cloneElement(item.badge, { colorVariant: 'neutral' }) : item.badge}\n </Box>\n )}\n <NS.ItemIndicator asChild>\n <IconCheck size={iconSize} css={styles.itemIndicator} />\n </NS.ItemIndicator>\n </NS.Item>\n );\n};\n\nexport default BitkitSelectMenu;\n"],"mappings":";;;;;;;;;;;;AAsEA,IAAM,mBAAmB,YAAmD,OAAO,QAAQ;CACzF,MAAM,EAAE,UAAU,YAAY,UAAU,UAAU,MAAM,GAAG,eAAe;CAC1E,MAAM,KAAK,YAAY,aAAa,WAAW;CAE/C,MAAM,SADS,cAAc,EAAE,KAAK,SAAS,CAC9B,CAAA,CAAO,EAAE,KAAK,CAAC;CAC9B,MAAM,EAAE,YAAY,UAAU,gBAAgB,cAAc,QAAQ;CAEpE,OACE,oBAAC,iBAAD;EACM;EACQ;EACZ,YAAY;EACF;EACV,aAAa,SACX,oBAAC,gBAAD;GAEM;GACQ;GACN;GACI;GACG;GACL;EACT,GAPM,KAAK,KAOX;EAEU;EACL;EACR,GAAI;EAEH;CACc,CAAA;AAErB,CAAC;AAED,iBAAiB,cAAc;AAW/B,IAAM,kBAAkB,EAAE,IAAI,YAAY,MAAM,UAAU,aAAa,aAAwC;CAC7G,MAAM,OAAO,KAAK;CAClB,MAAM,aAAa,KAAK,WAAW,KAAA;CAEnC,IAAI,KAAK,SAGP,OACE,qBAAC,KAAD;EAAK,KAAK,OAAO;EAAjB,UAAA,CACE,oBAAC,SAAD;GAAS,SAAQ;GAAS,MAAM;GAAa,KAAK,OAAO;EAAc,CAAA,GACvE,oBAAC,MAAD;GAAM,KAAK,OAAO;GAAkB,UAAA;EAAgB,CAAA,CACjD;;CAIT,OACE,qBAAC,GAAG,MAAJ;EAAS,KAAK,OAAO;EAAY;EAAjC,UAAA;GACG,cACC,oBAAC,KAAD;IAAK,aAAU;IAAS,KAAK,OAAO;IAClC,UAAA,oBAAC,cAAD;KAAc,SAAQ;KAAQ,KAAK,KAAK,UAAU;KAAI,MAAM,KAAK;KAAO,MAAM;IAAa,CAAA;GACxF,CAAA;GAEP,qBAAC,KAAD;IAAK,KAAK,OAAO;IAAjB,UAAA,CACE,qBAAC,KAAD;KAAK,SAAQ;KAAO,YAAW;KAAS,KAAI;KAA5C,UAAA,CACG,QAAQ,CAAC,cAAc,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAU,MAAM;KAAW,CAAA,GACrE,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAY,UAAA,KAAK;KAAY,CAAA,CAC5C;IACJ,CAAA,GAAA,KAAK,cAAc,oBAAC,MAAD;KAAM,KAAK,OAAO;KAAiB,UAAA,KAAK;IAAiB,CAAA,CAC1E;;GACJ,KAAK,SACJ,oBAAC,KAAD;IAAK,KAAK,OAAO;IACd,UAAA,KAAK,WAAW,aAAa,KAAK,OAAO,EAAE,cAAc,UAAU,CAAC,IAAI,KAAK;GAC3E,CAAA;GAEP,oBAAC,GAAG,eAAJ;IAAkB,SAAA;IAChB,UAAA,oBAAC,WAAD;KAAW,MAAM;KAAU,KAAK,OAAO;IAAgB,CAAA;GACvC,CAAA;EACX;;AAEb"}
1
+ {"version":3,"file":"BitkitSelectMenu.js","names":[],"sources":["../../../lib/components/BitkitSelectMenu/BitkitSelectMenu.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { type ListCollection } from '@chakra-ui/react/collection';\nimport { Combobox } from '@chakra-ui/react/combobox';\nimport { Select, type SelectContentProps } from '@chakra-ui/react/select';\nimport { Spinner } from '@chakra-ui/react/spinner';\nimport { type SystemStyleObject, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { cloneElement, forwardRef, type ReactElement, type ReactNode } from 'react';\n\nimport { type BitkitIconComponent, IconCheck } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport { type BitkitBadgeProps } from '../BitkitBadge/BitkitBadge';\nimport { type BitkitSelectMenuActionChild } from './BitkitSelectMenuAction';\nimport { SelectMenuShell, selectSizeMap, type SelectSizes } from './SelectMenuShell';\n\nexport { type BitkitSelectMenuActionProps } from './BitkitSelectMenuAction';\n\nexport type BitkitSelectMenuSearchProps = {\n /** When provided, a search input is rendered above the menu items. `searchValue` is required alongside. */\n onSearchChange?: (searchText: string) => void;\n searchPlaceholder?: string;\n searchValue?: string;\n};\n\nexport type BitkitSelectMenuScrollProps = {\n /** Fires when the item list reaches its end. Position only: keep your own loading and end-of-list guards. */\n onScrolledToBottom?: () => void;\n};\n\nexport type BitkitSelectMenuItemProps = {\n value: string;\n label: string;\n group?: string;\n icon?: BitkitIconComponent;\n /** The item's avatar. A URL renders that image; `null` renders the avatar with the initials\n * of `label`; omitting the prop renders no avatar and leaves the row's leading slot to\n * `icon`. */\n avatar?: string | null;\n /** Replaces the initials when `avatar` is `null` or its image fails to load. Decorative (`aria-hidden`): `label` names the row. */\n avatarFallback?: ReactNode;\n helperText?: ReactNode;\n /** A `BitkitBadge` after the label. Forced to `colorVariant=\"neutral\"` on a disabled item. */\n badge?: ReactElement<BitkitBadgeProps>;\n disabled?: boolean;\n loading?: boolean;\n};\n\nexport type BitkitSelectMenuEmptyStateProps = {\n emptyLabel?: string;\n emptyHelperText?: string;\n};\n\nexport type BitkitSelectMenuErrorStateProps = {\n errorLabel?: string;\n errorHelperText?: string;\n};\n\nexport type BitkitSelectMenuProps = {\n children?: BitkitSelectMenuActionChild;\n collection: ListCollection<BitkitSelectMenuItemProps>;\n /** Forwarded to the underlying Select/Combobox.Content. Kept for internal callers\n * (e.g. BitkitCalendar's in-grid selects that need to tweak max-height / width). The\n * public components (BitkitSelect, BitkitCombobox) do not expose this escape hatch. */\n contentProps?: SelectContentProps;\n isLoading?: boolean;\n size?: 'md' | 'lg';\n variant?: 'select' | 'combobox';\n} & BitkitSelectMenuSearchProps &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps &\n BitkitSelectMenuErrorStateProps;\n\nconst BitkitSelectMenu = forwardRef<HTMLDivElement, BitkitSelectMenuProps>((props, ref) => {\n const { children, collection, variant = 'select', size, ...shellProps } = props;\n const NS = variant === 'combobox' ? Combobox : Select;\n const recipe = useSlotRecipe({ key: 'select' });\n const styles = recipe({ size });\n const { avatarSize, iconSize, spinnerSize } = selectSizeMap[size ?? 'lg'];\n\n return (\n <SelectMenuShell\n NS={NS}\n collection={collection}\n contentRef={ref}\n iconSize={iconSize}\n renderItem={(item) => (\n <SelectMenuItem\n key={item.value}\n NS={NS}\n avatarSize={avatarSize}\n item={item}\n iconSize={iconSize}\n spinnerSize={spinnerSize}\n styles={styles}\n />\n )}\n spinnerSize={spinnerSize}\n styles={styles}\n {...shellProps}\n >\n {children}\n </SelectMenuShell>\n );\n});\n\nBitkitSelectMenu.displayName = 'BitkitSelectMenu';\n\ntype SelectMenuItemRenderProps = {\n NS: typeof Select | typeof Combobox;\n avatarSize: SelectSizes['avatarSize'];\n item: BitkitSelectMenuItemProps;\n iconSize: SelectSizes['iconSize'];\n spinnerSize: SelectSizes['spinnerSize'];\n styles: Record<string, SystemStyleObject>;\n};\n\nconst SelectMenuItem = ({ NS, avatarSize, item, iconSize, spinnerSize, styles }: SelectMenuItemRenderProps) => {\n const Icon = item.icon;\n const showAvatar = item.avatar !== undefined;\n\n if (item.loading) {\n // Rendered as a plain Box, not NS.Item — Zag's state machine won't track it as\n // an option, so keyboard nav skips it and it can't be selected.\n return (\n <Box css={styles.item}>\n <Spinner variant=\"purple\" size={spinnerSize} css={styles.itemLoading} />\n <Text css={styles.itemLoadingLabel}>Loading...</Text>\n </Box>\n );\n }\n\n return (\n <NS.Item css={styles.item} item={item}>\n {showAvatar && (\n <Box data-slot=\"avatar\" css={styles.avatar}>\n <BitkitAvatar\n variant=\"image\"\n src={item.avatar ?? ''}\n name={item.label}\n size={avatarSize}\n fallback={item.avatarFallback}\n />\n </Box>\n )}\n <Box css={styles.itemContent}>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {Icon && !showAvatar && <Icon css={styles.itemIcon} size={iconSize} />}\n <Text css={styles.itemLabel}>{item.label}</Text>\n </Box>\n {item.helperText && <Text css={styles.itemHelperText}>{item.helperText}</Text>}\n </Box>\n {item.badge && (\n <Box css={styles.itemBadge}>\n {item.disabled ? cloneElement(item.badge, { colorVariant: 'neutral' }) : item.badge}\n </Box>\n )}\n <NS.ItemIndicator asChild>\n <IconCheck size={iconSize} css={styles.itemIndicator} />\n </NS.ItemIndicator>\n </NS.Item>\n );\n};\n\nexport default BitkitSelectMenu;\n"],"mappings":";;;;;;;;;;;;AAwEA,IAAM,mBAAmB,YAAmD,OAAO,QAAQ;CACzF,MAAM,EAAE,UAAU,YAAY,UAAU,UAAU,MAAM,GAAG,eAAe;CAC1E,MAAM,KAAK,YAAY,aAAa,WAAW;CAE/C,MAAM,SADS,cAAc,EAAE,KAAK,SAAS,CAC9B,CAAA,CAAO,EAAE,KAAK,CAAC;CAC9B,MAAM,EAAE,YAAY,UAAU,gBAAgB,cAAc,QAAQ;CAEpE,OACE,oBAAC,iBAAD;EACM;EACQ;EACZ,YAAY;EACF;EACV,aAAa,SACX,oBAAC,gBAAD;GAEM;GACQ;GACN;GACI;GACG;GACL;EACT,GAPM,KAAK,KAOX;EAEU;EACL;EACR,GAAI;EAEH;CACc,CAAA;AAErB,CAAC;AAED,iBAAiB,cAAc;AAW/B,IAAM,kBAAkB,EAAE,IAAI,YAAY,MAAM,UAAU,aAAa,aAAwC;CAC7G,MAAM,OAAO,KAAK;CAClB,MAAM,aAAa,KAAK,WAAW,KAAA;CAEnC,IAAI,KAAK,SAGP,OACE,qBAAC,KAAD;EAAK,KAAK,OAAO;EAAjB,UAAA,CACE,oBAAC,SAAD;GAAS,SAAQ;GAAS,MAAM;GAAa,KAAK,OAAO;EAAc,CAAA,GACvE,oBAAC,MAAD;GAAM,KAAK,OAAO;GAAkB,UAAA;EAAgB,CAAA,CACjD;;CAIT,OACE,qBAAC,GAAG,MAAJ;EAAS,KAAK,OAAO;EAAY;EAAjC,UAAA;GACG,cACC,oBAAC,KAAD;IAAK,aAAU;IAAS,KAAK,OAAO;IAClC,UAAA,oBAAC,cAAD;KACE,SAAQ;KACR,KAAK,KAAK,UAAU;KACpB,MAAM,KAAK;KACX,MAAM;KACN,UAAU,KAAK;IAChB,CAAA;GACE,CAAA;GAEP,qBAAC,KAAD;IAAK,KAAK,OAAO;IAAjB,UAAA,CACE,qBAAC,KAAD;KAAK,SAAQ;KAAO,YAAW;KAAS,KAAI;KAA5C,UAAA,CACG,QAAQ,CAAC,cAAc,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAU,MAAM;KAAW,CAAA,GACrE,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAY,UAAA,KAAK;KAAY,CAAA,CAC5C;IACJ,CAAA,GAAA,KAAK,cAAc,oBAAC,MAAD;KAAM,KAAK,OAAO;KAAiB,UAAA,KAAK;IAAiB,CAAA,CAC1E;;GACJ,KAAK,SACJ,oBAAC,KAAD;IAAK,KAAK,OAAO;IACd,UAAA,KAAK,WAAW,aAAa,KAAK,OAAO,EAAE,cAAc,UAAU,CAAC,IAAI,KAAK;GAC3E,CAAA;GAEP,oBAAC,GAAG,eAAJ;IAAkB,SAAA;IAChB,UAAA,oBAAC,WAAD;KAAW,MAAM;KAAU,KAAK,OAAO;IAAgB,CAAA;GACvC,CAAA;EACX;;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitTabs.js","names":["createContext","_Fragment"],"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, useMenuStyles } 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 cloneElement,\n forwardRef,\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 { type BitkitColorToken } from '../../theme/themeUtils';\nimport { flattenChildren } from '../../utilities/flattenChildren';\nimport { getTextContent } from '../../utilities/getTextContent';\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\nexport type BitkitTabsTriggerProps = Tabs.TriggerProps & {\n badge?: ReactNode;\n children?: string;\n icon?: BitkitIconComponent;\n /**\n * `contained` variant only: an `icon/*` token for the icon when it carries meaning of its own,\n * such as a trend arrow. It wins over the hover and selected colours; a disabled tab keeps its own.\n */\n iconColor?: BitkitColorToken<'icon'>;\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\n// Canvas and line md tabs are the small ones: 16px icons, and an md overflow menu to match.\nconst isCompactTabs = (variant: BitkitTabsRootProps['variant'], size: BitkitTabsRootProps['size']) =>\n variant === 'canvas' || (variant === 'line' && size === 'md');\n\nconst Trigger = forwardRef<HTMLButtonElement, BitkitTabsTriggerProps>((props, ref) => {\n const {\n badge,\n children,\n className,\n icon: Icon,\n iconColor,\n onClose,\n onKeyDown,\n secondaryText,\n statusColor,\n ...rest\n } = props;\n // Mirror the recipe's defaultVariants: an unset `variant` must take the line branch below, not\n // fall through to the contained one, and an unset `size` the lg icon size.\n const { size = 'lg', 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 const isCompact = isCompactTabs(variant, size);\n const hasOwnIconColor = variant === 'contained' && !!iconColor && !rest.disabled;\n const icon = Icon && (\n <Icon color={hasOwnIconColor ? iconColor : 'var(--tabs-icon-color)'} size={isCompact ? '16' : '24'} />\n );\n const canvasPadding = isCanvas\n ? { paddingInlineEnd: showTrailing ? '8' : Icon ? '16' : undefined, paddingInlineStart: Icon ? '8' : undefined }\n : undefined;\n\n return (\n <Tabs.Trigger ref={ref} {...rest} {...canvasPadding} className={mergedClassName} onKeyDown={handleTriggerKeyDown}>\n {isCanvas ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {icon}\n {children}\n </Box>\n {trailing}\n </>\n ) : variant === 'line' ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {icon}\n {children}\n </Box>\n {badge}\n </>\n ) : (\n <>\n <Box display=\"flex\" flexDirection=\"column\">\n <Box display=\"flex\" alignItems=\"flex-start\" gap=\"16\">\n {children}\n {badge}\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 </Box>\n {icon}\n </>\n )}\n </Tabs.Trigger>\n );\n});\n\nTrigger.displayName = 'BitkitTabs.Trigger';\n\ntype TabItem = ReactElement<BitkitTabsTriggerProps>;\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\ntype MoreMenuItemProps = {\n isCompact: boolean;\n item: TabItem;\n onSelect: () => void;\n variant: BitkitTabsRootProps['variant'];\n};\n\nconst MoreMenuItem = ({ isCompact, item, onSelect, variant }: MoreMenuItemProps) => {\n const menuStyles = useMenuStyles();\n const { badge, children, disabled, icon: Icon, iconColor, secondaryText, statusColor, value } = item.props;\n\n const isCanvas = variant === 'canvas';\n const isContained = variant === 'contained';\n const helperText = isContained ? secondaryText : undefined;\n\n const ownIconColor = isContained ? iconColor : undefined;\n const helperStyles = disabled ? { ...menuStyles.itemHelper, color: 'text/on-disabled' } : menuStyles.itemHelper;\n\n return (\n <Menu.Item data-helper={helperText ? '' : undefined} disabled={disabled} onSelect={onSelect} value={value}>\n {Icon && <Icon color={disabled ? 'icon/on-disabled' : ownIconColor} size={isCompact ? '16' : '24'} />}\n <Box flex=\"1\">\n <Text color={disabled ? 'text/on-disabled' : undefined}>{children}</Text>\n {!!helperText && <Text css={helperStyles}>{helperText}</Text>}\n </Box>\n {!isCanvas && badge}\n {isCanvas && statusColor && (\n <chakra.span boxSize=\"8\" borderRadius=\"full\" flexShrink={0} backgroundColor={STATUS_COLOR_TOKEN[statusColor]} />\n )}\n </Menu.Item>\n );\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// A stretched trigger (line tabs on mobile, or a consumer's own `flex`/`width`) is as wide as\n// its share of the list, not its content, so its laid-out width would count the free space as\n// needed space. Measure it unstretched, and unrounded: two 163.5px tabs read as 164 + 164 through\n// `offsetWidth` and overflow a 327px row.\n// The whole inline style is restored, because resetting the `flex` shorthand would also wipe any\n// inline flex longhands the consumer set.\nconst measureContentWidth = (node: HTMLElement) => {\n const { cssText } = node.style;\n node.style.flex = '0 0 auto';\n node.style.width = 'max-content';\n const contentWidth = node.getBoundingClientRect().width;\n node.style.cssText = cssText;\n return contentWidth;\n};\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, css, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...rest } = props;\n const { size = 'lg', variant = 'line' } = useRootPropsContext();\n const api = useTabsContext();\n const styles = useTabsStyles();\n const isCompact = isCompactTabs(variant, size);\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 and size, each\n // tab's value/label, icon/status/close/disabled state, and the text of its badge and secondaryText,\n // so a count going from 3 to 12 invalidates the cache.\n const measureKey = [\n variant,\n size,\n ...items.map((item) => {\n const p = item.props;\n return [\n p.value,\n String(p.children),\n p.icon ? 'i' : '',\n getTextContent(p.badge),\n getTextContent(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 // Width reservations for the more button, sized for a two-digit count (\"10 more…\"), and for the\n // always-visible add button. Any slack here hides a tab that would have fitted.\n const moreReserve = isCompact ? 88 : 104;\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, measureContentWidth);\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 // Swapping the fallback font for the webfont changes the tabs' widths without resizing the list,\n // so no ResizeObserver fires for it: measure again once the fonts are in.\n useEffect(() => {\n let cancelled = false;\n void document.fonts?.ready.then(() => {\n if (!cancelled) setMeasuredKey(null);\n });\n return () => {\n cancelled = true;\n };\n }, []);\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, css]} data-overflowing={hiddenItems.length > 0 ? '' : undefined} {...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<BitkitTabsTriggerProps>),\n )}\n </Tabs.List>\n {hiddenItems.length > 0 && (\n <Menu.Root size={isCompact ? 'md' : 'lg'} 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 <MoreMenuItem\n isCompact={isCompact}\n item={item}\n key={item.props.value}\n onSelect={() => api.setValue(item.props.value)}\n variant={variant}\n />\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":["createContext","_Fragment"],"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, useMenuStyles } 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 cloneElement,\n forwardRef,\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 { type BitkitColorToken } from '../../theme/themeUtils';\nimport { flattenChildren } from '../../utilities/flattenChildren';\nimport { getTextContent } from '../../utilities/getTextContent';\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\nexport type BitkitTabsTriggerProps = Tabs.TriggerProps & {\n badge?: ReactNode;\n children?: string;\n icon?: BitkitIconComponent;\n /**\n * `contained` variant only: an `icon/*` token for the icon when it carries meaning of its own,\n * such as a trend arrow. It wins over the hover and selected colours; a disabled tab keeps its own.\n */\n iconColor?: BitkitColorToken<'icon'>;\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\n// Canvas and line md tabs are the small ones: 16px icons, and an md overflow menu to match.\nconst isCompactTabs = (variant: BitkitTabsRootProps['variant'], size: BitkitTabsRootProps['size']) =>\n variant === 'canvas' || (variant === 'line' && size === 'md');\n\nconst Trigger = forwardRef<HTMLButtonElement, BitkitTabsTriggerProps>((props, ref) => {\n const {\n badge,\n children,\n className,\n icon: Icon,\n iconColor,\n onClose,\n onKeyDown,\n secondaryText,\n statusColor,\n ...rest\n } = props;\n // Mirror the recipe's defaultVariants: an unset `variant` must take the line branch below, not\n // fall through to the contained one, and an unset `size` the lg icon size.\n const { size = 'lg', 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 const isCompact = isCompactTabs(variant, size);\n const hasOwnIconColor = variant === 'contained' && !!iconColor && !rest.disabled;\n const icon = Icon && (\n <Icon color={hasOwnIconColor ? iconColor : 'var(--tabs-icon-color)'} size={isCompact ? '16' : '24'} />\n );\n const canvasPadding = isCanvas\n ? { paddingInlineEnd: showTrailing ? '8' : Icon ? '16' : undefined, paddingInlineStart: Icon ? '8' : undefined }\n : undefined;\n\n return (\n <Tabs.Trigger ref={ref} {...rest} {...canvasPadding} className={mergedClassName} onKeyDown={handleTriggerKeyDown}>\n {isCanvas ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {icon}\n {children}\n </Box>\n {trailing}\n </>\n ) : variant === 'line' ? (\n <>\n <Box display=\"flex\" alignItems=\"center\" gap=\"8\">\n {icon}\n {children}\n </Box>\n {badge}\n </>\n ) : (\n <>\n <Box display=\"flex\" flexDirection=\"column\">\n <Box display=\"flex\" alignItems=\"flex-start\" gap=\"16\">\n {children}\n {badge}\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 </Box>\n {icon}\n </>\n )}\n </Tabs.Trigger>\n );\n});\n\nTrigger.displayName = 'BitkitTabs.Trigger';\n\ntype TabItem = ReactElement<BitkitTabsTriggerProps>;\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\ntype MoreMenuItemProps = {\n isCompact: boolean;\n item: TabItem;\n onSelect: () => void;\n variant: BitkitTabsRootProps['variant'];\n};\n\nconst MoreMenuItem = ({ isCompact, item, onSelect, variant }: MoreMenuItemProps) => {\n const menuStyles = useMenuStyles();\n const { badge, children, disabled, icon: Icon, iconColor, secondaryText, statusColor, value } = item.props;\n\n const isCanvas = variant === 'canvas';\n const isContained = variant === 'contained';\n const helperText = isContained ? secondaryText : undefined;\n\n const ownIconColor = isContained ? iconColor : undefined;\n const helperStyles = disabled ? { ...menuStyles.itemHelper, color: 'text/on-disabled' } : menuStyles.itemHelper;\n\n return (\n <Menu.Item data-helper={helperText ? '' : undefined} disabled={disabled} onSelect={onSelect} value={value}>\n {Icon && <Icon color={disabled ? 'icon/on-disabled' : ownIconColor} size={isCompact ? '16' : '24'} />}\n <Box flex=\"1\">\n <Text color={disabled ? 'text/on-disabled' : undefined}>{children}</Text>\n {!!helperText && <Text css={helperStyles}>{helperText}</Text>}\n </Box>\n {!isCanvas && badge}\n {isCanvas && statusColor && (\n <chakra.span boxSize=\"8\" borderRadius=\"full\" flexShrink={0} backgroundColor={STATUS_COLOR_TOKEN[statusColor]} />\n )}\n </Menu.Item>\n );\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 in a 24px box) 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// A stretched trigger (line tabs on mobile, or a consumer's own `flex`/`width`) is as wide as\n// its share of the list, not its content, so its laid-out width would count the free space as\n// needed space. Measure it unstretched, and unrounded: two 163.5px tabs read as 164 + 164 through\n// `offsetWidth` and overflow a 327px row.\n// The whole inline style is restored, because resetting the `flex` shorthand would also wipe any\n// inline flex longhands the consumer set.\nconst measureContentWidth = (node: HTMLElement) => {\n const { cssText } = node.style;\n node.style.flex = '0 0 auto';\n node.style.width = 'max-content';\n const contentWidth = node.getBoundingClientRect().width;\n node.style.cssText = cssText;\n return contentWidth;\n};\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, css, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...rest } = props;\n const { size = 'lg', variant = 'line' } = useRootPropsContext();\n const api = useTabsContext();\n const styles = useTabsStyles();\n const isCompact = isCompactTabs(variant, size);\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 and size, each\n // tab's value/label, icon/status/close/disabled state, and the text of its badge and secondaryText,\n // so a count going from 3 to 12 invalidates the cache.\n const measureKey = [\n variant,\n size,\n ...items.map((item) => {\n const p = item.props;\n return [\n p.value,\n String(p.children),\n p.icon ? 'i' : '',\n getTextContent(p.badge),\n getTextContent(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 // Width reservations for the more button, sized for a two-digit count (\"10 more…\"), and for the\n // always-visible add button. Any slack here hides a tab that would have fitted.\n const moreReserve = isCompact ? 88 : 104;\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, measureContentWidth);\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 // Swapping the fallback font for the webfont changes the tabs' widths without resizing the list,\n // so no ResizeObserver fires for it: measure again once the fonts are in.\n useEffect(() => {\n let cancelled = false;\n void document.fonts?.ready.then(() => {\n if (!cancelled) setMeasuredKey(null);\n });\n return () => {\n cancelled = true;\n };\n }, []);\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, css]} data-overflowing={hiddenItems.length > 0 ? '' : undefined} {...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<BitkitTabsTriggerProps>),\n )}\n </Tabs.List>\n {hiddenItems.length > 0 && (\n <Menu.Root size={isCompact ? 'md' : 'lg'} 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 <MoreMenuItem\n isCompact={isCompact}\n item={item}\n key={item.props.value}\n onSelect={() => api.setValue(item.props.value)}\n variant={variant}\n />\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,5 +1,8 @@
1
1
  import { HTMLChakraProps } from '@chakra-ui/react/styled-system';
2
+ import { ReactNode } from 'react';
2
3
  export interface BitkitUIHeaderNavButtonProps extends Omit<HTMLChakraProps<'button'>, 'children' | 'disabled'> {
4
+ /** Replaces the avatar's initials when the workspace has no logo, or its logo fails to load. Decorative (`aria-hidden`): the button's `label` names it. */
5
+ fallback?: ReactNode;
3
6
  /** The accessible name. */
4
7
  label?: string;
5
8
  /** The current workspace. Shown as the avatar's initials when it has no logo. */
@@ -16,10 +16,11 @@ import { Box } from "@chakra-ui/react/box";
16
16
  * It shows the current workspace, so switching workspaces is one tap away from anywhere.
17
17
  */
18
18
  var UIHeaderNavButton = forwardRef((props, ref) => {
19
- const { label = "Open navigation", name, src, ...rest } = props;
19
+ const { fallback, label = "Open navigation", name, src, ...rest } = props;
20
20
  const { styles } = useUIHeaderContext();
21
- const avatarProps = src ? {
22
- src,
21
+ const avatarProps = src || fallback ? {
22
+ fallback,
23
+ src: src ?? "",
23
24
  variant: "image"
24
25
  } : { variant: "letters" };
25
26
  return /* @__PURE__ */ jsxs(UIHeaderControlBase, {
@@ -1 +1 @@
1
- {"version":3,"file":"UIHeaderNavButton.js","names":[],"sources":["../../../lib/components/BitkitUIHeader/UIHeaderNavButton.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { type HTMLChakraProps } from '@chakra-ui/react/styled-system';\nimport { forwardRef } from 'react';\n\nimport { IconMenuHamburger } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport { useUIHeaderContext } from './UIHeaderContext';\nimport { UIHeaderControlBase } from './UIHeaderControl';\n\nexport interface BitkitUIHeaderNavButtonProps extends Omit<HTMLChakraProps<'button'>, 'children' | 'disabled'> {\n /** The accessible name. */\n label?: string;\n /** The current workspace. Shown as the avatar's initials when it has no logo. */\n name: string;\n /** The workspace's logo. Without one the avatar falls back to its initials. */\n src?: string;\n}\n\n/**\n * Opens `BitkitUISideNav` on mobile, where the navigation is a drawer rather than a rail. It sits\n * in the header's `leading` slot, which is hidden from the `tablet` breakpoint up.\n *\n * The two components do not know about each other: this button's `onClick` sets the navigation's\n * `open`, and the navigation asks to close through its own `onOpenChange`.\n *\n * It shows the current workspace, so switching workspaces is one tap away from anywhere.\n */\nconst UIHeaderNavButton = forwardRef<HTMLButtonElement, BitkitUIHeaderNavButtonProps>((props, ref) => {\n const { label = 'Open navigation', name, src, ...rest } = props;\n\n const { styles } = useUIHeaderContext();\n\n const avatarProps = src ? ({ src, variant: 'image' } as const) : ({ variant: 'letters' } as const);\n\n return (\n <UIHeaderControlBase ref={ref} footprint=\"navButton\" label={label} {...rest}>\n <BitkitAvatar name={name} {...avatarProps} />\n <Box css={styles.navButtonBadge}>\n <IconMenuHamburger css={styles.navButtonIcon} size=\"16\" />\n </Box>\n </UIHeaderControlBase>\n );\n});\n\nUIHeaderNavButton.displayName = 'BitkitUIHeader.NavButton';\n\nexport default UIHeaderNavButton;\n"],"mappings":";;;;;;;;;;;;;;;;;AA2BA,IAAM,oBAAoB,YAA6D,OAAO,QAAQ;CACpG,MAAM,EAAE,QAAQ,mBAAmB,MAAM,KAAK,GAAG,SAAS;CAE1D,MAAM,EAAE,WAAW,mBAAmB;CAEtC,MAAM,cAAc,MAAO;EAAE;EAAK,SAAS;CAAQ,IAAe,EAAE,SAAS,UAAU;CAEvF,OACE,qBAAC,qBAAD;EAA0B;EAAK,WAAU;EAAmB;EAAO,GAAI;EAAvE,UAAA,CACE,oBAAC,cAAD;GAAoB;GAAM,GAAI;EAAc,CAAA,GAC5C,oBAAC,KAAD;GAAK,KAAK,OAAO;GACf,UAAA,oBAAC,mBAAD;IAAmB,KAAK,OAAO;IAAe,MAAK;GAAM,CAAA;EACtD,CAAA,CACc;;AAEzB,CAAC;AAED,kBAAkB,cAAc"}
1
+ {"version":3,"file":"UIHeaderNavButton.js","names":[],"sources":["../../../lib/components/BitkitUIHeader/UIHeaderNavButton.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { type HTMLChakraProps } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport { IconMenuHamburger } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport { useUIHeaderContext } from './UIHeaderContext';\nimport { UIHeaderControlBase } from './UIHeaderControl';\n\nexport interface BitkitUIHeaderNavButtonProps extends Omit<HTMLChakraProps<'button'>, 'children' | 'disabled'> {\n /** Replaces the avatar's initials when the workspace has no logo, or its logo fails to load. Decorative (`aria-hidden`): the button's `label` names it. */\n fallback?: ReactNode;\n /** The accessible name. */\n label?: string;\n /** The current workspace. Shown as the avatar's initials when it has no logo. */\n name: string;\n /** The workspace's logo. Without one the avatar falls back to its initials. */\n src?: string;\n}\n\n/**\n * Opens `BitkitUISideNav` on mobile, where the navigation is a drawer rather than a rail. It sits\n * in the header's `leading` slot, which is hidden from the `tablet` breakpoint up.\n *\n * The two components do not know about each other: this button's `onClick` sets the navigation's\n * `open`, and the navigation asks to close through its own `onOpenChange`.\n *\n * It shows the current workspace, so switching workspaces is one tap away from anywhere.\n */\nconst UIHeaderNavButton = forwardRef<HTMLButtonElement, BitkitUIHeaderNavButtonProps>((props, ref) => {\n const { fallback, label = 'Open navigation', name, src, ...rest } = props;\n\n const { styles } = useUIHeaderContext();\n\n const avatarProps =\n src || fallback ? ({ fallback, src: src ?? '', variant: 'image' } as const) : ({ variant: 'letters' } as const);\n\n return (\n <UIHeaderControlBase ref={ref} footprint=\"navButton\" label={label} {...rest}>\n <BitkitAvatar name={name} {...avatarProps} />\n <Box css={styles.navButtonBadge}>\n <IconMenuHamburger css={styles.navButtonIcon} size=\"16\" />\n </Box>\n </UIHeaderControlBase>\n );\n});\n\nUIHeaderNavButton.displayName = 'BitkitUIHeader.NavButton';\n\nexport default UIHeaderNavButton;\n"],"mappings":";;;;;;;;;;;;;;;;;AA6BA,IAAM,oBAAoB,YAA6D,OAAO,QAAQ;CACpG,MAAM,EAAE,UAAU,QAAQ,mBAAmB,MAAM,KAAK,GAAG,SAAS;CAEpE,MAAM,EAAE,WAAW,mBAAmB;CAEtC,MAAM,cACJ,OAAO,WAAY;EAAE;EAAU,KAAK,OAAO;EAAI,SAAS;CAAQ,IAAe,EAAE,SAAS,UAAU;CAEtG,OACE,qBAAC,qBAAD;EAA0B;EAAK,WAAU;EAAmB;EAAO,GAAI;EAAvE,UAAA,CACE,oBAAC,cAAD;GAAoB;GAAM,GAAI;EAAc,CAAA,GAC5C,oBAAC,KAAD;GAAK,KAAK,OAAO;GACf,UAAA,oBAAC,mBAAD;IAAmB,KAAK,OAAO;IAAe,MAAK;GAAM,CAAA;EACtD,CAAA,CACc;;AAEzB,CAAC;AAED,kBAAkB,cAAc"}
@@ -1,4 +1,5 @@
1
1
  import { Select, SelectTriggerProps } from '@chakra-ui/react/select';
2
+ import { ReactNode } from 'react';
2
3
  import { BitkitSelectMenuEmptyStateProps, BitkitSelectMenuErrorStateProps, BitkitSelectMenuItemProps, BitkitSelectMenuScrollProps, BitkitSelectMenuSearchProps } from '../BitkitSelectMenu/BitkitSelectMenu';
3
4
  import { BitkitSelectMenuActionChild } from '../BitkitSelectMenu/BitkitSelectMenuAction';
4
5
  export type BitkitUISideNavWorkspaceProps = Pick<BitkitSelectMenuItemProps, 'label' | 'value'> & {
@@ -7,6 +8,8 @@ export type BitkitUISideNavWorkspaceProps = Pick<BitkitSelectMenuItemProps, 'lab
7
8
  * a workspace always has one, unlike a plain menu item.
8
9
  */
9
10
  avatar?: string;
11
+ /** Replaces the initials when the workspace has no logo, or its logo fails to load. Decorative (`aria-hidden`): `label` names the workspace. */
12
+ avatarFallback?: ReactNode;
10
13
  };
11
14
  /**
12
15
  * The list length at which the design adds the search input. Below it the menu is short enough
@@ -77,6 +77,7 @@ var UISideNavWorkspaceSelector = forwardRef((props, ref) => {
77
77
  /* @__PURE__ */ jsx(BitkitAvatar, {
78
78
  borderColor: "border/minimal",
79
79
  borderWidth: "1",
80
+ fallback: selected?.avatarFallback,
80
81
  name: selected?.label ?? "",
81
82
  size: "40",
82
83
  src: selected?.avatar ?? "",
@@ -1 +1 @@
1
- {"version":3,"file":"UISideNavWorkspaceSelector.js","names":[],"sources":["../../../lib/components/BitkitUISideNav/UISideNavWorkspaceSelector.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { useControllableState } from '@chakra-ui/react/hooks';\nimport { useFilter } from '@chakra-ui/react/locale';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Select, type SelectTriggerProps } from '@chakra-ui/react/select';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, useId, useMemo, useRef, useState } from 'react';\n\nimport { IconChevronDown } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuErrorStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n type BitkitSelectMenuSearchProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu';\nimport { type BitkitSelectMenuActionChild } from '../BitkitSelectMenu/BitkitSelectMenuAction';\nimport { useUISideNavContext } from './UISideNavContext';\n\nexport type BitkitUISideNavWorkspaceProps = Pick<BitkitSelectMenuItemProps, 'label' | 'value'> & {\n /**\n * The workspace's logo. Leave it off and the avatar falls back to the initials of `label` —\n * a workspace always has one, unlike a plain menu item.\n */\n avatar?: string;\n};\n\n/**\n * The list length at which the design adds the search input. Below it the menu is short enough\n * to read at a glance and a search field would only be in the way. Exported for the stories,\n * which label each list by the side of this it falls on — not part of the package's surface.\n */\nexport const searchableFrom = 6;\n\nexport type BitkitUISideNavWorkspaceSelectorProps = Omit<SelectTriggerProps, 'children' | 'onChange'> & {\n /**\n * A single `BitkitSelectMenuAction` shown under the list, typically \"Create workspace\". Its\n * presence is the toggle — there is no boolean for it.\n */\n children?: BitkitSelectMenuActionChild;\n isLoading?: boolean;\n /** The small uppercase caption above the workspace name. */\n label?: string;\n /**\n * Fires when the menu opens and when it closes — where a list loaded on demand belongs. The\n * menu also opens from the keyboard, which fires no click on the trigger.\n */\n onOpenChange?: (open: boolean) => void;\n onValueChange?: (value: string) => void;\n /** The selected workspace's `value`. Leave it off to let the selector track the choice itself. */\n value?: string;\n workspaces: Array<BitkitUISideNavWorkspaceProps>;\n // The copy is the design's, not the consumer's, so the placeholder is not on the way through.\n} & Omit<BitkitSelectMenuSearchProps, 'searchPlaceholder'> &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps &\n BitkitSelectMenuErrorStateProps;\n\nconst UISideNavWorkspaceSelector = forwardRef<HTMLButtonElement, BitkitUISideNavWorkspaceSelectorProps>(\n (props, ref) => {\n const {\n children,\n css,\n emptyHelperText,\n emptyLabel,\n errorHelperText,\n errorLabel,\n isLoading,\n label = 'Workspace',\n onOpenChange,\n onScrolledToBottom,\n onSearchChange,\n onValueChange,\n searchValue,\n value,\n workspaces,\n ...rest\n } = props;\n\n const { isMobile, menuContainerRef, setMenuOpen, styles } = useUISideNavContext();\n const id = useId();\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const { contains } = useFilter({ sensitivity: 'base' });\n\n const [ownQuery, setOwnQuery] = useState('');\n const [currentValue, setCurrentValue] = useControllableState<string | undefined>({\n defaultValue: undefined,\n value,\n });\n\n // A consumer that passes `onSearchChange` is searching somewhere we cannot see — a server, a\n // paged list — so `workspaces` is already the result and its length says nothing about how\n // many there are. They keep the input and the filtering; otherwise both are ours.\n const isSearchDelegated = onSearchChange !== undefined;\n const query = isSearchDelegated ? searchValue : ownQuery;\n const hasSearch = isSearchDelegated || workspaces.length >= searchableFrom;\n\n const collection = useMemo(() => {\n const matches = ownQuery ? workspaces.filter((workspace) => contains(workspace.label, ownQuery)) : workspaces;\n\n // `null` rather than nothing for a workspace without a logo: that is what tells the menu to\n // draw the initials, where an absent `avatar` would leave the row with no avatar at all.\n const items: Array<BitkitSelectMenuItemProps> = matches.map((workspace) => ({\n ...workspace,\n avatar: workspace.avatar ?? null,\n }));\n\n return createListCollection({ items });\n }, [contains, ownQuery, workspaces]);\n\n // `workspaces` is the search result while the search is delegated, so the selected workspace\n // can drop out of it mid-query. The cell keeps naming the workspace you are actually in.\n const lastSelected = useRef<BitkitUISideNavWorkspaceProps>(undefined);\n const matched = workspaces.find((workspace) => workspace.value === currentValue);\n if (matched) lastSelected.current = matched;\n const selected = matched ?? (lastSelected.current?.value === currentValue ? lastSelected.current : undefined);\n\n return (\n <Select.Root\n collection={collection}\n css={styles.workspaceRoot}\n ids={{ label: labelId }}\n onOpenChange={(details) => {\n setMenuOpen(details.open);\n if (!details.open) setOwnQuery('');\n onOpenChange?.(details.open);\n }}\n onValueChange={(details) => {\n setCurrentValue(details.value[0]);\n onValueChange?.(details.value[0]);\n }}\n // Tucked into the rail rather than hung off it: inset from the rail's edge and lapping\n // over the workspace cell, so the menu reads as the cell unfolding.\n positioning={\n isMobile\n ? { gutter: 0, offset: { crossAxis: 16 }, overflowPadding: 16 }\n : { gutter: -8, offset: { crossAxis: 16 }, placement: 'bottom-start' }\n }\n size=\"lg\"\n value={currentValue === undefined ? [] : [currentValue]}\n >\n <Select.Control css={styles.workspaceControl}>\n <Select.HiddenSelect />\n <Select.Trigger\n ref={ref}\n unstyled\n aria-labelledby={`${labelId} ${valueId}`}\n {...rest}\n css={[styles.workspace, css]}\n >\n <BitkitAvatar\n borderColor=\"border/minimal\"\n borderWidth=\"1\"\n name={selected?.label ?? ''}\n size=\"40\"\n src={selected?.avatar ?? ''}\n variant=\"image\"\n />\n <Box css={styles.workspaceTextBlock}>\n <Text css={styles.workspaceLabel} id={labelId}>\n {label}\n </Text>\n <Text css={styles.workspaceValue} id={valueId}>\n {selected?.label}\n </Text>\n </Box>\n <Select.Indicator css={styles.workspaceChevron}>\n <IconChevronDown />\n </Select.Indicator>\n </Select.Trigger>\n </Select.Control>\n {/*\n In the drawer the menu goes to the panel rather than the document: the panel scrolls\n under its own header, so a menu left in the flow would travel with the rows and be\n clipped by them, and anywhere outside the panel is outside the dialog's focus trap.\n */}\n <Portal container={isMobile ? menuContainerRef : undefined}>\n <Select.Positioner>\n <BitkitSelectMenu\n collection={collection}\n contentProps={{ css: styles.workspaceMenu }}\n emptyHelperText={emptyHelperText}\n emptyLabel={emptyLabel}\n errorHelperText={errorHelperText}\n errorLabel={errorLabel}\n isLoading={isLoading}\n onScrolledToBottom={onScrolledToBottom}\n onSearchChange={hasSearch ? (onSearchChange ?? setOwnQuery) : undefined}\n searchPlaceholder=\"Search your workspaces\"\n searchValue={query}\n size=\"lg\"\n >\n {children}\n </BitkitSelectMenu>\n </Select.Positioner>\n </Portal>\n </Select.Root>\n );\n },\n);\n\nUISideNavWorkspaceSelector.displayName = 'BitkitUISideNav.WorkspaceSelector';\n\nexport default UISideNavWorkspaceSelector;\n"],"mappings":";;;;;;;;;;;;;AA4DA,IAAM,6BAA6B,YAChC,OAAO,QAAQ;CACd,MAAM,EACJ,UACA,KACA,iBACA,YACA,iBACA,YACA,WACA,QAAQ,aACR,cACA,oBACA,gBACA,eACA,aACA,OACA,YACA,GAAG,SACD;CAEJ,MAAM,EAAE,UAAU,kBAAkB,aAAa,WAAW,oBAAoB;CAChF,MAAM,KAAK,MAAM;CACjB,MAAM,UAAU,GAAG,GAAG;CACtB,MAAM,UAAU,GAAG,GAAG;CACtB,MAAM,EAAE,aAAa,UAAU,EAAE,aAAa,OAAO,CAAC;CAEtD,MAAM,CAAC,UAAU,eAAe,SAAS,EAAE;CAC3C,MAAM,CAAC,cAAc,mBAAmB,qBAAyC;EAC/E,cAAc,KAAA;EACd;CACF,CAAC;CAKD,MAAM,oBAAoB,mBAAmB,KAAA;CAC7C,MAAM,QAAQ,oBAAoB,cAAc;CAChD,MAAM,YAAY,qBAAqB,WAAW,UAAA;CAElD,MAAM,aAAa,cAAc;EAK/B,MAAM,SAJU,WAAW,WAAW,QAAQ,cAAc,SAAS,UAAU,OAAO,QAAQ,CAAC,IAAI,WAAA,CAI3C,KAAK,eAAe;GAC1E,GAAG;GACH,QAAQ,UAAU,UAAU;EAC9B,EAAE;EAEF,OAAO,qBAAqB,EAAE,MAAM,CAAC;CACvC,GAAG;EAAC;EAAU;EAAU;CAAU,CAAC;CAInC,MAAM,eAAe,OAAsC,KAAA,CAAS;CACpE,MAAM,UAAU,WAAW,MAAM,cAAc,UAAU,UAAU,YAAY;CAC/E,IAAI,SAAS,aAAa,UAAU;CACpC,MAAM,WAAW,YAAY,aAAa,SAAS,UAAU,eAAe,aAAa,UAAU,KAAA;CAEnG,OACE,qBAAC,OAAO,MAAR;EACc;EACZ,KAAK,OAAO;EACZ,KAAK,EAAE,OAAO,QAAQ;EACtB,eAAe,YAAY;GACzB,YAAY,QAAQ,IAAI;GACxB,IAAI,CAAC,QAAQ,MAAM,YAAY,EAAE;GACjC,eAAe,QAAQ,IAAI;EAC7B;EACA,gBAAgB,YAAY;GAC1B,gBAAgB,QAAQ,MAAM,EAAE;GAChC,gBAAgB,QAAQ,MAAM,EAAE;EAClC;EAGA,aACE,WACI;GAAE,QAAQ;GAAG,QAAQ,EAAE,WAAW,GAAG;GAAG,iBAAiB;EAAG,IAC5D;GAAE,QAAQ;GAAI,QAAQ,EAAE,WAAW,GAAG;GAAG,WAAW;EAAe;EAEzE,MAAK;EACL,OAAO,iBAAiB,KAAA,IAAY,CAAC,IAAI,CAAC,YAAY;EArBxD,UAAA,CAuBE,qBAAC,OAAO,SAAR;GAAgB,KAAK,OAAO;GAA5B,UAAA,CACE,oBAAC,OAAO,cAAR,CAAsB,CAAA,GACtB,qBAAC,OAAO,SAAR;IACO;IACL,UAAA;IACA,mBAAiB,GAAG,QAAQ,GAAG;IAC/B,GAAI;IACJ,KAAK,CAAC,OAAO,WAAW,GAAG;IAL7B,UAAA;KAOE,oBAAC,cAAD;MACE,aAAY;MACZ,aAAY;MACZ,MAAM,UAAU,SAAS;MACzB,MAAK;MACL,KAAK,UAAU,UAAU;MACzB,SAAQ;KACT,CAAA;KACD,qBAAC,KAAD;MAAK,KAAK,OAAO;MAAjB,UAAA,CACE,oBAAC,MAAD;OAAM,KAAK,OAAO;OAAgB,IAAI;OACnC,UAAA;MACG,CAAA,GACN,oBAAC,MAAD;OAAM,KAAK,OAAO;OAAgB,IAAI;OACnC,UAAA,UAAU;MACP,CAAA,CACH;;KACL,oBAAC,OAAO,WAAR;MAAkB,KAAK,OAAO;MAC5B,UAAA,oBAAC,iBAAD,CAAkB,CAAA;KACF,CAAA;IACJ;GACF,CAAA,CAAA;EAMhB,CAAA,GAAA,oBAAC,QAAD;GAAQ,WAAW,WAAW,mBAAmB,KAAA;GAC/C,UAAA,oBAAC,OAAO,YAAR,EAAA,UACE,oBAAC,kBAAD;IACc;IACZ,cAAc,EAAE,KAAK,OAAO,cAAc;IACzB;IACL;IACK;IACL;IACD;IACS;IACpB,gBAAgB,YAAa,kBAAkB,cAAe,KAAA;IAC9D,mBAAkB;IAClB,aAAa;IACb,MAAK;IAEJ;GACe,CAAA,EACD,CAAA;EACb,CAAA,CACG;;AAEjB,CACF;AAEA,2BAA2B,cAAc"}
1
+ {"version":3,"file":"UISideNavWorkspaceSelector.js","names":[],"sources":["../../../lib/components/BitkitUISideNav/UISideNavWorkspaceSelector.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { createListCollection } from '@chakra-ui/react/collection';\nimport { useControllableState } from '@chakra-ui/react/hooks';\nimport { useFilter } from '@chakra-ui/react/locale';\nimport { Portal } from '@chakra-ui/react/portal';\nimport { Select, type SelectTriggerProps } from '@chakra-ui/react/select';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactNode, useId, useMemo, useRef, useState } from 'react';\n\nimport { IconChevronDown } from '../../icons';\nimport BitkitAvatar from '../BitkitAvatar/BitkitAvatar';\nimport BitkitSelectMenu, {\n type BitkitSelectMenuEmptyStateProps,\n type BitkitSelectMenuErrorStateProps,\n type BitkitSelectMenuItemProps,\n type BitkitSelectMenuScrollProps,\n type BitkitSelectMenuSearchProps,\n} from '../BitkitSelectMenu/BitkitSelectMenu';\nimport { type BitkitSelectMenuActionChild } from '../BitkitSelectMenu/BitkitSelectMenuAction';\nimport { useUISideNavContext } from './UISideNavContext';\n\nexport type BitkitUISideNavWorkspaceProps = Pick<BitkitSelectMenuItemProps, 'label' | 'value'> & {\n /**\n * The workspace's logo. Leave it off and the avatar falls back to the initials of `label` —\n * a workspace always has one, unlike a plain menu item.\n */\n avatar?: string;\n /** Replaces the initials when the workspace has no logo, or its logo fails to load. Decorative (`aria-hidden`): `label` names the workspace. */\n avatarFallback?: ReactNode;\n};\n\n/**\n * The list length at which the design adds the search input. Below it the menu is short enough\n * to read at a glance and a search field would only be in the way. Exported for the stories,\n * which label each list by the side of this it falls on — not part of the package's surface.\n */\nexport const searchableFrom = 6;\n\nexport type BitkitUISideNavWorkspaceSelectorProps = Omit<SelectTriggerProps, 'children' | 'onChange'> & {\n /**\n * A single `BitkitSelectMenuAction` shown under the list, typically \"Create workspace\". Its\n * presence is the toggle — there is no boolean for it.\n */\n children?: BitkitSelectMenuActionChild;\n isLoading?: boolean;\n /** The small uppercase caption above the workspace name. */\n label?: string;\n /**\n * Fires when the menu opens and when it closes — where a list loaded on demand belongs. The\n * menu also opens from the keyboard, which fires no click on the trigger.\n */\n onOpenChange?: (open: boolean) => void;\n onValueChange?: (value: string) => void;\n /** The selected workspace's `value`. Leave it off to let the selector track the choice itself. */\n value?: string;\n workspaces: Array<BitkitUISideNavWorkspaceProps>;\n // The copy is the design's, not the consumer's, so the placeholder is not on the way through.\n} & Omit<BitkitSelectMenuSearchProps, 'searchPlaceholder'> &\n BitkitSelectMenuScrollProps &\n BitkitSelectMenuEmptyStateProps &\n BitkitSelectMenuErrorStateProps;\n\nconst UISideNavWorkspaceSelector = forwardRef<HTMLButtonElement, BitkitUISideNavWorkspaceSelectorProps>(\n (props, ref) => {\n const {\n children,\n css,\n emptyHelperText,\n emptyLabel,\n errorHelperText,\n errorLabel,\n isLoading,\n label = 'Workspace',\n onOpenChange,\n onScrolledToBottom,\n onSearchChange,\n onValueChange,\n searchValue,\n value,\n workspaces,\n ...rest\n } = props;\n\n const { isMobile, menuContainerRef, setMenuOpen, styles } = useUISideNavContext();\n const id = useId();\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const { contains } = useFilter({ sensitivity: 'base' });\n\n const [ownQuery, setOwnQuery] = useState('');\n const [currentValue, setCurrentValue] = useControllableState<string | undefined>({\n defaultValue: undefined,\n value,\n });\n\n // A consumer that passes `onSearchChange` is searching somewhere we cannot see — a server, a\n // paged list — so `workspaces` is already the result and its length says nothing about how\n // many there are. They keep the input and the filtering; otherwise both are ours.\n const isSearchDelegated = onSearchChange !== undefined;\n const query = isSearchDelegated ? searchValue : ownQuery;\n const hasSearch = isSearchDelegated || workspaces.length >= searchableFrom;\n\n const collection = useMemo(() => {\n const matches = ownQuery ? workspaces.filter((workspace) => contains(workspace.label, ownQuery)) : workspaces;\n\n // `null` rather than nothing for a workspace without a logo: that is what tells the menu to\n // draw the initials, where an absent `avatar` would leave the row with no avatar at all.\n const items: Array<BitkitSelectMenuItemProps> = matches.map((workspace) => ({\n ...workspace,\n avatar: workspace.avatar ?? null,\n }));\n\n return createListCollection({ items });\n }, [contains, ownQuery, workspaces]);\n\n // `workspaces` is the search result while the search is delegated, so the selected workspace\n // can drop out of it mid-query. The cell keeps naming the workspace you are actually in.\n const lastSelected = useRef<BitkitUISideNavWorkspaceProps>(undefined);\n const matched = workspaces.find((workspace) => workspace.value === currentValue);\n if (matched) lastSelected.current = matched;\n const selected = matched ?? (lastSelected.current?.value === currentValue ? lastSelected.current : undefined);\n\n return (\n <Select.Root\n collection={collection}\n css={styles.workspaceRoot}\n ids={{ label: labelId }}\n onOpenChange={(details) => {\n setMenuOpen(details.open);\n if (!details.open) setOwnQuery('');\n onOpenChange?.(details.open);\n }}\n onValueChange={(details) => {\n setCurrentValue(details.value[0]);\n onValueChange?.(details.value[0]);\n }}\n // Tucked into the rail rather than hung off it: inset from the rail's edge and lapping\n // over the workspace cell, so the menu reads as the cell unfolding.\n positioning={\n isMobile\n ? { gutter: 0, offset: { crossAxis: 16 }, overflowPadding: 16 }\n : { gutter: -8, offset: { crossAxis: 16 }, placement: 'bottom-start' }\n }\n size=\"lg\"\n value={currentValue === undefined ? [] : [currentValue]}\n >\n <Select.Control css={styles.workspaceControl}>\n <Select.HiddenSelect />\n <Select.Trigger\n ref={ref}\n unstyled\n aria-labelledby={`${labelId} ${valueId}`}\n {...rest}\n css={[styles.workspace, css]}\n >\n <BitkitAvatar\n borderColor=\"border/minimal\"\n borderWidth=\"1\"\n fallback={selected?.avatarFallback}\n name={selected?.label ?? ''}\n size=\"40\"\n src={selected?.avatar ?? ''}\n variant=\"image\"\n />\n <Box css={styles.workspaceTextBlock}>\n <Text css={styles.workspaceLabel} id={labelId}>\n {label}\n </Text>\n <Text css={styles.workspaceValue} id={valueId}>\n {selected?.label}\n </Text>\n </Box>\n <Select.Indicator css={styles.workspaceChevron}>\n <IconChevronDown />\n </Select.Indicator>\n </Select.Trigger>\n </Select.Control>\n {/*\n In the drawer the menu goes to the panel rather than the document: the panel scrolls\n under its own header, so a menu left in the flow would travel with the rows and be\n clipped by them, and anywhere outside the panel is outside the dialog's focus trap.\n */}\n <Portal container={isMobile ? menuContainerRef : undefined}>\n <Select.Positioner>\n <BitkitSelectMenu\n collection={collection}\n contentProps={{ css: styles.workspaceMenu }}\n emptyHelperText={emptyHelperText}\n emptyLabel={emptyLabel}\n errorHelperText={errorHelperText}\n errorLabel={errorLabel}\n isLoading={isLoading}\n onScrolledToBottom={onScrolledToBottom}\n onSearchChange={hasSearch ? (onSearchChange ?? setOwnQuery) : undefined}\n searchPlaceholder=\"Search your workspaces\"\n searchValue={query}\n size=\"lg\"\n >\n {children}\n </BitkitSelectMenu>\n </Select.Positioner>\n </Portal>\n </Select.Root>\n );\n },\n);\n\nUISideNavWorkspaceSelector.displayName = 'BitkitUISideNav.WorkspaceSelector';\n\nexport default UISideNavWorkspaceSelector;\n"],"mappings":"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"}
@@ -39,19 +39,24 @@ declare const controlButtonRecipe: import('@chakra-ui/react').RecipeDefinition<{
39
39
  };
40
40
  size: {
41
41
  xxs: {
42
- padding: "4";
42
+ width: "24";
43
+ height: "24";
43
44
  };
44
45
  xs: {
45
- padding: "8";
46
+ width: "32";
47
+ height: "32";
46
48
  };
47
49
  sm: {
48
- padding: "4";
50
+ width: "32";
51
+ height: "32";
49
52
  };
50
53
  md: {
51
- padding: "8";
54
+ width: "40";
55
+ height: "40";
52
56
  };
53
57
  lg: {
54
- padding: "12";
58
+ width: "48";
59
+ height: "48";
55
60
  };
56
61
  };
57
62
  }>;
@@ -8,6 +8,8 @@ var controlButtonRecipe = defineRecipe({
8
8
  justifyContent: "center",
9
9
  borderRadius: "4",
10
10
  cursor: "pointer",
11
+ flexShrink: "0",
12
+ padding: 0,
11
13
  background: "transparent",
12
14
  _disabled: {
13
15
  cursor: "not-allowed",
@@ -51,11 +53,26 @@ var controlButtonRecipe = defineRecipe({
51
53
  }
52
54
  },
53
55
  size: {
54
- xxs: { padding: "4" },
55
- xs: { padding: "8" },
56
- sm: { padding: "4" },
57
- md: { padding: "8" },
58
- lg: { padding: "12" }
56
+ xxs: {
57
+ width: "24",
58
+ height: "24"
59
+ },
60
+ xs: {
61
+ width: "32",
62
+ height: "32"
63
+ },
64
+ sm: {
65
+ width: "32",
66
+ height: "32"
67
+ },
68
+ md: {
69
+ width: "40",
70
+ height: "40"
71
+ },
72
+ lg: {
73
+ width: "48",
74
+ height: "48"
75
+ }
59
76
  }
60
77
  },
61
78
  defaultVariants: {
@@ -1 +1 @@
1
- {"version":3,"file":"ControlButton.recipe.js","names":[],"sources":["../../../lib/theme/recipes/ControlButton.recipe.ts"],"sourcesContent":["import { defineRecipe } from '@chakra-ui/react/styled-system';\n\nconst controlButtonRecipe = defineRecipe({\n className: 'control-button',\n base: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '4',\n cursor: 'pointer',\n background: 'transparent',\n _disabled: {\n cursor: 'not-allowed',\n _hover: {\n background: 'transparent',\n },\n _active: {\n background: 'transparent',\n },\n },\n },\n variants: {\n isDanger: {\n false: {\n color: 'button/control/fg',\n _hover: {\n background: 'button/control/bg-hover',\n color: 'button/control/fg-hover',\n _active: {\n background: 'button/control/bg-active',\n color: 'button/control/fg-active',\n },\n },\n _active: {\n background: 'button/control/bg-active',\n color: 'button/control/fg-active',\n },\n _disabled: {\n color: 'button/control/fg-disabled!',\n },\n },\n true: {\n color: 'button/control-danger/fg',\n _hover: {\n background: 'button/control-danger/bg-hover',\n color: 'button/control-danger/fg-hover',\n _active: {\n background: 'button/control-danger/bg-active',\n color: 'button/control-danger/fg-active',\n },\n },\n _active: {\n background: 'button/control-danger/bg-active',\n color: 'button/control-danger/fg-active',\n },\n _disabled: {\n color: 'button/control-danger/fg-disabled!',\n },\n },\n },\n size: {\n xxs: { padding: '4' },\n xs: { padding: '8' },\n sm: { padding: '4' },\n md: { padding: '8' },\n lg: { padding: '12' },\n },\n },\n defaultVariants: {\n isDanger: false,\n size: 'sm',\n },\n});\n\nexport default controlButtonRecipe;\n"],"mappings":";;AAEA,IAAM,sBAAsB,aAAa;CACvC,WAAW;CACX,MAAM;EACJ,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,cAAc;EACd,QAAQ;EACR,YAAY;EACZ,WAAW;GACT,QAAQ;GACR,QAAQ,EACN,YAAY,cACd;GACA,SAAS,EACP,YAAY,cACd;EACF;CACF;CACA,UAAU;EACR,UAAU;GACR,OAAO;IACL,OAAO;IACP,QAAQ;KACN,YAAY;KACZ,OAAO;KACP,SAAS;MACP,YAAY;MACZ,OAAO;KACT;IACF;IACA,SAAS;KACP,YAAY;KACZ,OAAO;IACT;IACA,WAAW,EACT,OAAO,8BACT;GACF;GACA,MAAM;IACJ,OAAO;IACP,QAAQ;KACN,YAAY;KACZ,OAAO;KACP,SAAS;MACP,YAAY;MACZ,OAAO;KACT;IACF;IACA,SAAS;KACP,YAAY;KACZ,OAAO;IACT;IACA,WAAW,EACT,OAAO,qCACT;GACF;EACF;EACA,MAAM;GACJ,KAAK,EAAE,SAAS,IAAI;GACpB,IAAI,EAAE,SAAS,IAAI;GACnB,IAAI,EAAE,SAAS,IAAI;GACnB,IAAI,EAAE,SAAS,IAAI;GACnB,IAAI,EAAE,SAAS,KAAK;EACtB;CACF;CACA,iBAAiB;EACf,UAAU;EACV,MAAM;CACR;AACF,CAAC"}
1
+ {"version":3,"file":"ControlButton.recipe.js","names":[],"sources":["../../../lib/theme/recipes/ControlButton.recipe.ts"],"sourcesContent":["import { defineRecipe } from '@chakra-ui/react/styled-system';\n\nconst controlButtonRecipe = defineRecipe({\n className: 'control-button',\n base: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '4',\n cursor: 'pointer',\n flexShrink: '0',\n padding: 0,\n background: 'transparent',\n _disabled: {\n cursor: 'not-allowed',\n _hover: {\n background: 'transparent',\n },\n _active: {\n background: 'transparent',\n },\n },\n },\n variants: {\n isDanger: {\n false: {\n color: 'button/control/fg',\n _hover: {\n background: 'button/control/bg-hover',\n color: 'button/control/fg-hover',\n _active: {\n background: 'button/control/bg-active',\n color: 'button/control/fg-active',\n },\n },\n _active: {\n background: 'button/control/bg-active',\n color: 'button/control/fg-active',\n },\n _disabled: {\n color: 'button/control/fg-disabled!',\n },\n },\n true: {\n color: 'button/control-danger/fg',\n _hover: {\n background: 'button/control-danger/bg-hover',\n color: 'button/control-danger/fg-hover',\n _active: {\n background: 'button/control-danger/bg-active',\n color: 'button/control-danger/fg-active',\n },\n },\n _active: {\n background: 'button/control-danger/bg-active',\n color: 'button/control-danger/fg-active',\n },\n _disabled: {\n color: 'button/control-danger/fg-disabled!',\n },\n },\n },\n // Fixed sizes, not padding around the icon: a padded button stretches to the height of a\n // taller sibling in a flex row.\n size: {\n xxs: { width: '24', height: '24' },\n xs: { width: '32', height: '32' },\n sm: { width: '32', height: '32' },\n md: { width: '40', height: '40' },\n lg: { width: '48', height: '48' },\n },\n },\n defaultVariants: {\n isDanger: false,\n size: 'sm',\n },\n});\n\nexport default controlButtonRecipe;\n"],"mappings":";;AAEA,IAAM,sBAAsB,aAAa;CACvC,WAAW;CACX,MAAM;EACJ,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,cAAc;EACd,QAAQ;EACR,YAAY;EACZ,SAAS;EACT,YAAY;EACZ,WAAW;GACT,QAAQ;GACR,QAAQ,EACN,YAAY,cACd;GACA,SAAS,EACP,YAAY,cACd;EACF;CACF;CACA,UAAU;EACR,UAAU;GACR,OAAO;IACL,OAAO;IACP,QAAQ;KACN,YAAY;KACZ,OAAO;KACP,SAAS;MACP,YAAY;MACZ,OAAO;KACT;IACF;IACA,SAAS;KACP,YAAY;KACZ,OAAO;IACT;IACA,WAAW,EACT,OAAO,8BACT;GACF;GACA,MAAM;IACJ,OAAO;IACP,QAAQ;KACN,YAAY;KACZ,OAAO;KACP,SAAS;MACP,YAAY;MACZ,OAAO;KACT;IACF;IACA,SAAS;KACP,YAAY;KACZ,OAAO;IACT;IACA,WAAW,EACT,OAAO,qCACT;GACF;EACF;EAGA,MAAM;GACJ,KAAK;IAAE,OAAO;IAAM,QAAQ;GAAK;GACjC,IAAI;IAAE,OAAO;IAAM,QAAQ;GAAK;GAChC,IAAI;IAAE,OAAO;IAAM,QAAQ;GAAK;GAChC,IAAI;IAAE,OAAO;IAAM,QAAQ;GAAK;GAChC,IAAI;IAAE,OAAO;IAAM,QAAQ;GAAK;EAClC;CACF;CACA,iBAAiB;EACf,UAAU;EACV,MAAM;CACR;AACF,CAAC"}
@@ -27,7 +27,11 @@ var avatarSlotRecipe = defineSlotRecipe({
27
27
  height: "100%",
28
28
  objectFit: "cover"
29
29
  },
30
- fallback: {},
30
+ fallback: { "&:has(> *)": {
31
+ width: "100%",
32
+ height: "100%",
33
+ overflow: "hidden"
34
+ } },
31
35
  icon: {},
32
36
  badge: {
33
37
  width: 16,
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Avatar.recipe.ts"],"sourcesContent":["import { avatarAnatomy } from '@chakra-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nconst avatarSlotRecipe = defineSlotRecipe({\n slots: [...avatarAnatomy.keys(), 'icon', 'badge', 'badgeContainer'],\n className: 'avatar',\n base: {\n root: {\n alignItems: 'center',\n border: '1px solid',\n borderColor: 'border/minimal',\n display: 'inline-flex',\n flexShrink: '0',\n justifyContent: 'center',\n position: 'relative',\n userSelect: 'none',\n verticalAlign: 'top',\n '&:has([data-part=image][data-state=visible])': {\n backgroundColor: 'transparent',\n },\n },\n image: {\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n },\n fallback: {},\n icon: {},\n badge: {\n width: 16,\n height: 16,\n },\n badgeContainer: {\n display: 'flex',\n boxShadow: 'elevation/sm',\n position: 'absolute',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'text/secondary',\n backgroundColor: 'background/primary',\n },\n },\n\n variants: {\n size: {\n '24': {\n root: { width: 24, height: 24, borderRadius: 4 },\n image: { borderRadius: 'calc({radii.4} - 1px)' },\n fallback: { textStyle: 'comp/avatar/24', borderRadius: 'calc({radii.4} - 1px)' },\n icon: { width: 16, height: 16 },\n badgeContainer: { display: 'none' },\n },\n '32': {\n root: { width: 32, height: 32, borderRadius: 6 },\n image: { borderRadius: 'calc({radii.6} - 1px)' },\n fallback: { textStyle: 'comp/avatar/32', borderRadius: 'calc({radii.6} - 1px)' },\n icon: { width: 20, height: 20 },\n badgeContainer: { width: 16, height: 16, bottom: -5, right: -5, borderRadius: 4 },\n },\n '40': {\n root: { width: 40, height: 40, borderRadius: 8 },\n image: { borderRadius: 'calc({radii.8} - 1px)' },\n fallback: { textStyle: 'comp/avatar/40', borderRadius: 'calc({radii.8} - 1px)' },\n icon: { width: 24, height: 24 },\n badgeContainer: { width: 16, height: 16, bottom: -2, right: -2, borderRadius: 4 },\n },\n '48': {\n root: { width: 48, height: 48, borderRadius: '10' },\n image: { borderRadius: 'calc({radii.10} - 1px)' },\n fallback: { textStyle: 'comp/avatar/48', borderRadius: 'calc({radii.10} - 1px)' },\n icon: { width: 32, height: 32 },\n badgeContainer: { width: 24, height: 24, bottom: -7, right: -7, borderRadius: '50%' },\n },\n '64': {\n root: { width: 64, height: 64, borderRadius: 12 },\n image: { borderRadius: 'calc({radii.12} - 1px)' },\n fallback: { textStyle: 'comp/avatar/64', borderRadius: 'calc({radii.12} - 1px)' },\n icon: { width: 48, height: 48 },\n badgeContainer: { width: 24, height: 24, bottom: -5, right: -5, borderRadius: '50%' },\n },\n '128': {\n root: { width: 128, height: 128, borderRadius: 16 },\n image: { borderRadius: 'calc({radii.16} - 1px)' },\n fallback: { textStyle: 'comp/avatar/128', borderRadius: 'calc({radii.16} - 1px)' },\n icon: { width: 96, height: 96 },\n badgeContainer: { display: 'none' },\n },\n },\n\n variant: {\n circle: {\n root: { borderRadius: '50%' },\n image: { borderRadius: '50%' },\n fallback: { borderRadius: '50%' },\n },\n rounded: {},\n },\n\n colorVariant: {\n neutral: { root: { color: 'color/neutral/strong', backgroundColor: 'color/neutral/subtle' } },\n purple: { root: { color: 'color/purple/strong', backgroundColor: 'color/purple/subtle' } },\n indigo: { root: { color: 'color/indigo/strong', backgroundColor: 'color/indigo/subtle' } },\n blue: { root: { color: 'color/blue/strong', backgroundColor: 'color/blue/subtle' } },\n green: { root: { color: 'color/green/strong', backgroundColor: 'color/green/subtle' } },\n yellow: { root: { color: 'color/yellow/strong', backgroundColor: 'color/yellow/subtle' } },\n red: { root: { color: 'color/red/strong', backgroundColor: 'color/red/subtle' } },\n orange: { root: { color: 'color/orange/strong', backgroundColor: 'color/orange/subtle' } },\n turquoise: { root: { color: 'color/turquoise/strong', backgroundColor: 'color/turquoise/subtle' } },\n },\n },\n\n defaultVariants: {\n colorVariant: 'neutral',\n size: '32',\n variant: 'circle',\n },\n});\n\nexport default avatarSlotRecipe;\n"],"mappings":"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"}
1
+ {"version":3,"file":"Avatar.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/Avatar.recipe.ts"],"sourcesContent":["import { avatarAnatomy } from '@chakra-ui/react/anatomy';\nimport { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nconst avatarSlotRecipe = defineSlotRecipe({\n slots: [...avatarAnatomy.keys(), 'icon', 'badge', 'badgeContainer'],\n className: 'avatar',\n base: {\n root: {\n alignItems: 'center',\n border: '1px solid',\n borderColor: 'border/minimal',\n display: 'inline-flex',\n flexShrink: '0',\n justifyContent: 'center',\n position: 'relative',\n userSelect: 'none',\n verticalAlign: 'top',\n '&:has([data-part=image][data-state=visible])': {\n backgroundColor: 'transparent',\n },\n },\n image: {\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n },\n fallback: {\n '&:has(> *)': {\n width: '100%',\n height: '100%',\n overflow: 'hidden',\n },\n },\n icon: {},\n badge: {\n width: 16,\n height: 16,\n },\n badgeContainer: {\n display: 'flex',\n boxShadow: 'elevation/sm',\n position: 'absolute',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'text/secondary',\n backgroundColor: 'background/primary',\n },\n },\n\n variants: {\n size: {\n '24': {\n root: { width: 24, height: 24, borderRadius: 4 },\n image: { borderRadius: 'calc({radii.4} - 1px)' },\n fallback: { textStyle: 'comp/avatar/24', borderRadius: 'calc({radii.4} - 1px)' },\n icon: { width: 16, height: 16 },\n badgeContainer: { display: 'none' },\n },\n '32': {\n root: { width: 32, height: 32, borderRadius: 6 },\n image: { borderRadius: 'calc({radii.6} - 1px)' },\n fallback: { textStyle: 'comp/avatar/32', borderRadius: 'calc({radii.6} - 1px)' },\n icon: { width: 20, height: 20 },\n badgeContainer: { width: 16, height: 16, bottom: -5, right: -5, borderRadius: 4 },\n },\n '40': {\n root: { width: 40, height: 40, borderRadius: 8 },\n image: { borderRadius: 'calc({radii.8} - 1px)' },\n fallback: { textStyle: 'comp/avatar/40', borderRadius: 'calc({radii.8} - 1px)' },\n icon: { width: 24, height: 24 },\n badgeContainer: { width: 16, height: 16, bottom: -2, right: -2, borderRadius: 4 },\n },\n '48': {\n root: { width: 48, height: 48, borderRadius: '10' },\n image: { borderRadius: 'calc({radii.10} - 1px)' },\n fallback: { textStyle: 'comp/avatar/48', borderRadius: 'calc({radii.10} - 1px)' },\n icon: { width: 32, height: 32 },\n badgeContainer: { width: 24, height: 24, bottom: -7, right: -7, borderRadius: '50%' },\n },\n '64': {\n root: { width: 64, height: 64, borderRadius: 12 },\n image: { borderRadius: 'calc({radii.12} - 1px)' },\n fallback: { textStyle: 'comp/avatar/64', borderRadius: 'calc({radii.12} - 1px)' },\n icon: { width: 48, height: 48 },\n badgeContainer: { width: 24, height: 24, bottom: -5, right: -5, borderRadius: '50%' },\n },\n '128': {\n root: { width: 128, height: 128, borderRadius: 16 },\n image: { borderRadius: 'calc({radii.16} - 1px)' },\n fallback: { textStyle: 'comp/avatar/128', borderRadius: 'calc({radii.16} - 1px)' },\n icon: { width: 96, height: 96 },\n badgeContainer: { display: 'none' },\n },\n },\n\n variant: {\n circle: {\n root: { borderRadius: '50%' },\n image: { borderRadius: '50%' },\n fallback: { borderRadius: '50%' },\n },\n rounded: {},\n },\n\n colorVariant: {\n neutral: { root: { color: 'color/neutral/strong', backgroundColor: 'color/neutral/subtle' } },\n purple: { root: { color: 'color/purple/strong', backgroundColor: 'color/purple/subtle' } },\n indigo: { root: { color: 'color/indigo/strong', backgroundColor: 'color/indigo/subtle' } },\n blue: { root: { color: 'color/blue/strong', backgroundColor: 'color/blue/subtle' } },\n green: { root: { color: 'color/green/strong', backgroundColor: 'color/green/subtle' } },\n yellow: { root: { color: 'color/yellow/strong', backgroundColor: 'color/yellow/subtle' } },\n red: { root: { color: 'color/red/strong', backgroundColor: 'color/red/subtle' } },\n orange: { root: { color: 'color/orange/strong', backgroundColor: 'color/orange/subtle' } },\n turquoise: { root: { color: 'color/turquoise/strong', backgroundColor: 'color/turquoise/subtle' } },\n },\n },\n\n defaultVariants: {\n colorVariant: 'neutral',\n size: '32',\n variant: 'circle',\n },\n});\n\nexport default avatarSlotRecipe;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit-v2",
3
3
  "private": false,
4
- "version": "0.3.498",
4
+ "version": "0.3.500",
5
5
  "description": "Bitrise Design System Components built with Chakra UI V3",
6
6
  "keywords": [
7
7
  "react",