@baseline-ui/mcp 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +74 -0
- package/dist/data/component-docs.json +69 -0
- package/dist/data/component-source.json +75 -0
- package/dist/data/component-types.json +75 -0
- package/dist/data/components.json +586 -0
- package/dist/data/docs.json +1 -0
- package/dist/data/getting-started.json +3 -0
- package/dist/data/icons.json +638 -0
- package/dist/data/react-aria-components.json +44 -0
- package/dist/data/tokens-metadata.json +54 -0
- package/dist/data/usage-examples.json +42 -0
- package/dist/index.js +395 -0
- package/package.json +45 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Accordion": "import React, { useEffect, useMemo } from \"react\";\nimport { FocusScope } from \"react-aria\";\n\nimport { AccordionBase } from \"./AccordionBase\";\n\nimport type { Key } from \"react\";\nimport type { AccordionProps } from \"./Accordion.types\";\n\nexport const AccordionContext = React.createContext<{\n expandedKeys: Set<Key>;\n setExpandedKeys: React.Dispatch<React.SetStateAction<Set<Key>>>;\n disabledKeys?: Set<Key>;\n headers: Map<number, React.RefObject<HTMLButtonElement>>;\n expansionMode?: \"single\" | \"multiple\";\n}>({\n expandedKeys: new Set(),\n setExpandedKeys: () => {},\n disabledKeys: new Set(),\n headers: new Map(),\n expansionMode: \"multiple\",\n});\n\nexport const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(\n (\n {\n children,\n disabledKeys,\n defaultExpandedKeys = new Set(),\n expandedKeys: _expandedKeys,\n expansionMode: _expansionMode = \"multiple\",\n onChange,\n className,\n style,\n },\n ref,\n ) => {\n const [expandedKeys, setExpandedKeys] =\n React.useState<Set<Key>>(defaultExpandedKeys);\n const headers = React.useRef<\n Map<number, React.RefObject<HTMLButtonElement>>\n >(new Map()).current;\n const [expansionMode, setExpansionMode] = React.useState<\n \"single\" | \"multiple\"\n >(_expansionMode);\n\n useEffect(() => {\n setExpansionMode(_expansionMode);\n }, [_expansionMode]);\n\n useEffect(() => {\n onChange?.(expandedKeys);\n }, [expandedKeys, onChange]);\n\n const accordionContextValue = useMemo(\n () => ({\n expandedKeys: _expandedKeys || expandedKeys,\n setExpandedKeys: _expandedKeys ? () => {} : setExpandedKeys,\n disabledKeys,\n headers,\n expansionMode,\n }),\n [_expandedKeys, expandedKeys, disabledKeys, headers, expansionMode],\n );\n\n return (\n <AccordionContext.Provider value={accordionContextValue}>\n <FocusScope>\n <AccordionBase className={className} style={style} ref={ref}>\n {children}\n </AccordionBase>\n </FocusScope>\n </AccordionContext.Provider>\n );\n },\n);\n\nAccordion.displayName = \"Accordion\";\n",
|
|
3
|
+
"ActionButton": "import { CaretDownIcon } from \"@baseline-ui/icons/8\";\nimport { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport { mergeProps } from \"react-aria\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { chevronCn } from \"../ActionIconButton/ActionIconButton.css\";\nimport { useCustomButton } from \"../shared/buttons\";\nimport { buttonCn, buttonIconCn } from \"./ActionButton.css\";\n\nimport type { ActionButtonProps } from \"./ActionButton.types\";\n\nconst iconSizeMap = {\n sm: 16,\n md: 20,\n lg: 24,\n};\n\nconst ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProps>(\n (\n {\n label,\n style,\n className,\n iconStart: IconStart,\n iconEnd: IconEnd,\n variant = \"primary\",\n elementType: ElementType = \"button\",\n isDisabled,\n size = \"sm\",\n \"data-block-id\": blockId,\n \"data-block-class\": blockGroup,\n ...props\n },\n ref,\n ) => {\n const {\n buttonProps,\n isPressed,\n ref: _ref,\n isFocusVisible,\n isFocused,\n isHovered,\n uiStateOptions,\n } = useCustomButton({\n ...props,\n elementType: ElementType,\n isDisabled,\n });\n\n const iconSize = iconSizeMap[size];\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": blockId,\n \"data-block-class\": blockGroup,\n \"data-disabled\": isDisabled,\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n \"data-hovered\": isHovered,\n \"data-pressed\": isPressed,\n });\n\n return (\n <ElementType\n {...mergeProps(buttonProps, dataAttrs)}\n className={classNames(\n buttonCn({\n isDisabled: !!isDisabled,\n isFocusVisible,\n isHovered,\n variant,\n size,\n }),\n \"BaselineUI-ActionButton\",\n typeof className === \"function\"\n ? className(uiStateOptions)\n : className,\n )}\n style={typeof style === \"function\" ? style(uiStateOptions) : style}\n ref={mergeRefs(ref, _ref)}\n >\n {!!IconStart && (\n <IconStart\n size={iconSize}\n className={buttonIconCn({\n variant,\n isDisabled,\n isFocusVisible,\n isHovered,\n })}\n />\n )}\n {label}\n {!!IconEnd && (\n <IconEnd\n size={iconSize}\n className={buttonIconCn({\n variant,\n isDisabled,\n isFocusVisible,\n isHovered,\n })}\n />\n )}\n {variant === \"popover\" && (\n <CaretDownIcon size={8} className={chevronCn} />\n )}\n </ElementType>\n );\n },\n);\n\nActionButton.displayName = \"ActionButton\";\n\nexport { ActionButton };\n",
|
|
4
|
+
"ActionGroup": "import { useActionGroup, useActionGroupItem } from \"@react-aria/actiongroup\";\nimport { PressResponder } from \"@react-aria/interactions\";\nimport { useObjectRef } from \"@react-aria/utils\";\nimport React, { useMemo } from \"react\";\nimport { mergeProps } from \"react-aria\";\nimport { useListState } from \"react-stately\";\n\nimport { classNames, invariant } from \"../../utils\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { Box } from \"../Box\";\nimport { useCollectionChildren } from \"../shared/collection\";\nimport { Tooltip } from \"../Tooltip\";\nimport {\n actionGroupIconCn,\n actionGroupItemSelectedCn,\n} from \"./ActionGroup.css\";\n\nimport type { ListItem } from \"../shared/types/List\";\nimport type { ListOption } from \"../ListBox\";\nimport type { ListState, Node } from \"react-stately\";\nimport type { ActionGroupProps } from \"./ActionGroup.types\";\n\nexport const ActionGroup = React.forwardRef<HTMLDivElement, ActionGroupProps>(\n (\n {\n className,\n selectionMode,\n style,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n tooltipProps,\n icon: Icon,\n renderActionItem,\n ...props\n },\n ref,\n ) => {\n const divRef = useObjectRef(ref);\n\n const _children = useCollectionChildren();\n\n const state = useListState({\n ...props,\n selectionMode,\n suppressTextValueWarning: true,\n children: _children,\n });\n\n const { actionGroupProps } = useActionGroup(\n {\n ...props,\n selectionMode,\n children: _children,\n },\n state,\n divRef,\n );\n\n const actionGroupContent = [...state.collection].map((item) => {\n invariant(item.value);\n\n return (\n <ActionGroupItem\n key={item.key}\n item={item}\n state={state}\n isDisabled={props.isDisabled}\n onAction={props.onAction}\n tooltipProps={tooltipProps}\n renderActionItem={renderActionItem}\n />\n );\n });\n\n return Icon ? (\n <Box\n {...actionGroupProps}\n display=\"flex\"\n flexDirection=\"row\"\n gap=\"md\"\n alignItems=\"center\"\n className={classNames(\"BaselineUI-ActionGroup\", className)}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={divRef}\n >\n <Icon aria-hidden=\"true\" size={24} className={actionGroupIconCn} />\n <Box display=\"flex\" flexDirection=\"row\" gap=\"xs\" flex={1}>\n {actionGroupContent}\n </Box>\n </Box>\n ) : (\n <Box\n {...actionGroupProps}\n display=\"flex\"\n flexDirection=\"row\"\n gap=\"xs\"\n className={classNames(\"BaselineUI-ActionGroup\", className)}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={divRef}\n >\n {actionGroupContent}\n </Box>\n );\n },\n);\n\nActionGroup.displayName = \"ActionGroup\";\n\nexport const ActionGroupItem: React.FC<ActionGroupItemProps> = ({\n item,\n state,\n tooltipProps,\n renderActionItem,\n onAction,\n isDisabled,\n}) => {\n const { buttonProps } = useActionGroupItem({ key: item.key }, state);\n\n const isDisabledFinal =\n isDisabled || state.selectionManager.isDisabled(item.key);\n const isSelected = state.selectionManager.isSelected(item.key);\n\n invariant(item.value);\n\n const { icon } = item.value as ListOption;\n\n invariant(icon);\n\n const _tooltipProps = useMemo(() => {\n invariant(item.value);\n\n return typeof tooltipProps === \"function\"\n ? tooltipProps(item.value)\n : tooltipProps;\n }, [tooltipProps, item.value]);\n\n return (\n <PressResponder\n {...mergeProps(buttonProps, {\n onPress: () => {\n onAction?.(item.key);\n },\n })}\n >\n <Tooltip\n text={item.textValue}\n size=\"sm\"\n variant=\"inverse\"\n includeArrow={false}\n offset={4}\n {..._tooltipProps}\n >\n {renderActionItem?.(item, {\n isSelected,\n isDisabled: isDisabledFinal,\n }) || (\n <ActionIconButton\n variant=\"tertiary\"\n icon={icon}\n aria-label={item.textValue}\n size=\"sm\"\n isDisabled={isDisabledFinal}\n className={classNames(\n {\n [actionGroupItemSelectedCn]: isSelected,\n },\n \"BaselineUI-ActionGroup-Item\",\n )}\n />\n )}\n </Tooltip>\n </PressResponder>\n );\n};\n\ninterface ActionGroupItemProps\n extends Pick<\n ActionGroupProps,\n \"tooltipProps\" | \"renderActionItem\" | \"onAction\" | \"isDisabled\"\n > {\n item: Node<ListItem>;\n state: ListState<ListItem>;\n}\n",
|
|
5
|
+
"ActionIconButton": "import { CaretDownIcon } from \"@baseline-ui/icons/8\";\nimport { mergeRefs, useObjectRef } from \"@react-aria/utils\";\nimport React, { useEffect } from \"react\";\nimport { mergeProps } from \"react-aria\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { useButtonTooltipProps, useCustomButton } from \"../shared/buttons\";\nimport { chevronCn, iconButtonCn } from \"./ActionIconButton.css\";\nimport { Tooltip } from \"../Tooltip\";\n\nimport type { ActionIconButtonProps } from \"./ActionIconButton.types\";\n\nconst ICON_SIZES = {\n xxs: 8,\n xs: 12,\n sm: 16,\n md: 20,\n lg: 24,\n};\n\nexport const ActionIconButton = React.forwardRef<\n HTMLButtonElement,\n ActionIconButtonProps\n>((props, ref) => {\n const buttonRef = useObjectRef(ref);\n\n const { tooltipProps } = useButtonTooltipProps(props, buttonRef);\n\n return tooltipProps ? (\n <Tooltip {...tooltipProps}>\n <ActionIconButtonCore {...props} ref={buttonRef} />\n </Tooltip>\n ) : (\n <ActionIconButtonCore {...props} ref={buttonRef} />\n );\n});\n\nActionIconButton.displayName = \"ActionIconButton\";\n\nconst ActionIconButtonCore = React.forwardRef<\n HTMLButtonElement,\n ActionIconButtonProps\n>(\n (\n {\n className,\n style,\n icon: Icon,\n size = \"md\",\n variant = \"primary\",\n UNSAFE_isVisuallyInteractiveOnly: UNSAFE_VISUALLY_INTERACTIVE,\n elementType: ElementType = UNSAFE_VISUALLY_INTERACTIVE ? \"div\" : \"button\",\n isExcludedFromRovingFocus,\n ...props\n },\n ref,\n ) => {\n const ariaLabel = props[\"aria-label\"];\n const ariaLabelledby = props[\"aria-labelledby\"];\n\n useEffect(() => {\n if (!ariaLabel && !ariaLabelledby) {\n console.warn(\n \"An `ActionIconButton` requires an aria-label or aria-labelledby prop.\",\n );\n }\n }, [ariaLabel, ariaLabelledby]);\n\n const {\n buttonProps,\n isPressed,\n ref: _ref,\n isFocusVisible,\n isFocused,\n isHovered,\n uiStateOptions,\n } = useCustomButton({\n ...props,\n elementType: ElementType,\n });\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": props[\"data-block-id\"],\n \"data-block-class\": props[\"data-block-class\"],\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n \"data-disabled\": props.isDisabled,\n \"data-hovered\": isHovered,\n \"data-pressed\": isPressed,\n \"data-bui-avoid-focus\": isExcludedFromRovingFocus,\n });\n\n return (\n <ElementType\n {...mergeProps(buttonProps, dataAttrs)}\n className={classNames(\n iconButtonCn({\n variant,\n size,\n isDisabled: !!props.isDisabled,\n isFocusVisible,\n isHovered,\n }),\n \"BaselineUI-ActionIconButton\",\n typeof className === \"function\"\n ? className(uiStateOptions)\n : className,\n )}\n ref={mergeRefs(ref, _ref)}\n style={typeof style === \"function\" ? style(uiStateOptions) : style}\n {...(UNSAFE_VISUALLY_INTERACTIVE && {\n tabIndex: undefined,\n role: undefined,\n \"aria-label\": undefined,\n \"aria-labelledby\": undefined,\n \"aria-disabled\": undefined,\n })}\n >\n <Icon role=\"presentation\" size={ICON_SIZES[size]} />\n\n {variant === \"popover\" && (\n <CaretDownIcon size={8} className={chevronCn} />\n )}\n </ElementType>\n );\n },\n);\n\nActionIconButtonCore.displayName = \"ActionIconButtonCore\";\n",
|
|
6
|
+
"AlertDialog": "import { XIcon } from \"@baseline-ui/icons/20\";\nimport React from \"react\";\nimport { useId } from \"react-aria\";\n\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { ActionButton } from \"../ActionButton\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { Box } from \"../Box\";\nimport { Dialog } from \"../Dialog\";\nimport { Separator } from \"../Separator\";\nimport { Text } from \"../Text\";\nimport { TextInput } from \"../TextInput\";\nimport {\n alertFooterButtonCn,\n alertHeaderCn,\n minHeightCn,\n} from \"./AlertDialog.css\";\nimport { resolveAriaLabelAttrs } from \"../../utils/resolveAriaLabelAttrs\";\n\nimport type { AlertDialogProps } from \"./AlertDialog.types\";\n\nexport const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(\n (\n {\n className,\n style,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n message,\n title,\n primaryActionLabel,\n cancelLabel,\n textInputProps,\n icon: Icon,\n iconColor,\n showCloseButton = true,\n onPrimaryAction,\n onCancel,\n autoFocusButton,\n isPrimaryActionDisabled,\n ...ariaProps\n },\n ref,\n ) => {\n const labelId = useId();\n const descriptionId = useId();\n const { formatMessage } = useI18n();\n\n const _title = title && (\n <Text type=\"title\" size=\"sm\" elementType=\"h3\" id={labelId}>\n {title}\n </Text>\n );\n\n const resolvedAriaProps = resolveAriaLabelAttrs({\n ariaProps,\n fallbackLabelId: title ? labelId : undefined,\n fallbackDescriptionId: message ? descriptionId : undefined,\n });\n\n return (\n <Dialog\n {...resolvedAriaProps}\n className={className}\n style={style}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n size=\"sm\"\n variant=\"primary\"\n role=\"alertdialog\"\n ref={ref}\n >\n <div\n className={alertHeaderCn({\n hasTitleAndIcon: !!(Icon && title),\n hasNoCloseBtn: !showCloseButton,\n })}\n >\n <Box\n display=\"flex\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n flexDirection=\"row\"\n >\n {Icon ? <Icon style={{ color: iconColor }} size={24} /> : null}\n\n {!Icon && _title}\n\n {showCloseButton ? (\n <ActionIconButton\n icon={XIcon}\n variant=\"secondary\"\n size=\"md\"\n aria-label={formatMessage(messages.close)}\n onPress={onCancel}\n />\n ) : null}\n </Box>\n\n {Icon ? _title : null}\n </div>\n {message ? (\n <Box\n elementType=\"section\"\n className={minHeightCn}\n paddingX=\"lg\"\n paddingBottom=\"lg\"\n typography=\"body.sm.regular\"\n color=\"text.primary\"\n id={descriptionId}\n >\n {message}\n </Box>\n ) : null}\n {textInputProps ? (\n <Box paddingX=\"lg\" paddingBottom=\"lg\">\n <TextInput {...textInputProps} />\n </Box>\n ) : null}\n\n {cancelLabel || primaryActionLabel ? (\n <>\n <Separator />\n\n <Box padding=\"lg\" display=\"flex\" gap=\"xl\">\n {cancelLabel ? (\n <ActionButton\n variant=\"secondary\"\n className={alertFooterButtonCn}\n label={cancelLabel}\n onPress={onCancel}\n autoFocus={autoFocusButton === \"cancel\"}\n />\n ) : null}\n\n {primaryActionLabel ? (\n <ActionButton\n className={alertFooterButtonCn}\n label={primaryActionLabel}\n onPress={onPrimaryAction}\n autoFocus={autoFocusButton === \"primary\"}\n isDisabled={isPrimaryActionDisabled}\n />\n ) : null}\n </Box>\n </>\n ) : null}\n </Dialog>\n );\n },\n);\n\nAlertDialog.displayName = \"AlertDialog\";\n\nconst messages = defineMessages({\n close: {\n id: \"close\",\n defaultMessage: \"Close\",\n description: \"Close button label\",\n },\n});\n",
|
|
7
|
+
"AudioPlayer": "import { PauseIcon, PlayIcon } from \"@baseline-ui/icons/24\";\nimport React from \"react\";\nimport { VisuallyHidden } from \"react-aria\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { useMedia } from \"../../hooks/useMedia\";\nimport { Slider } from \"../Slider\";\nimport { ToggleIconButton } from \"../ToggleIconButton\";\nimport {\n audioPlayerCn,\n mediaPlayerCn,\n mediaProgressCn,\n} from \"./AudioPlayer.css\";\n\nimport type { AudioPlayerProps } from \"./AudioPlayer.types\";\n\nexport const AudioPlayer = React.forwardRef<HTMLDivElement, AudioPlayerProps>(\n (\n {\n className,\n style,\n size = \"lg\",\n sources,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...arialLabelProps\n },\n ref,\n ) => {\n const audioRef = React.useRef<HTMLAudioElement>(null);\n\n const {\n isPlaying,\n progress,\n duration,\n togglePlay,\n seek,\n formattedDuration,\n formattedCurrentTime,\n setProgress,\n setIsSliderDragging,\n } = useMedia(audioRef);\n\n const { formatMessage } = useI18n();\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-state\": isPlaying ? \"playing\" : \"paused\",\n \"data-loaded\": duration > 0,\n });\n\n return (\n <div\n {...dataAttrs}\n {...arialLabelProps}\n className={classNames(\n audioPlayerCn,\n \"BaselineUI-AudioPlayer\",\n className,\n )}\n role=\"group\"\n style={style}\n ref={ref}\n >\n <VisuallyHidden>\n <audio ref={audioRef}>\n {sources.map(({ url, type }) => (\n <source src={url} type={type} key={url} />\n ))}\n </audio>\n </VisuallyHidden>\n\n <ToggleIconButton\n icon={isPlaying ? PauseIcon : PlayIcon}\n variant=\"toolbar\"\n isSelected={isPlaying}\n size={size}\n aria-label={\n isPlaying\n ? formatMessage(messages.pause)\n : formatMessage(messages.play)\n }\n style={{ borderRadius: \"50%\" }}\n onPress={togglePlay}\n />\n\n <div className={mediaPlayerCn}>\n <span aria-hidden={true}>{formattedCurrentTime}</span>\n\n <Slider\n aria-label={formatMessage(messages.audioTimeline)}\n value={progress}\n minValue={0}\n maxValue={100}\n className={mediaProgressCn}\n onChange={(value) => {\n setProgress(value);\n setIsSliderDragging(true);\n }}\n onChangeEnd={(value) => {\n seek(value);\n setIsSliderDragging(false);\n }}\n />\n\n <span aria-hidden={true}>{formattedDuration}</span>\n </div>\n </div>\n );\n },\n);\n\nAudioPlayer.displayName = \"AudioPlayer\";\n\nconst messages = defineMessages({\n play: {\n id: \"play\",\n defaultMessage: \"Play\",\n },\n pause: {\n id: \"pause\",\n defaultMessage: \"Pause\",\n },\n audioTimeline: {\n id: \"audioTimeline\",\n defaultMessage: \"Audio timeline\",\n },\n});\n",
|
|
8
|
+
"Avatar": "import { AvatarIcon } from \"@baseline-ui/icons/16\";\nimport { AvatarIcon as AvatarIcon24 } from \"@baseline-ui/icons/24\";\nimport React from \"react\";\n\nimport { classNames } from \"../../utils\";\nimport { notificationCn, userCn, userImgCn } from \"./Avatar.css\";\n\nimport type { AvatarProps } from \"./Avatar.types\";\n\nconst sizeToIcon = {\n sm: AvatarIcon,\n md: AvatarIcon24,\n};\n\nexport const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>(\n (\n {\n className,\n style,\n size = \"md\",\n icon: Icon = sizeToIcon[size],\n name,\n imgSrc,\n showInitials,\n isDisabled,\n hasNotifications,\n imgLoading,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n },\n ref,\n ) => {\n const initials = name\n ?.split(\" \")\n .map((n) => n[0])\n .join(\"\");\n\n return (\n <span\n className={classNames(\n userCn({ size, isDisabled }),\n \"BaselineUI-Avatar\",\n className,\n )}\n data-disabled={isDisabled}\n ref={ref}\n style={style}\n aria-disabled={isDisabled}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n >\n {imgSrc ? (\n <img\n src={imgSrc}\n alt={name}\n loading={imgLoading}\n className={userImgCn}\n />\n ) : null}\n {!showInitials && !imgSrc && <Icon size={size === \"sm\" ? 16 : 24} />}\n {showInitials && !imgSrc\n ? size === \"sm\"\n ? initials[0]\n : initials\n : null}\n\n {hasNotifications ? (\n <div data-testid=\"notification\" className={notificationCn} />\n ) : null}\n </span>\n );\n },\n);\n\nAvatar.displayName = \"Avatar\";\n",
|
|
9
|
+
"Box": "import { sprinkles } from \"@baseline-ui/tokens\";\nimport React, { useMemo } from \"react\";\n\nimport { classNames } from \"../../utils\";\n\nimport type { BoxProps, SprinkleProps } from \"./Box.types\";\n\nfunction isSprinkleKey(key: unknown): key is keyof SprinkleProps {\n return sprinkles.properties.has(key as keyof SprinkleProps);\n}\n\nexport const Box = React.forwardRef<HTMLDivElement, BoxProps>(\n ({ elementType = \"div\", children, ...rest }, ref) => {\n const ElementType = elementType as React.ElementType;\n\n const { sprinkleProps, domProps } = useMemo(() => {\n const sprinkleProps = {} as SprinkleProps;\n const domProps = {} as React.ComponentPropsWithoutRef<\"div\">;\n\n for (const key in rest) {\n if (isSprinkleKey(key)) {\n // @ts-expect-error Too complicated union\n sprinkleProps[key] = rest[key];\n } else {\n domProps[key] = rest[key];\n }\n }\n\n return { sprinkleProps, domProps };\n }, [rest]);\n\n return (\n <ElementType\n {...domProps}\n className={classNames(\n sprinkles(sprinkleProps),\n \"BaselineUI-Box\",\n domProps.className,\n )}\n ref={ref}\n >\n {children}\n </ElementType>\n );\n },\n);\n\nBox.displayName = \"Box\";\n",
|
|
10
|
+
"ButtonSelect": "import { CaretDownIcon } from \"@baseline-ui/icons/8\";\nimport { PressResponder } from \"@react-aria/interactions\";\nimport { useObjectRef } from \"@react-aria/utils\";\nimport { useControlledState } from \"@react-stately/utils\";\nimport React, { useCallback, useMemo } from \"react\";\nimport { useHover } from \"react-aria\";\nimport { useSelectState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { Box } from \"../Box\";\nimport { Select } from \"../Select\";\nimport { SelectContext } from \"../Select/Select\";\nimport { ToggleButton } from \"../ToggleButton\";\nimport { toggleButtonCn } from \"../ToggleButton/ToggleButton.css\";\nimport { ToggleIconButton } from \"../ToggleIconButton\";\nimport { containerCn, selectCn, selectTriggerCn } from \"./ButtonSelect.css\";\nimport { Tooltip } from \"../Tooltip\";\nimport { ActionButton } from \"../ActionButton\";\nimport { ListCollectionBuilder } from \"../shared/components/ListCollectionBuilder\";\nimport {\n ListBoxItemContent,\n ListOptionContext,\n} from \"../UNSAFE_ListBox/ListBox\";\n\nimport type { ListItem } from \"../shared/types/List\";\nimport type { BaseCollection } from \"@react-aria/collections\";\nimport type { PressEvent } from \"@react-aria/interactions\";\nimport type { Key } from \"@react-types/shared\";\nimport type { SelectProps } from \"../Select\";\nimport type { ListOption } from \"../ListBox\";\nimport type { ButtonSelectProps } from \"./ButtonSelect.types\";\n\nconst ButtonSelectCore = React.forwardRef<\n HTMLDivElement,\n Omit<ButtonSelectProps, \"onPress\"> & {\n collection: BaseCollection<ListItem>;\n onButtonPress?: (e: PressEvent) => void;\n }\n>(\n (\n {\n className,\n style,\n items,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n hideLabel,\n size = \"md\",\n isDisabled,\n excludeFromTabOrder,\n onButtonPress,\n onButtonAction,\n isSelected,\n defaultSelected,\n onButtonSelectionChange,\n collection,\n moreAriaLabel,\n optionStyle,\n optionClassName,\n triggerClassName,\n triggerStyle,\n tooltipProps,\n buttonBehaviour = \"toggle\",\n ...rest\n },\n ref,\n ) => {\n const { formatMessage } = useI18n();\n\n const state = useSelectState({\n ...rest,\n collection,\n defaultValue: rest.defaultSelectedKey || items?.[0]?.id,\n children: undefined,\n selectionMode: \"single\",\n validationState: rest.validationState === \"error\" ? \"invalid\" : \"valid\",\n });\n\n const _buttonBehaviour = useMemo(() => {\n if (typeof buttonBehaviour === \"function\") {\n return buttonBehaviour(state.value as string);\n }\n return buttonBehaviour;\n }, [buttonBehaviour, state.value]);\n\n const [isButtonSelected, setIsButtonSelected] = useControlledState(\n isSelected,\n !!defaultSelected,\n (isSelected) => {\n onButtonSelectionChange?.({\n isSelected,\n selectedKey: state.value as string,\n });\n },\n );\n\n const { hoverProps, isHovered } = useHover({ isDisabled });\n\n const button = useMemo(() => {\n const selectedItem = state.collection.getItem(state.value as string);\n if (!selectedItem) return null;\n\n const buttonLabel =\n selectedItem?.textValue || selectedItem?.[\"aria-label\"];\n const _item = selectedItem?.value as ListOption;\n\n const Icon = _item?.icon;\n\n const isToggle = _buttonBehaviour === \"toggle\";\n const isButtonDisabled =\n isDisabled || state.disabledKeys.has(state.value as string);\n\n const sharedProps = {\n size,\n ...(isToggle && { isSelected: isButtonSelected }),\n onPress: (e: PressEvent) => {\n if (isToggle) {\n setIsButtonSelected(!isButtonSelected);\n }\n onButtonPress?.(e);\n onButtonAction?.({\n isSelected: isToggle ? !isButtonSelected : false,\n selectedKey: state.value as string,\n buttonBehaviour: _buttonBehaviour,\n });\n },\n isDisabled: isButtonDisabled,\n excludeFromTabOrder,\n className: classNames(\n typeof optionClassName === \"function\"\n ? optionClassName(_item, {\n isButton: true,\n isSelected: isButtonSelected,\n })\n : optionClassName,\n \"BaselineUI-ButtonSelect-Button\",\n ),\n style:\n typeof optionStyle === \"function\"\n ? optionStyle(_item, {\n isButton: true,\n isSelected: isButtonSelected,\n })\n : optionStyle,\n };\n\n if (hideLabel && Icon) {\n return (\n <Tooltip\n variant=\"inverse\"\n size=\"sm\"\n includeArrow={false}\n text={buttonLabel}\n placement=\"bottom start\"\n offset={4}\n {...(typeof tooltipProps === \"function\"\n ? tooltipProps(\"button\")\n : tooltipProps)}\n >\n {_buttonBehaviour === \"action\" ? (\n <ActionIconButton\n icon={Icon}\n aria-label={buttonLabel}\n variant=\"toolbar\"\n {...sharedProps}\n />\n ) : (\n <ToggleIconButton\n icon={Icon}\n aria-label={buttonLabel}\n variant=\"toolbar\"\n {...sharedProps}\n />\n )}\n </Tooltip>\n );\n } else {\n return _buttonBehaviour === \"toggle\" ? (\n <ToggleButton\n iconStart={Icon}\n label={buttonLabel}\n variant=\"toolbar\"\n {...sharedProps}\n />\n ) : (\n <ActionButton\n label={buttonLabel}\n variant=\"toolbar\"\n {...sharedProps}\n />\n );\n }\n }, [\n state.collection,\n state.value,\n state.disabledKeys,\n _buttonBehaviour,\n isDisabled,\n size,\n isButtonSelected,\n excludeFromTabOrder,\n optionClassName,\n optionStyle,\n hideLabel,\n onButtonPress,\n onButtonAction,\n setIsButtonSelected,\n tooltipProps,\n ]);\n\n const ariaLabel = rest[\"aria-label\"];\n\n const renderTrigger = useCallback<\n Exclude<SelectProps[\"renderTrigger\"], undefined>\n >(\n ({ buttonProps, ref }) => {\n delete buttonProps[\"aria-labelledby\"];\n\n const label =\n moreAriaLabel || `${formatMessage(messages.more)} ${ariaLabel}`;\n\n return (\n <Tooltip\n variant=\"inverse\"\n size=\"sm\"\n includeArrow={false}\n text={label}\n placement=\"bottom start\"\n offset={4}\n {...tooltipProps}\n >\n <PressResponder {...buttonProps}>\n <ActionIconButton\n aria-label={label}\n ref={ref}\n icon={CaretDownIcon}\n variant=\"toolbar\"\n size={size}\n className={({ isFocusVisible }) =>\n classNames(\n toggleButtonCn({\n isSelected:\n isButtonSelected && _buttonBehaviour === \"toggle\",\n isFocusVisible,\n }),\n selectTriggerCn,\n triggerClassName,\n \"BaselineUI-ButtonSelect-Trigger\",\n )\n }\n style={triggerStyle}\n isDisabled={isDisabled}\n excludeFromTabOrder={excludeFromTabOrder}\n />\n </PressResponder>\n </Tooltip>\n );\n },\n [\n ariaLabel,\n _buttonBehaviour,\n excludeFromTabOrder,\n formatMessage,\n isButtonSelected,\n isDisabled,\n moreAriaLabel,\n size,\n tooltipProps,\n triggerClassName,\n triggerStyle,\n ],\n );\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-hovered\": isHovered,\n \"data-selected\": isButtonSelected,\n \"data-disabled\": isDisabled,\n \"data-expanded\": state.isOpen,\n \"data-button-behaviour\": _buttonBehaviour,\n });\n\n const containerRef = useObjectRef(ref);\n\n const contextValue = useMemo(\n () => ({ state, popoverAnchorRef: containerRef }),\n [state, containerRef],\n );\n\n return (\n <SelectContext.Provider value={contextValue}>\n <Box\n {...hoverProps}\n role=\"group\"\n display=\"inline-flex\"\n flexDirection=\"row\"\n alignItems=\"center\"\n className={classNames(\n containerCn,\n \"BaselineUI-ButtonSelect\",\n className,\n )}\n aria-label={rest[\"aria-label\"]}\n aria-labelledby={rest[\"aria-labelledby\"]}\n aria-describedby={rest[\"aria-describedby\"]}\n aria-details={rest[\"aria-details\"]}\n style={style}\n ref={containerRef}\n {...dataAttrs}\n >\n {button}\n\n <Select\n isDisabled={isDisabled}\n placement=\"bottom start\"\n items={items}\n {...rest}\n variant=\"ghost\"\n renderTrigger={renderTrigger}\n className={classNames(selectCn, \"BaselineUI-ButtonSelect-Select\")}\n />\n </Box>\n </SelectContext.Provider>\n );\n },\n);\n\nButtonSelectCore.displayName = \"ButtonSelectCore\";\n\nexport const ButtonSelect = React.forwardRef<HTMLDivElement, ButtonSelectProps>(\n (\n {\n onPress,\n optionStyle,\n optionClassName,\n onSelectionChange,\n onOptionPress,\n ...props\n },\n ref,\n ) => {\n const onPressHandler = useCallback(\n (e: PressEvent, key: string) => {\n onOptionPress?.(e, key);\n // When clicking the selected option, react-aria won't call onSelectionChange\n // because the selection doesn't change, so we call it here\n if (props.selectedKey === key) {\n onSelectionChange?.(key);\n }\n },\n [onOptionPress, onSelectionChange, props.selectedKey],\n );\n\n const wrappedOnSelectionChange = useCallback(\n (key: Key | null) => {\n // Always call onSelectionChange when react-aria calls it (when selection changes)\n if (key) {\n onSelectionChange?.(key as string);\n }\n },\n [onSelectionChange],\n );\n\n const contextValue = useMemo(\n () => ({ onPress: onPressHandler }),\n [onPressHandler],\n );\n\n return (\n <ListOptionContext.Provider value={contextValue}>\n <ListCollectionBuilder\n items={props.items}\n listBoxProps={{\n renderOption: (item, itemProps) => (\n <ListBoxItemContent {...itemProps} item={item} />\n ),\n optionStyle: (item, props) =>\n typeof optionStyle === \"function\"\n ? optionStyle(item, {\n isButton: false,\n isSelected: props.isSelected,\n })\n : optionStyle,\n optionClassName: (item, props) =>\n typeof optionClassName === \"function\"\n ? optionClassName(item, {\n isButton: false,\n isSelected: props.isSelected,\n })\n : optionClassName,\n }}\n >\n {(collection) => (\n <ButtonSelectCore\n collection={collection}\n onButtonPress={onPress}\n onSelectionChange={wrappedOnSelectionChange}\n optionStyle={optionStyle}\n optionClassName={optionClassName}\n {...props}\n ref={ref}\n />\n )}\n </ListCollectionBuilder>\n </ListOptionContext.Provider>\n );\n },\n);\n\nButtonSelect.displayName = \"ButtonSelect\";\n\nconst messages = defineMessages({\n more: {\n id: \"more\",\n defaultMessage: \"More\",\n },\n});\n",
|
|
11
|
+
"Checkbox": "import React from \"react\";\nimport {\n VisuallyHidden,\n mergeProps,\n useCheckbox,\n useFocusRing,\n useHover,\n useObjectRef,\n} from \"react-aria\";\nimport { useToggleState } from \"react-stately\";\nimport { CheckmarkIcon, MinusIcon } from \"@baseline-ui/icons/12\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { checkBoxLabelCn, checkboxCn, checkboxIconCn } from \"./Checkbox.css\";\n\nimport type { CheckboxProps } from \"./Checkbox.types\";\n\nexport const Checkbox = React.forwardRef<HTMLLabelElement, CheckboxProps>(\n (\n {\n className,\n style,\n label,\n labelPosition = \"end\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const labelRef = useObjectRef(ref);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const state = useToggleState(rest);\n const { isFocusVisible, isFocused, focusProps } = useFocusRing();\n const { inputProps, isPressed, isDisabled, isSelected, isReadOnly } =\n useCheckbox({ ...rest, children: label }, state, inputRef);\n const { hoverProps, isHovered } = useHover(\n {\n isDisabled,\n },\n labelRef,\n );\n\n const dataAttrs = filterTruthyValues({\n \"data-disabled\": isDisabled,\n \"data-readonly\": isReadOnly,\n \"data-hovered\": isHovered,\n \"data-selected\": isSelected,\n \"data-pressed\": isPressed,\n \"data-indeterminate\": rest.isIndeterminate,\n \"data-invalid\": rest.isInvalid,\n \"data-focus-visible\": isFocusVisible,\n \"data-focused\": isFocused,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n });\n\n return (\n <label\n {...dataAttrs}\n className={classNames(\n checkboxCn({ labelPosition }),\n \"BaselineUI-Checkbox\",\n className,\n )}\n style={style}\n ref={labelRef}\n >\n <VisuallyHidden>\n <input {...mergeProps(inputProps, focusProps)} ref={inputRef} />\n </VisuallyHidden>\n\n <div\n {...hoverProps}\n className={checkboxIconCn({\n isSelected: isSelected && !rest.isIndeterminate,\n isIndeterminate: rest.isIndeterminate,\n isFocusVisible,\n isDisabled,\n isReadOnly,\n hasError: rest.isInvalid,\n isHovered,\n })}\n >\n {isSelected && !rest.isIndeterminate ? (\n <CheckmarkIcon size={12} />\n ) : null}\n {rest.isIndeterminate ? <MinusIcon size={12} /> : null}\n </div>\n {label ? (\n <span className={checkBoxLabelCn({ isDisabled })}>{label}</span>\n ) : null}\n </label>\n );\n },\n);\n\nCheckbox.displayName = \"Checkbox\";\n",
|
|
12
|
+
"ColorInput": "import { useControlledState } from \"@react-stately/utils\";\nimport React, { useMemo } from \"react\";\nimport { mergeProps, useId, useOverlayTrigger } from \"react-aria\";\nimport { useOverlayTriggerState } from \"react-stately\";\n\nimport _messages from \"./intl\";\nimport { classNames } from \"../../utils\";\nimport { Separator } from \"../Separator\";\nimport { Dialog } from \"../Dialog\";\nimport { CustomColors } from \"./CustomColors\";\nimport { ColorPresetList } from \"./ColorPresetList\";\nimport { ColorInputButton } from \"./ColorInputButton\";\nimport {\n colorInputCn,\n colorInputLabelCn,\n dialogCn,\n maxWidthCn,\n} from \"./ColorInput.css\";\nimport { defineMessages, useI18n, usePortalContainer } from \"../../hooks\";\nimport { PopoverContent } from \"../Popover/PopoverContent\";\nimport { Picker } from \"./Picker\";\nimport {\n FALLBACK_COLOR,\n NONE_ID,\n getParsedColor,\n getPresetsWithNone,\n} from \"./utils\";\nimport { useColorTrigger } from \"./hooks/useColorTrigger\";\n\nimport type Messages from \"./intl/en.json\";\nimport type { ColorInputProps } from \"./ColorInput.types\";\n\nexport const ColorInput = React.forwardRef<HTMLDivElement, ColorInputProps>(\n (\n {\n className,\n style,\n allowRemoval,\n allowAlpha = true,\n presets,\n labelPosition = \"top\",\n colorLabel = true,\n includePicker = true,\n onChange,\n onChangeEnd,\n defaultValue,\n storePickedColorKey = \"baselinePickedColor\",\n value,\n addColorButtonLabel,\n removeColorButtonLabel,\n customColorsLabel,\n onTriggerPress,\n renderTriggerButton = ({\n isOpen,\n color,\n ref,\n colorName,\n triggerProps,\n labelId,\n isIndeterminate,\n indeterminateIcon,\n }) => (\n <ColorInputButton\n {...mergeProps(triggerProps, {\n ...(rest.label\n ? { \"aria-labelledby\": labelId }\n : { \"aria-label\": rest[\"aria-label\"] }),\n })}\n ref={ref}\n isOpen={isOpen}\n isDisabled={rest.isDisabled}\n labelPosition={labelPosition}\n color={color}\n colorLabel={colorLabel}\n colorName={colorName}\n isIndeterminate={isIndeterminate}\n indeterminateIcon={indeterminateIcon}\n />\n ),\n pickerMode = \"active\",\n offset = 2,\n placement = labelPosition === \"start\" ? \"bottom end\" : \"bottom start\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n isIndeterminate,\n indeterminateIcon,\n ...rest\n },\n ref,\n ) => {\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const dialogRef = React.useRef<HTMLDivElement>(null);\n const state = useOverlayTriggerState(rest);\n const { triggerProps, overlayProps } = useOverlayTrigger(\n { type: \"listbox\" },\n state,\n triggerRef,\n );\n const _portalContainer = usePortalContainer(rest.portalContainer);\n\n const [color, setColor] = useControlledState(\n value === undefined ? undefined : getParsedColor(value),\n getParsedColor(defaultValue),\n (color) => onChange?.(color?.toFormat(\"rgba\") || null),\n );\n\n const { formatMessage } = useI18n<typeof Messages>(_messages);\n\n const _presets = useMemo(\n () => getPresetsWithNone(formatMessage(messages.noColor), presets),\n [presets, formatMessage],\n );\n\n const labelId = useId();\n\n const { colorName } = useColorTrigger({\n colorLabel,\n color,\n presets: _presets,\n isIndeterminate,\n });\n\n const presetsToShow = useMemo(\n () => _presets.filter((p) => allowRemoval || p.id !== NONE_ID),\n [_presets, allowRemoval],\n );\n\n const _addColorButtonLabel =\n addColorButtonLabel || formatMessage(messages.addColor);\n\n const _removeColorButtonLabel =\n removeColorButtonLabel || formatMessage(messages.removeColor);\n\n const _customColorsLabel =\n customColorsLabel || formatMessage(messages.customColors);\n\n const customColors = (\n <CustomColors\n color={color || FALLBACK_COLOR}\n setColor={(color) => {\n setColor(color);\n onChangeEnd?.(color?.toFormat(\"rgba\") || null);\n }}\n storePickedColorKey={storePickedColorKey}\n addColorButtonLabel={_addColorButtonLabel}\n removeColorButtonLabel={_removeColorButtonLabel}\n customColorsLabel={_customColorsLabel}\n pickerMode={pickerMode}\n allowAlpha={allowAlpha}\n isIndeterminate={!!isIndeterminate}\n />\n );\n\n return (\n <>\n <div\n className={classNames(\n { [colorInputCn]: labelPosition === \"start\" },\n \"BaselineUI-ColorInput-Trigger\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={ref}\n >\n {rest.label ? (\n <label\n id={labelId}\n className={colorInputLabelCn({ labelPosition })}\n >\n {rest.label}\n </label>\n ) : null}\n\n {renderTriggerButton({\n isOpen: state.isOpen,\n color,\n ref: triggerRef,\n colorName,\n triggerProps: mergeProps(triggerProps, {\n onPress: onTriggerPress,\n }),\n labelId,\n isIndeterminate,\n indeterminateIcon,\n })}\n </div>\n <PopoverContent\n {...rest}\n portalContainer={_portalContainer}\n placement={placement}\n state={state}\n offset={offset}\n className={classNames(maxWidthCn, \"BaselineUI-ColorInput-Popover\")}\n triggerRef={triggerRef}\n overlayProps={overlayProps}\n >\n <Dialog\n size=\"content\"\n className={dialogCn({\n includesCustomColorPicker: includePicker,\n })}\n ref={dialogRef}\n aria-labelledby={rest.label ? labelId : undefined}\n aria-label={rest[\"aria-label\"]}\n >\n {includePicker && pickerMode === \"active\" ? (\n <>\n <Picker\n color={color || FALLBACK_COLOR}\n setColor={setColor}\n allowAlpha={allowAlpha}\n onChangeEnd={(color) => {\n onChangeEnd?.(color?.toFormat(\"rgba\") || null);\n }}\n />\n\n {customColors}\n </>\n ) : null}\n\n {!!presetsToShow.length && (\n <>\n {includePicker && pickerMode === \"active\" ? (\n <Separator />\n ) : null}\n\n <ColorPresetList\n autoFocus={\n includePicker && pickerMode === \"active\" ? false : \"first\"\n }\n state={state}\n triggerRef={triggerRef}\n presets={presetsToShow}\n color={color}\n setColor={(color) => {\n setColor(color);\n onChangeEnd?.(color?.toFormat(\"rgba\") || null);\n }}\n isIndeterminate={!!isIndeterminate}\n />\n </>\n )}\n\n {includePicker && pickerMode === \"lazy\" ? customColors : null}\n </Dialog>\n </PopoverContent>\n </>\n );\n },\n);\n\nColorInput.displayName = \"ColorInput\";\n\nconst messages = defineMessages({\n addColor: {\n defaultMessage: \"Add color\",\n id: \"addColor\",\n },\n removeColor: {\n defaultMessage: \"Remove color\",\n id: \"removeColor\",\n },\n customColors: {\n defaultMessage: \"Custom colors\",\n id: \"customColors\",\n },\n noColor: {\n defaultMessage: \"No color\",\n id: \"noColor\",\n },\n transparent: {\n defaultMessage: \"Transparent\",\n id: \"transparent\",\n },\n});\n",
|
|
13
|
+
"ColorSwatch": "import { parseColor } from \"@react-stately/color\";\nimport React from \"react\";\nimport { Focusable, mergeProps, useHover } from \"react-aria\";\nimport { useColorSwatch } from \"@react-aria/color\";\nimport { HelpIcon } from \"@baseline-ui/icons/16\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { Box } from \"../Box\";\nimport { NONE_ID, getTransparentStyle } from \"../ColorInput/utils\";\nimport { Tooltip } from \"../Tooltip\";\nimport { useSharedTooltipProps } from \"../shared/tooltips\";\nimport { colorSwatchCn, indeterminateCn, noneCn } from \"./ColorSwatch.css\";\n\nimport type { ColorSwatchProps } from \"./ColorSwatch.types\";\n\nexport const ColorSwatch = React.forwardRef<HTMLDivElement, ColorSwatchProps>(\n (\n {\n color,\n isFocusVisible,\n isSelected,\n isInteractive,\n isDisabled,\n style,\n className,\n id,\n tooltip,\n isIndeterminate,\n indeterminateIcon: IndeterminateIcon = HelpIcon,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const parsed =\n color &&\n color !== NONE_ID &&\n (typeof color === \"string\" ? parseColor(color) : color);\n\n const { formatMessage } = useI18n();\n\n const isNone = !parsed;\n\n const { colorSwatchProps } = useColorSwatch({\n color: isNone ? \"#000000\" : color,\n });\n\n const { hoverProps, isHovered } = useHover({\n isDisabled: isDisabled || !isInteractive,\n });\n\n const dataAttrs = filterTruthyValues({\n \"data-focus-visible\": isFocusVisible,\n \"data-selected\": isSelected,\n \"data-hovered\": isHovered,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-disabled\": isDisabled,\n \"data-interactive\": isInteractive,\n \"data-indeterminate\": isIndeterminate,\n });\n\n const inferredTooltipContent = isNone\n ? formatMessage(messages.none)\n : colorSwatchProps[\"aria-label\"];\n\n // prefer provided aria-label over inferred one\n const tooltipContent = rest[\"aria-label\"] ?? inferredTooltipContent;\n\n const { tooltipProps } = useSharedTooltipProps({\n tooltip,\n label: tooltipContent,\n });\n\n const content = (\n <div\n {...mergeProps(hoverProps, dataAttrs, isNone ? {} : colorSwatchProps)}\n style={style}\n aria-label={\n isNone ? formatMessage(messages.none) : colorSwatchProps[\"aria-label\"]\n }\n role=\"img\"\n className={classNames(\n colorSwatchCn({\n removeBlendMode: isNone,\n isFocusVisible,\n isDisabled: !!isDisabled,\n isSelected: !!isSelected,\n isHovered,\n }),\n \"BaselineUI-ColorSwatch\",\n className,\n )}\n ref={ref}\n id={id}\n {...rest}\n >\n <Box\n display=\"inline-flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n borderRadius=\"full\"\n height=\"full\"\n width=\"full\"\n opacity={isDisabled ? \"medium\" : undefined}\n style={\n parsed && !isIndeterminate\n ? getTransparentStyle(parsed, 4)\n : undefined\n }\n >\n {isNone && !isIndeterminate ? <div className={noneCn} /> : null}\n {isIndeterminate && IndeterminateIcon ? (\n <IndeterminateIcon\n role=\"presentation\"\n size={16}\n className={indeterminateCn}\n aria-hidden=\"true\"\n />\n ) : null}\n </Box>\n </div>\n );\n\n return tooltipProps && !isDisabled ? (\n <Tooltip {...tooltipProps}>\n <Focusable>{content}</Focusable>\n </Tooltip>\n ) : (\n content\n );\n },\n);\n\nColorSwatch.displayName = \"ColorSwatch\";\n\nconst messages = defineMessages({\n none: {\n id: \"none\",\n defaultMessage: \"None\",\n },\n});\n",
|
|
14
|
+
"ColorSwatchPicker": "import { sprinkles } from \"@baseline-ui/tokens\";\nimport { filterDOMProps } from \"@react-aria/utils\";\nimport React, { useEffect, useMemo } from \"react\";\nimport { Focusable, useId } from \"react-aria\";\n\nimport { classNames } from \"../../utils\";\nimport { Box } from \"../Box\";\nimport { colorSwatchCn, groupContainerCn } from \"./ColorSwatchPicker.css\";\nimport { ColorSwatch } from \"../ColorSwatch\";\nimport { ListBox } from \"../ListBox\";\nimport { Text } from \"../Text\";\nimport { Tooltip } from \"../Tooltip\";\nimport { useSharedTooltipProps } from \"../shared/tooltips\";\n\nimport type { ListBoxProps } from \"../ListBox\";\nimport type { ColorSwatchPickerProps } from \"./ColorSwatchPicker.types\";\n\nexport const ColorSwatchPicker = React.forwardRef<\n HTMLDivElement,\n ColorSwatchPickerProps\n>(\n (\n {\n className,\n style,\n items,\n optionsContainerClassName,\n label,\n icon: Icon,\n value,\n defaultValue,\n onChange,\n isDisabled: _isDisabled,\n optionClassName,\n optionStyle,\n labelPosition = \"top\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n shouldFocusWrap,\n tooltip = true,\n ...rest\n },\n ref,\n ) => {\n const _items = useMemo(\n () =>\n items.map((item) => ({\n id: item.color,\n label: item.label,\n })),\n [items],\n );\n\n const labelId = useId();\n\n useEffect(() => {\n if (!label && !rest[\"aria-label\"] && !rest[\"aria-labelledby\"]) {\n console.warn(\n \"ColorSwatchPicker: Please provide a label or aria-label for accessibility.\",\n );\n }\n }, [label, rest]);\n\n return (\n <Box\n {...filterDOMProps(rest)}\n className={classNames(\"BaselineUI-ColorSwatchPicker\", className)}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n display=\"flex\"\n flexDirection={labelPosition === \"top\" ? \"column\" : \"row\"}\n alignItems={labelPosition === \"top\" ? \"flex-start\" : \"center\"}\n justifyContent=\"space-between\"\n gap=\"md\"\n style={style}\n ref={ref}\n >\n {Icon || label ? (\n <Box display=\"flex\" alignItems=\"center\" gap=\"md\">\n {Icon ? (\n <Icon\n aria-hidden=\"true\"\n size={24}\n className={sprinkles({ color: \"icon.primary\" })}\n />\n ) : null}\n {label ? (\n <Text id={labelId} type=\"label\" size=\"sm\">\n {label}\n </Text>\n ) : null}\n </Box>\n ) : null}\n\n <ListBox\n className={classNames(groupContainerCn, optionsContainerClassName)}\n selectionMode=\"single\"\n items={_items}\n orientation=\"horizontal\"\n defaultSelectedKeys={defaultValue ? [defaultValue] : undefined}\n disabledKeys={_isDisabled ? _items.map((item) => item.id) : undefined}\n isDisabled={_isDisabled}\n selectedKeys={value ? [value] : undefined}\n onSelectionChange={(keys) => {\n onChange?.([...keys][0] as string);\n }}\n optionClassName={optionClassName}\n optionStyle={optionStyle}\n renderOption={(...args) => RenderOption(...args, tooltip)}\n shouldFocusWrap={shouldFocusWrap}\n {...rest}\n aria-labelledby={label ? labelId : rest[\"aria-labelledby\"]}\n />\n </Box>\n );\n },\n);\n\nColorSwatchPicker.displayName = \"ColorSwatchPicker\";\n\ntype RenderOptionParameters = Parameters<\n Exclude<ListBoxProps[\"renderOption\"], undefined>\n>;\n\nconst RenderOption = (\n { key, textValue }: RenderOptionParameters[0],\n {\n isSelected,\n isFocusVisible,\n isDisabled,\n optionProps,\n }: RenderOptionParameters[1],\n ref: RenderOptionParameters[2],\n tooltip: ColorSwatchPickerProps[\"tooltip\"],\n) => {\n const { tooltipProps } = useSharedTooltipProps({\n label: textValue,\n tooltip,\n });\n\n return (\n <Tooltip {...tooltipProps}>\n <Focusable>\n <li\n {...optionProps}\n data-disabled={isDisabled}\n className={classNames(colorSwatchCn, optionProps.className)}\n ref={ref}\n >\n <ColorSwatch\n isFocusVisible={isFocusVisible}\n color={key as string}\n isSelected={isSelected}\n aria-label={textValue}\n isInteractive={true}\n isDisabled={isDisabled}\n tooltip={false}\n />\n </li>\n </Focusable>\n </Tooltip>\n );\n};\n",
|
|
15
|
+
"ComboBox": "import React, { useMemo } from \"react\";\nimport {\n mergeProps,\n useComboBox,\n useFilter,\n useFocus,\n useFocusRing,\n useHover,\n useKeyboard,\n useLabel,\n useObjectRef,\n usePress,\n} from \"react-aria\";\nimport { useComboBoxState } from \"react-stately\";\nimport { mergeRefs } from \"@react-aria/utils\";\nimport { ListBoxContext, ListStateContext } from \"react-aria-components\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { ActionButton, UNSAFE_ListBox as ListBox, Tooltip } from \"../\";\nimport { popoverContentCn } from \"../Select/Select.css\";\nimport {\n comboBoxCn,\n comboBoxInputCn,\n comboBoxInputIconCn,\n comboBoxInputWrapperCn,\n comboBoxLabelCn,\n comboBoxLabelContainerCn,\n comboBoxListBoxCn,\n comboBoxToggleCn,\n} from \"./ComboBox.css\";\nimport { getMessage } from \"../TextInput/utils/getMessage\";\nimport { getStatusIcon } from \"../TextInput/utils/getStatusIcon\";\nimport { PopoverContent } from \"../Popover/PopoverContent\";\nimport { useOnValueChange } from \"./useOnValueChange\";\nimport { useSharedTooltipProps } from \"../shared/tooltips\";\nimport { ListCollectionBuilder } from \"../shared/components/ListCollectionBuilder\";\n\nimport type { BaseCollection } from \"@react-aria/collections\";\nimport type { ListItem } from \"../shared/types/List\";\nimport type { DOMAttributes } from \"@react-types/shared\";\nimport type { ComboBoxProps } from \"./ComboBox.types\";\n\nexport const ComboBox = React.forwardRef<HTMLDivElement, ComboBoxProps>(\n (props, ref) => {\n return (\n <ListCollectionBuilder items={props.items}>\n {(collection) => (\n <ComboBoxInner {...props} collection={collection} ref={ref} />\n )}\n </ListCollectionBuilder>\n );\n },\n);\n\nComboBox.displayName = \"ComboBox\";\n\nconst ComboBoxInner = React.forwardRef<\n HTMLDivElement,\n ComboBoxProps & {\n collection: BaseCollection<ListItem>;\n }\n>(\n (\n {\n className,\n style,\n inputStyle,\n inputClassName,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n label,\n items,\n isInvalid = false,\n labelPosition = \"top\",\n validationState = \"valid\",\n variant = \"primary\",\n toggleLabel,\n renderOption,\n showIcon = true,\n filter,\n minValue,\n maxValue,\n inputType = \"text\",\n onInputSubmit,\n onValueChange,\n tooltip,\n collection,\n onTriggerPress,\n ...rest\n },\n ref,\n ) => {\n const divRef = useObjectRef(ref);\n const { contains } = useFilter({ sensitivity: \"base\" });\n\n const state = useComboBoxState({\n ...rest,\n defaultFilter: filter || contains,\n defaultItems: items,\n collection,\n children: undefined,\n validationState: isInvalid ? \"invalid\" : \"valid\",\n });\n\n // Setup refs and get props for child elements.\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listBoxRef = React.useRef<HTMLDivElement>(null);\n const popoverRef = React.useRef<HTMLDivElement>(null);\n const inputIconAndButtonWrapperRef = React.useRef<HTMLDivElement>(null);\n\n const { inputProps: _inputProps } = useOnValueChange(\n {\n onValueChange,\n allowsCustomValue: rest.allowsCustomValue,\n inputValue: rest.inputValue,\n },\n state,\n divRef,\n inputRef,\n popoverRef,\n );\n\n const {\n buttonProps,\n inputProps,\n listBoxProps,\n labelProps,\n errorMessageProps,\n descriptionProps,\n validationErrors,\n } = useComboBox(\n {\n ...rest,\n inputRef,\n defaultItems: items,\n buttonRef,\n listBoxRef,\n popoverRef,\n validationState: validationState === \"error\" ? \"invalid\" : \"valid\",\n },\n state,\n );\n\n const _errorMessage = rest.errorMessage || validationErrors.join(\" \");\n\n const message = getMessage({\n ...rest,\n errorMessage: _errorMessage,\n descriptionProps,\n errorMessageProps,\n labelPosition,\n });\n\n if (_errorMessage) {\n validationState = \"error\";\n }\n\n const { hoverProps, isHovered } = useHover({\n isDisabled: rest.isDisabled,\n });\n\n const { isFocused, focusProps, isFocusVisible } = useFocusRing();\n\n const Icon = getStatusIcon({\n isReadOnly: rest.isReadOnly,\n validationState,\n });\n\n const dataAttrs = filterTruthyValues({\n \"data-read-only\": rest.isReadOnly,\n \"data-disabled\": rest.isDisabled,\n \"data-validation-state\": validationState,\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n });\n\n // useLabel() helps in associating the label with the form field.\n const { labelProps: propsFromUseLabel, fieldProps } = useLabel({\n label,\n \"aria-label\": rest[\"aria-label\"],\n \"aria-labelledby\": rest[\"aria-labelledby\"],\n \"aria-describedby\": rest[\"aria-describedby\"],\n \"aria-details\": rest[\"aria-details\"],\n });\n\n const { keyboardProps } = useKeyboard({\n onKeyDown: (e) => {\n if (rest.allowsCustomValue && e.key === \"Enter\") {\n onInputSubmit?.(inputProps.value?.toString() ?? \"\");\n }\n },\n });\n\n const { focusProps: _focusProps } = useFocus({\n onBlur: () => {\n if (rest.allowsCustomValue && inputProps.value) {\n onInputSubmit?.(inputProps.value?.toString() ?? \"\");\n }\n },\n });\n\n const { tooltipProps } = useSharedTooltipProps({\n tooltip,\n label: rest[\"aria-label\"],\n });\n\n const { pressProps } = usePress({\n onPress: onTriggerPress,\n });\n\n const getTriggerElement = (options?: {\n triggerProps?: DOMAttributes;\n triggerRef?: React.RefObject<HTMLElement>;\n }) => {\n const { triggerProps, triggerRef } = options ?? {};\n\n return (\n <div\n className={classNames(\n comboBoxCn({\n labelPosition,\n }),\n className,\n \"BaselineUI-ComboBox\",\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={divRef}\n >\n {label || (labelPosition === \"start\" && message) ? (\n <div\n className={comboBoxLabelContainerCn({\n labelPosition,\n hasMessage: !!message,\n })}\n >\n <label\n {...mergeProps(labelProps, propsFromUseLabel)}\n className={comboBoxLabelCn({ isDisabled: rest.isDisabled })}\n >\n {label}\n </label>\n {labelPosition === \"start\" && message}\n </div>\n ) : null}\n <div\n {...hoverProps}\n className={comboBoxInputWrapperCn({\n validationState,\n isHovered,\n isFocused,\n variant,\n isReadOnly: !!rest.isReadOnly,\n isDisabled: !!rest.isDisabled,\n })}\n ref={inputIconAndButtonWrapperRef}\n >\n <input\n {...mergeProps(\n pressProps,\n triggerProps,\n inputProps,\n _inputProps,\n focusProps,\n dataAttrs,\n fieldProps,\n _focusProps,\n keyboardProps,\n inputType === \"number\"\n ? {\n type: \"number\",\n min: minValue,\n max: maxValue,\n onKeyDown: (e: KeyboardEvent) => {\n // In case of number input, disable increment/decrement of number using\n // arrow keys. This is done to prevent the number from changing when\n // the user is trying to open the dropdown using arrow keys.\n if (e.key === \"ArrowUp\" || e.key === \"ArrowDown\") {\n e.preventDefault();\n }\n },\n }\n : null,\n )}\n id={rest.id}\n ref={mergeRefs(\n triggerRef as React.RefObject<HTMLInputElement>,\n inputRef,\n )}\n className={classNames(\n comboBoxInputCn({\n isDisabled: rest.isDisabled,\n labelPosition,\n }),\n \"BaselineUI-ComboBox-Input\",\n inputClassName,\n )}\n style={inputStyle}\n />\n\n {Icon && showIcon ? (\n <Icon\n className={comboBoxInputIconCn({\n validationState,\n isReadOnly: rest.isReadOnly,\n })}\n size={16}\n />\n ) : null}\n\n <ActionButton\n {...buttonProps}\n onPressStart={(e) => {\n // This is hacky because we are using the onPressStart event to trigger the onTriggerPress event.\n // This is because the onPress event is not triggered when the user clicks on the button at times.\n // So for consistency, we are using the onPressStart event.\n buttonProps.onPressStart?.(e);\n onTriggerPress?.(e);\n }}\n ref={buttonRef}\n variant=\"popover\"\n label={toggleLabel}\n className={comboBoxToggleCn({\n isFocusVisible:\n !rest.isDisabled && !rest.isReadOnly && isFocusVisible,\n isHovered: !rest.isDisabled && !rest.isReadOnly && isHovered,\n isOpen: state.isOpen,\n })}\n />\n </div>\n\n {labelPosition === \"top\" && message}\n </div>\n );\n };\n\n return (\n <>\n {tooltipProps ? (\n <Tooltip {...tooltipProps}>{getTriggerElement}</Tooltip>\n ) : (\n getTriggerElement()\n )}\n\n <PopoverContent\n ref={popoverRef}\n triggerRef={inputIconAndButtonWrapperRef}\n state={state}\n // This adjusts the width caused due to the outline of the trigger\n crossOffset={-2}\n className=\"BaselineUI-ComboBox-Popover\"\n >\n <ListStateContext.Provider value={state}>\n <ListBoxContext.Provider\n value={useMemo(\n () => ({\n ...listBoxProps,\n ref: listBoxRef,\n }),\n [listBoxProps],\n )}\n >\n <ListBox\n renderOption={renderOption}\n selectionMode=\"single\"\n className={classNames(\n popoverContentCn,\n comboBoxListBoxCn,\n \"BaselineUI-ComboBox-ListBox\",\n )}\n />\n </ListBoxContext.Provider>\n </ListStateContext.Provider>\n </PopoverContent>\n </>\n );\n },\n);\n\nComboBoxInner.displayName = \"ComboBoxInner\";\n",
|
|
16
|
+
"DateField": "import React from \"react\";\nimport {\n VisuallyHidden,\n useDateField,\n useFocusRing,\n useLocale,\n} from \"react-aria\";\nimport { useDateFieldState } from \"react-stately\";\nimport { createCalendar } from \"@internationalized/date\";\n\nimport { classNames } from \"../../utils\";\nimport { getMessage } from \"../TextInput/utils/getMessage\";\nimport { SharedInput } from \"../shared/components/SharedInput/SharedInput\";\nimport { timeFieldCn } from \"../TimeField/TimeField.css\";\nimport { Box } from \"../Box/Box\";\nimport { DateTimeSegment } from \"../TimeField/DateTimeSegment\";\n\nimport type { DateFieldStateOptions } from \"react-stately\";\nimport type { DateFieldProps } from \"./DateField.types\";\n\nexport const DateField = React.forwardRef<HTMLDivElement, DateFieldProps>(\n (\n {\n className,\n labelPosition,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const { locale } = useLocale();\n\n const props = {\n ...rest,\n validationState: undefined,\n isInvalid: rest.validationState === \"error\" || undefined,\n } as DateFieldStateOptions;\n\n const state = useDateFieldState({\n ...props,\n locale,\n createCalendar,\n });\n\n const wrapperRef = React.useRef(null);\n const {\n labelProps,\n fieldProps,\n validationErrors,\n descriptionProps,\n inputProps,\n errorMessageProps,\n isInvalid,\n } = useDateField(props, state, wrapperRef);\n\n const _errorMessage = rest.errorMessage || validationErrors.join(\" \");\n\n const message = getMessage({\n ...rest,\n errorMessage: _errorMessage,\n descriptionProps,\n errorMessageProps,\n labelPosition,\n });\n\n const { focusProps, isFocused } = useFocusRing({ within: true });\n\n return (\n <SharedInput\n className={classNames(\"BaselineUI-DateField\", className)}\n labelProps={labelProps}\n fieldProps={fieldProps}\n ref={ref}\n wrapperRef={wrapperRef}\n wrapperClassName={timeFieldCn}\n labelPosition={labelPosition}\n message={message}\n isFocused={isFocused}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n isInvalid={isInvalid}\n {...rest}\n >\n <VisuallyHidden>\n <input {...inputProps} />\n </VisuallyHidden>\n <Box\n display=\"flex\"\n flexDirection=\"row\"\n paddingRight=\"md\"\n {...focusProps}\n >\n {state.segments.map((segment, i) => (\n <DateTimeSegment key={i} segment={segment} state={state} />\n ))}\n </Box>\n </SharedInput>\n );\n },\n);\n\nDateField.displayName = \"DateField\";\n",
|
|
17
|
+
"DateFormat": "import { useDateFormatter } from \"react-aria\";\nimport React from \"react\";\n\nimport type { DateFormatProps } from \"./DateFormat.types\";\n\nexport const DateFormat: React.FC<DateFormatProps> = ({ date, ...props }) => {\n const formatter = useDateFormatter(props);\n\n return <>{formatter.format(date)}</>;\n};\n\nDateFormat.displayName = \"DateFormat\";\n",
|
|
18
|
+
"DeviceProvider": "import React, { useMemo } from \"react\";\nimport { breakpoints as defaultBreakpoints } from \"@baseline-ui/tokens\";\n\nimport type { Breakpoints, DeviceProviderProps } from \"./DeviceProvider.types\";\n\nexport const DeviceProviderContext = React.createContext<{\n breakpoints: Breakpoints;\n}>({\n breakpoints: defaultBreakpoints,\n});\n\nexport const DeviceProvider: React.FC<DeviceProviderProps> = ({\n children,\n breakpoints = defaultBreakpoints,\n}) => {\n const contextValue = useMemo(() => ({ breakpoints }), [breakpoints]);\n\n return (\n <DeviceProviderContext.Provider value={contextValue}>\n {children}\n </DeviceProviderContext.Provider>\n );\n};\n",
|
|
19
|
+
"Dialog": "import { mergeRefs } from \"@react-aria/utils\";\nimport React, { useContext, useLayoutEffect, useMemo } from \"react\";\nimport { mergeProps, useDialog, useFocusRing } from \"react-aria\";\nimport { assignInlineVars } from \"@vanilla-extract/dynamic\";\n\nimport { classNames } from \"../../utils\";\nimport { Drawer } from \"../Drawer\";\nimport { ModalDialogContext } from \"../Modal/ModalContent\";\nimport { containerWidthVar, contentCn } from \"./Dialog.css\";\nimport { FrameContext } from \"../FrameProvider/FrameProvider\";\n\nimport type { DialogProps } from \"./Dialog.types\";\n\nexport const DialogContentContext = React.createContext<{\n titleProps: React.HTMLAttributes<HTMLElement>;\n}>({\n titleProps: {},\n});\n\nexport const Dialog = React.forwardRef<HTMLDivElement, DialogProps>(\n (\n {\n children,\n className,\n style,\n inheritDrawer,\n size = \"md\",\n variant = \"primary\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n drawerBackground,\n ...rest\n },\n ref,\n ) => {\n const dialogRef = React.useRef(null);\n const drawerRef = React.useRef<HTMLDivElement>(null);\n const { containerWidth, shouldContainOverlays } = useContext(FrameContext);\n\n const {\n isDrawer,\n closeDrawer,\n dialogStyle,\n setDialogElement,\n isWithinModal,\n } = useContext(ModalDialogContext);\n const [drawerTitle, setDrawerTitle] = React.useState(\"\");\n const { focusProps, isFocusVisible } = useFocusRing();\n\n useLayoutEffect(() => {\n const ownerDocument = drawerRef.current?.getRootNode() as HTMLElement;\n let label = rest[\"aria-label\"] || \"\";\n\n if (rest[\"aria-labelledby\"]) {\n const title = ownerDocument?.querySelector(\n // eslint-disable-next-line sonarjs/no-nested-template-literals\n `#${rest[\"aria-labelledby\"].replaceAll(\":\", String.raw`\\:`)}`,\n );\n label = title?.textContent || \"\";\n }\n\n setDrawerTitle(label);\n }, [rest]);\n\n const { dialogProps, titleProps } = useDialog(rest, dialogRef);\n const dialogContextValue = useMemo(() => ({ titleProps }), [titleProps]);\n\n if (isDrawer && inheritDrawer) {\n return (\n <Drawer\n title={drawerTitle}\n onCloseRequest={closeDrawer}\n className={className}\n style={dialogStyle}\n {...rest}\n data-drawer={true}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n ref={mergeRefs(drawerRef, ref, (node) => {\n setDialogElement(node);\n })}\n background={drawerBackground}\n >\n {children}\n </Drawer>\n );\n }\n\n return (\n <DialogContentContext.Provider value={dialogContextValue}>\n <section\n {...mergeProps(dialogProps, focusProps)}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n data-focus-visible={isFocusVisible}\n data-overlay-type={isWithinModal ? \"modal\" : \"popover\"}\n ref={mergeRefs(dialogRef, ref, (node) => {\n setDialogElement(node);\n })}\n style={{\n ...dialogStyle,\n ...style,\n ...assignInlineVars({\n [containerWidthVar]:\n containerWidth && shouldContainOverlays\n ? `${containerWidth}px`\n : \"100vw\",\n }),\n }}\n className={classNames(\n contentCn({\n size: isDrawer ? \"content\" : size,\n variant,\n isDrawer,\n isFocusVisible,\n overlayType: isWithinModal ? \"modal\" : \"popover\",\n }),\n \"BaselineUI-Dialog\",\n className,\n )}\n >\n {children}\n </section>\n </DialogContentContext.Provider>\n );\n },\n);\n\nDialog.displayName = \"Dialog\";\n",
|
|
20
|
+
"DomNodeRenderer": "import { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport { usePress } from \"react-aria\";\n\nimport type { DomNodeRendererProps } from \"./DomNodeRenderer.types\";\n\nexport const DomNodeRenderer = React.forwardRef<\n HTMLDivElement,\n DomNodeRendererProps\n>(\n (\n {\n className,\n style,\n node,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const elementRef = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n const element = elementRef.current;\n element?.append(node);\n\n return () => {\n if (element?.contains(node)) (node as ChildNode).remove();\n };\n }, [node]);\n\n const { pressProps, isPressed } = usePress(rest);\n\n return (\n <div\n {...pressProps}\n className={className}\n style={style}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n data-pressed={isPressed}\n data-testid=\"dom-node-renderer\"\n ref={mergeRefs(ref, elementRef)}\n />\n );\n },\n);\n\nDomNodeRenderer.displayName = \"DomNodeRenderer\";\n",
|
|
21
|
+
"Drawer": "import { XIcon } from \"@baseline-ui/icons/20\";\nimport { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport { mergeProps, useDialog, useFocusRing } from \"react-aria\";\n\nimport { classNames } from \"../../utils\";\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { ellipsisCn } from \"../../utils/styleMixins.css\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { Box } from \"../Box\";\nimport { Separator } from \"../Separator\";\nimport { Text } from \"../Text\";\nimport { drawerCn, headerCn } from \"./Drawer.css\";\n\nimport type { DrawerProps } from \"./Drawer.types\";\n\nexport const Drawer = React.forwardRef<HTMLDivElement, DrawerProps>(\n (\n {\n className,\n style,\n children,\n background = \"medium\",\n title,\n action,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n onCloseRequest,\n contentStyle,\n contentClassName,\n ...rest\n },\n ref,\n ) => {\n const { formatMessage } = useI18n();\n const dialogRef = React.useRef(null);\n\n const { dialogProps, titleProps } = useDialog(rest, dialogRef);\n const { focusProps, isFocusVisible } = useFocusRing();\n\n return (\n <div\n {...mergeProps(dialogProps, focusProps)}\n className={classNames(\n drawerCn({ background, isFocusVisible }),\n \"BaselineUI-Drawer\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={mergeRefs(ref, dialogRef)}\n >\n <Box\n paddingLeft=\"lg\"\n paddingRight=\"sm\"\n backgroundColor=\"background.primary.subtle\"\n display=\"flex\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n gap=\"lg\"\n className={headerCn}\n >\n <Text type=\"title\" size=\"sm\" {...titleProps} className={ellipsisCn}>\n {title}\n </Text>\n\n <Box>\n {action ? (\n <ActionIconButton {...action} variant=\"secondary\" />\n ) : null}\n\n <ActionIconButton\n variant=\"secondary\"\n size=\"md\"\n icon={XIcon}\n onPress={onCloseRequest}\n aria-label={formatMessage(messages.close)}\n />\n </Box>\n </Box>\n <Separator\n variant={background === \"subtle\" ? \"primary\" : \"secondary\"}\n />\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n flex={1}\n style={{\n overflow: \"auto\",\n ...contentStyle,\n }}\n className={classNames(contentClassName, \"BaselineUI-Drawer-Content\")}\n >\n {children}\n </Box>\n </div>\n );\n },\n);\n\nDrawer.displayName = \"Drawer\";\n\nconst messages = defineMessages({\n close: {\n id: \"close\",\n defaultMessage: \"Close\",\n },\n});\n",
|
|
22
|
+
"Editor": "import {\n ArrowUpCircleFilledIcon,\n AtIcon,\n XCircleFilledIcon,\n} from \"@baseline-ui/icons/20\";\nimport { sprinkles } from \"@baseline-ui/tokens\";\nimport { mergeRefs } from \"@react-aria/utils\";\nimport { mergeProps, useKeyboard, useLabel } from \"react-aria\";\nimport React, { useCallback, useRef } from \"react\";\n\nimport { classNames, getPlainText } from \"../../utils\";\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { Avatar } from \"../Avatar\";\nimport { Box } from \"../Box\";\nimport { Separator } from \"../Separator\";\nimport { ToggleIconButton } from \"../ToggleIconButton\";\nimport {\n avatarCn,\n editorCn,\n minimalSaveButtonCn,\n minimalTextAreaContainerCn,\n toolbarCn,\n} from \"./Editor.css\";\nimport { HelpDialog } from \"./HelpDialog\";\nimport { PlainEditor } from \"./elements/PlainEditor\";\nimport { RichEditor } from \"./RichEditor\";\nimport { serialize } from \"./utils\";\n\nimport type { PlateNode } from \"./utils\";\nimport type { PlateEditor } from \"@udecode/plate-common\";\nimport type { EditorProps } from \"./Editor.types\";\n\nexport const Editor = React.memo(\n React.forwardRef<HTMLDivElement, EditorProps>(\n (\n {\n className,\n style,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n enableRichText = false,\n onCancel,\n onSave,\n isInline = true,\n submitButtonIcon = ArrowUpCircleFilledIcon,\n submitButtonAriaLabel,\n cancelButtonAriaLabel,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedby,\n \"aria-labelledby\": ariaLabelledby,\n \"aria-details\": ariaDetails,\n editableContentAriaLabel = \"Editing Area\",\n footerButtons,\n clearOnCancel,\n clearOnSave,\n ...rest\n },\n ref,\n ) => {\n const editorRef = useRef<PlateEditor>(null);\n\n const { formatMessage } = useI18n();\n const textBoxRef = useRef<HTMLTextAreaElement>(null);\n const plainEditorRef = useRef<{ clear: () => void }>(null);\n const divRef = useRef<HTMLDivElement>(null);\n const isMinimal = !enableRichText && rest.variant === \"minimal\";\n\n const richTextRef = useRef<{\n startMention: () => void;\n }>(null);\n\n const [value, setValue] = React.useState(rest.value || rest.defaultValue);\n\n const isSaveDisabled =\n rest.isDisabled || rest.isSaveDisabled || !getPlainText(value || \"\");\n\n const { fieldProps } = useLabel({\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n \"aria-describedby\": ariaDescribedby,\n \"aria-details\": ariaDetails,\n labelElementType: \"div\",\n });\n\n if (isMinimal && !rest.avatarName) {\n console.warn(\n \"Editor: When using the minimal `variant` and `enableRichText` is `false`, you should provide an `avatarName`.\",\n );\n }\n\n const clear = useCallback(() => {\n setValue(\"\");\n\n if (enableRichText) {\n editorRef.current?.reset();\n } else {\n plainEditorRef.current?.clear();\n }\n }, [enableRichText]);\n\n const handleSave = useCallback(() => {\n if (isSaveDisabled) return;\n\n onSave?.(\n enableRichText\n ? serialize(editorRef.current?.children as PlateNode[])\n : textBoxRef.current!.value,\n );\n\n if (clearOnSave) {\n clear();\n }\n }, [clear, clearOnSave, enableRichText, onSave, isSaveDisabled]);\n\n const saveButton = (\n <ActionIconButton\n variant=\"ghost\"\n aria-label={submitButtonAriaLabel || formatMessage(messages.save)}\n icon={submitButtonIcon}\n isDisabled={isSaveDisabled}\n size=\"md\"\n excludeFromTabOrder={isMinimal}\n className={classNames(\n { [minimalSaveButtonCn]: isMinimal },\n \"BaselineUI-Editor-Save\",\n )}\n onPress={handleSave}\n preventFocusOnPress={true}\n />\n );\n\n const [isHelpDialogOpen, setIsHelpDialogOpen] = React.useState(false);\n\n const { keyboardProps } = useKeyboard({\n onKeyDown: (e) => {\n if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === \"h\") {\n e.preventDefault();\n setIsHelpDialogOpen(true);\n }\n },\n });\n\n const handleChange = useCallback(\n (newValue: string) => {\n setValue(newValue);\n rest.onChange?.(newValue);\n },\n [rest],\n );\n\n return (\n <div\n {...mergeProps(fieldProps, enableRichText ? keyboardProps : {})}\n role=\"application\"\n ref={mergeRefs(divRef, ref)}\n style={style}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n className={classNames(\n editorCn({ isInline, isMinimal }),\n \"BaselineUI-Editor\",\n className,\n )}\n aria-disabled={rest.isDisabled}\n >\n {isMinimal ? (\n <>\n {isInline ? (\n <Avatar className={avatarCn} name={rest.avatarName || \"\"} />\n ) : null}\n <div className={minimalTextAreaContainerCn({ isInline })}>\n <PlainEditor\n isMinimal={isMinimal}\n textBoxRef={textBoxRef}\n isInline={isInline}\n editableContentAriaLabel={editableContentAriaLabel}\n {...rest}\n onChange={handleChange}\n onSave={handleSave}\n />\n {saveButton}\n </div>\n </>\n ) : (\n <>\n {enableRichText ? (\n <RichEditor\n containerRef={divRef}\n ref={richTextRef}\n editorRef={editorRef}\n isInline={isInline}\n editableContentAriaLabel={editableContentAriaLabel}\n onHelpDialogOpenRequest={() => {\n setIsHelpDialogOpen(true);\n }}\n {...rest}\n onChange={handleChange}\n onSave={handleSave}\n />\n ) : (\n <>\n {!isInline && <Separator variant=\"secondary\" />}\n <PlainEditor\n onCancel={onCancel}\n textBoxRef={textBoxRef}\n ref={plainEditorRef}\n isInline={isInline}\n editableContentAriaLabel={editableContentAriaLabel}\n {...rest}\n onChange={(newValue) => {\n setValue(newValue);\n\n rest.onChange?.(newValue);\n }}\n onSave={handleSave}\n />\n </>\n )}\n\n <Separator variant=\"secondary\" />\n <div\n className={classNames(\n toolbarCn,\n sprinkles({ justifyContent: \"space-between\" }),\n \"BaselineUI-Editor-Footer\",\n )}\n >\n <Box paddingX=\"sm\" gap=\"sm\" display=\"flex\">\n {enableRichText && rest.mentionableUsers ? (\n <ActionIconButton\n icon={AtIcon}\n size=\"sm\"\n aria-label={formatMessage(messages.mention)}\n onPress={() => richTextRef.current?.startMention()}\n preventFocusOnPress={true}\n className=\"BaselineUI-Editor-Mention\"\n />\n ) : null}\n {footerButtons?.map(({ type, props }, index) =>\n type === \"action\" ? (\n <ActionIconButton\n key={index}\n {...props}\n size=\"sm\"\n isDisabled={rest.isDisabled || props.isDisabled}\n />\n ) : (\n <ToggleIconButton\n key={index}\n {...props}\n size=\"sm\"\n variant=\"tertiary\"\n isDisabled={rest.isDisabled || props.isDisabled}\n />\n ),\n )}\n </Box>\n\n <Box>\n {!!onCancel && (\n <ActionIconButton\n variant=\"secondary\"\n icon={XCircleFilledIcon}\n isDisabled={rest.isDisabled}\n size=\"md\"\n onPress={() => {\n onCancel();\n if (clearOnCancel) {\n clear();\n }\n }}\n aria-label={\n cancelButtonAriaLabel || formatMessage(messages.cancel)\n }\n className=\"BaselineUI-Editor-Cancel\"\n />\n )}\n {saveButton}\n </Box>\n </div>\n </>\n )}\n\n <HelpDialog\n isOpen={isHelpDialogOpen}\n onClose={() => {\n setIsHelpDialogOpen(false);\n }}\n />\n </div>\n );\n },\n ),\n);\n\nEditor.displayName = \"Editor\";\n\nconst messages = defineMessages({\n cancel: {\n defaultMessage: \"Cancel\",\n id: \"cancel\",\n },\n save: {\n defaultMessage: \"Save\",\n id: \"save\",\n },\n mention: {\n defaultMessage: \"Mention\",\n id: \"mention\",\n },\n});\n",
|
|
23
|
+
"FileUpload": "import { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport {\n VisuallyHidden,\n mergeProps,\n useDrop,\n useFocusRing,\n useHover,\n} from \"react-aria\";\n\nimport { classNames, invariant } from \"../../utils\";\nimport { useFileUpload } from \"./hooks/useFileUpload\";\nimport {\n fileUploadCn,\n fileUploadDescriptionCn,\n fileUploadIconCn,\n fileUploadLabelCn,\n} from \"./FileUpload.css\";\n\nimport type { FileUploadProps } from \"./FileUpload.types\";\n\nexport const FileUpload = React.forwardRef<HTMLLabelElement, FileUploadProps>(\n (\n { className, label, description, icon: Icon, variant = \"default\", ...rest },\n ref,\n ) => {\n const elementRef = React.useRef<HTMLLabelElement>(null);\n const { inputProps, descriptionProps, labelProps } = useFileUpload(rest);\n const { dropProps } = useDrop({\n ref: elementRef,\n onDrop: async ({ items }) => {\n const filePromises = items\n .filter((item) => item.kind === \"file\")\n .map(async (item) => {\n invariant(item.kind === \"file\");\n return item.getFile();\n });\n\n const files = await Promise.all(filePromises);\n rest.onValueChange?.(files);\n },\n });\n const { focusProps, isFocusVisible } = useFocusRing();\n const { hoverProps, isHovered } = useHover({\n isDisabled: rest.isDisabled,\n });\n\n return (\n <label\n {...mergeProps(dropProps, hoverProps, labelProps)}\n className={classNames(\n fileUploadCn({\n variant,\n disabled: rest.isDisabled,\n hoveredOrFocusedOrActive: isHovered || isFocusVisible,\n }),\n className,\n )}\n ref={mergeRefs(ref, elementRef)}\n >\n <VisuallyHidden>\n <input {...mergeProps(focusProps, inputProps)} />\n </VisuallyHidden>\n {Icon ? (\n <Icon\n size={20}\n className={fileUploadIconCn({ disabled: rest.isDisabled })}\n />\n ) : null}\n <div>\n <div\n className={fileUploadLabelCn({\n disabled: rest.isDisabled,\n hovered: isHovered,\n })}\n >\n {label}\n </div>\n\n {description ? (\n <div\n {...descriptionProps}\n className={fileUploadDescriptionCn({\n disabled: rest.isDisabled,\n })}\n >\n {description}\n </div>\n ) : null}\n </div>\n </label>\n );\n },\n);\n\nFileUpload.displayName = \"FileUpload\";\n",
|
|
24
|
+
"FrameProvider": "/* eslint-disable no-restricted-globals */\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport { getOwnerWindow } from \"@react-aria/utils\";\nimport { addWindowFocusTracking } from \"@react-aria/interactions\";\nimport { enableShadowDOM } from \"@react-stately/flags\";\nimport { sprinkles } from \"@baseline-ui/tokens\";\n\nimport { PortalContainerProvider } from \"../PortalContainerProvider\";\nimport { useDevice, useIsFirstRender } from \"../../hooks\";\n\nimport type { FrameProviderProps } from \"./FrameProvider.types\";\n\nfunction isDocument(node: Node | null): node is Document {\n return node?.nodeType === Node.DOCUMENT_NODE;\n}\n\nexport const FrameContext = React.createContext<{\n container: HTMLElement | null;\n portalContainer: HTMLElement | null;\n shouldContainOverlays: boolean;\n isContainerPositioned: boolean;\n containerWidth: number | undefined;\n containerHeight: number | undefined;\n}>({\n container: null,\n portalContainer: null,\n shouldContainOverlays: false,\n isContainerPositioned: false,\n containerWidth: undefined,\n containerHeight: undefined,\n});\n\nexport const FrameProvider: React.FC<FrameProviderProps> = ({\n children,\n container,\n portalContainer,\n shouldContainOverlays = \"auto\",\n}) => {\n const device = useDevice();\n const [containerWidth, setContainerWidth] = useState<number | undefined>();\n const [containerHeight, setContainerHeight] = useState<number | undefined>();\n const [ownerDocument, setOwnerDocument] = useState<Document>(document);\n\n const _container = container ?? ownerDocument.body;\n\n useEffect(() => {\n const rootNode = _container?.getRootNode();\n let cleanup: (() => void) | undefined;\n\n enableShadowDOM();\n if (isDocument(rootNode) && rootNode !== document) {\n cleanup = addWindowFocusTracking(rootNode.body);\n }\n return () => {\n cleanup?.();\n };\n }, [_container]);\n\n const _shouldContainOverlays = useMemo(() => {\n if (_container === ownerDocument.body || shouldContainOverlays === \"never\")\n return false;\n if (shouldContainOverlays === \"always\") return true;\n return device !== \"mobile\";\n }, [shouldContainOverlays, device, _container, ownerDocument]);\n\n const [isContainerPositioned, setIsContainerPositioned] = useState(false);\n\n const isFirstRender = useIsFirstRender();\n\n useEffect(() => {\n if (!_container || isFirstRender) return;\n const ownerWindow = getOwnerWindow(_container);\n\n const style = ownerWindow.getComputedStyle(_container);\n const isContainerPositioned =\n !!style.position && style.position !== \"static\";\n\n if (!isContainerPositioned) {\n console.warn(\n \"FrameProvider: container is not positioned. Please ensure the container is non-static.\",\n );\n }\n\n setIsContainerPositioned(isContainerPositioned);\n\n const bounds = _container.getBoundingClientRect();\n\n if (bounds.width === 0 || bounds.height === 0) {\n console.error(\n \"FrameProvider: container does not have a width or height. Please ensure that the container is visible and has a width and height.\",\n );\n }\n\n function setContainerStyle({\n width,\n height,\n }: {\n width: number | undefined;\n height: number | undefined;\n }) {\n if (!_container) return;\n\n setContainerWidth(width);\n setContainerHeight(height);\n }\n\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n setContainerStyle({\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n });\n }\n });\n\n if (_shouldContainOverlays) {\n setContainerStyle({\n width: bounds.width,\n height: bounds.height,\n });\n\n resizeObserver.observe(_container);\n }\n\n _container.dataset[\"shouldContainOverlays\"] =\n _shouldContainOverlays.toString();\n\n return () => {\n resizeObserver.disconnect();\n\n _container.dataset[\"shouldContainOverlays\"] = \"\";\n };\n }, [_container, isFirstRender, _shouldContainOverlays]);\n\n const _portalContainer = portalContainer || _container || ownerDocument.body;\n\n useEffect(() => {\n if (portalContainer && !_container.contains(portalContainer)) {\n console.warn(\n \"FrameProvider: portalContainer is not a child of container. Please ensure the portalContainer is a child of the container.\",\n );\n }\n }, [_container, portalContainer]);\n\n const contextValue = useMemo(\n () => ({\n container: _container,\n portalContainer: _portalContainer,\n shouldContainOverlays: _shouldContainOverlays,\n isContainerPositioned,\n containerWidth,\n containerHeight,\n }),\n [\n _container,\n _portalContainer,\n _shouldContainOverlays,\n isContainerPositioned,\n containerWidth,\n containerHeight,\n ],\n );\n\n return (\n <FrameContext.Provider value={contextValue}>\n <PortalContainerProvider\n portalContainer={contextValue.portalContainer || undefined}\n >\n {children}\n <div\n ref={(el) => {\n if (el) {\n setOwnerDocument(el.ownerDocument);\n }\n }}\n className={sprinkles({\n display: \"none\",\n })}\n />\n </PortalContainerProvider>\n </FrameContext.Provider>\n );\n};\n",
|
|
25
|
+
"FreehandCanvas": "import { RedoIcon, UndoIcon } from \"@baseline-ui/icons/16\";\nimport { clamp, mergeRefs } from \"@react-aria/utils\";\nimport { getStroke } from \"perfect-freehand\";\nimport React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n VisuallyHidden,\n mergeProps,\n useFocusRing,\n useHover,\n useId,\n useKeyboard,\n useLabel,\n} from \"react-aria\";\n\nimport messages from \"./intl\";\nimport { classNames, getSvgPathFromStroke } from \"../../utils\";\nimport { imageDropZoneCn } from \"../ImageDropZone/ImageDropZone.css\";\nimport { useI18n, useUndoRedo } from \"../../hooks\";\nimport {\n freehandCanvasCn,\n freehandCanvasContainerCn,\n freehandCanvasFooterCn,\n freehandCanvasHeaderCn,\n} from \"./FreehandCanvas.css\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { ActionButton } from \"../ActionButton\";\nimport { Text } from \"../Text\";\nimport { usePathsData } from \"./hooks/usePathsData\";\n\nimport type { StrokeOptions } from \"perfect-freehand\";\nimport type {\n FreehandCanvasLine,\n FreehandCanvasPoint,\n FreehandCanvasProps,\n} from \"./FreehandCanvas.types\";\n\nexport const BORDER_SIZE = 1;\n\nconst defaultOptions = {\n size: 1,\n thinning: 0,\n smoothing: 0,\n streamline: 0,\n easing: (t) => t,\n start: {\n taper: 0,\n easing: (t) => t,\n cap: true,\n },\n end: {\n taper: 0,\n easing: (t) => t,\n cap: true,\n },\n simulatePressure: false,\n} as StrokeOptions;\n\nexport const FreehandCanvas = React.forwardRef<\n HTMLDivElement,\n FreehandCanvasProps\n>(\n (\n {\n className,\n style,\n canvasClassName,\n canvasStyle,\n isBound = true,\n strokeWidth = 2,\n strokeColor = \"#3A87FD\",\n onChange,\n defaultValue,\n onChangeEnd,\n enableHistory = false,\n isInline = true,\n isDisabled,\n placeholder,\n clearLabel,\n undoLabel,\n redoLabel,\n footerClassName,\n canvasRef,\n value,\n footerStyle,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"aria-label\": ariaLabel = \"Drawing canvas\",\n description = \"This is a canvas for drawing using a mouse or touch input.\",\n ...options\n },\n ref,\n ) => {\n const intl = useI18n(messages);\n\n const divRef = React.useRef<HTMLDivElement>(null);\n\n // We cannot use `useControlledState()` because it's broken for callback state setters like the one we use:\n // https://github.com/adobe/react-spectrum/issues/2320\n const [lines, setLines] = React.useState<FreehandCanvasLine[]>(\n value || defaultValue || [],\n );\n\n const _options = React.useMemo(\n () => ({ ...defaultOptions, ...options }),\n [options],\n );\n\n const [svgCoordinates, setSvgCoordinates] = React.useState<DOMRect>();\n\n const id = useId();\n\n const { labelProps, fieldProps } = useLabel({\n ...options,\n \"aria-label\": ariaLabel,\n labelElementType: \"span\",\n });\n\n const { isFocusVisible, focusProps } = useFocusRing({ within: true });\n const { hoverProps, isHovered } = useHover({ isDisabled });\n\n const { undo, redo, canRedo, canUndo, push } = useUndoRedo(lines, {\n onAction: (state) => {\n if (state) {\n setLines(state);\n onChangeEnd?.(state);\n }\n },\n isDisabled: !enableHistory,\n });\n\n const { keyboardProps } = useKeyboard({\n onKeyDown: (e) => {\n if (e.key === \"Backspace\") {\n clear();\n } else if (e.key === \"z\" && (e.metaKey || e.ctrlKey)) {\n if (e.shiftKey) {\n if (canRedo) {\n redo();\n }\n } else {\n if (canUndo) {\n undo();\n }\n }\n }\n },\n });\n\n // Used to enable stroke splitting on the drawing stroke when true\n const [isDrawing, setIsDrawing] = useState(false);\n\n const handlePointerDown: React.PointerEventHandler<SVGSVGElement> =\n useCallback((e) => {\n const coords = e.currentTarget.getBoundingClientRect();\n setSvgCoordinates(coords);\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n\n const newPoint: FreehandCanvasPoint = [\n e.clientX - coords.x,\n e.clientY - coords.y,\n e.pressure,\n ];\n\n setLines((lines) => [...lines, [newPoint]]);\n setIsDrawing(true);\n }, []);\n\n const handlePointerMove: React.PointerEventHandler<SVGSVGElement> =\n useCallback(\n (e) => {\n if (e.buttons !== 1 || !svgCoordinates) return;\n\n const normalizedEvents: PointerEvent[] = getFlattenedCoalescedEvents(\n e.nativeEvent,\n );\n\n const lastDrawnPoints: FreehandCanvasPoint[] = normalizedEvents.map(\n (event) => [\n isBound\n ? clamp(\n event.clientX - svgCoordinates.left,\n 0,\n svgCoordinates.width,\n )\n : event.clientX - svgCoordinates.left,\n isBound\n ? clamp(\n event.clientY - svgCoordinates.top,\n 0,\n svgCoordinates.height,\n )\n : event.clientY - svgCoordinates.top,\n event.pressure,\n ],\n );\n\n // Use a callback state setter to make sure we update the latest copy of the state.\n setLines((lines) => {\n const newLines = [...lines];\n\n newLines[lines.length - 1] = [\n ...newLines[lines.length - 1],\n ...lastDrawnPoints,\n ];\n\n return newLines;\n });\n },\n [svgCoordinates, isBound],\n );\n\n const handlePointerUp: React.PointerEventHandler<SVGSVGElement> = () => {\n if (!svgCoordinates) return;\n\n onChangeEnd?.(lines);\n\n push(lines);\n\n setIsDrawing(false);\n };\n\n useEffect(() => {\n onChange?.(lines);\n }, [lines, onChange]);\n\n const clear = () => {\n setLines([]);\n onChange?.([]);\n push([]);\n };\n\n return (\n <div\n ref={mergeRefs(divRef, ref)}\n className={classNames(\n freehandCanvasContainerCn,\n \"BaselineUI-FreehandCanvas\",\n className,\n )}\n style={style}\n aria-disabled={isDisabled}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n >\n {options.label || enableHistory ? (\n <div className={freehandCanvasHeaderCn({ isInline })}>\n {options.label ? (\n <Text type=\"label\" size=\"sm\" {...labelProps}>\n {options.label}\n </Text>\n ) : (\n <span />\n )}\n\n {enableHistory ? (\n <div>\n <ActionIconButton\n variant=\"secondary\"\n size=\"sm\"\n isDisabled={!canUndo || isDisabled}\n icon={UndoIcon}\n onPress={undo}\n aria-label={undoLabel || intl.formatMessage(\"undo\")}\n />\n <ActionIconButton\n variant=\"secondary\"\n size=\"sm\"\n isDisabled={!canRedo || isDisabled}\n icon={RedoIcon}\n onPress={redo}\n aria-label={redoLabel || intl.formatMessage(\"redo\")}\n />\n </div>\n ) : null}\n </div>\n ) : null}\n <div\n role=\"application\"\n {...mergeProps(focusProps, keyboardProps, hoverProps, fieldProps)}\n tabIndex={isDisabled ? undefined : 0}\n className={classNames(\n imageDropZoneCn({\n isInline,\n isHovered,\n }),\n freehandCanvasCn({\n isFocusVisible,\n isInline,\n isDisabled: !!isDisabled,\n }),\n canvasClassName,\n )}\n aria-describedby={options[\"aria-describedby\"] || id}\n style={canvasStyle}\n ref={canvasRef}\n >\n <VisuallyHidden>\n <div id={id}>{description}</div>\n </VisuallyHidden>\n <svg\n width={canvasStyle?.width || \"100%\"}\n height={canvasStyle?.height || \"100%\"}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n >\n {(value || lines).map((strokePoints, index, arr) =>\n index < arr.length - 1 || !isDrawing ? (\n <Stroke\n key={index}\n line={strokePoints}\n options={_options}\n strokeColor={strokeColor}\n strokeWidth={strokeWidth}\n />\n ) : (\n <SplitStroke\n key={index}\n line={strokePoints}\n options={_options}\n strokeColor={strokeColor}\n strokeWidth={strokeWidth}\n />\n ),\n )}\n </svg>\n </div>\n\n {placeholder || clearLabel ? (\n <div\n style={footerStyle}\n className={classNames(\n freehandCanvasFooterCn({ isInline, isDisabled }),\n footerClassName,\n )}\n >\n {(lines.length || !placeholder) && clearLabel ? (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={clearLabel}\n isDisabled={isDisabled}\n onPress={clear}\n />\n ) : (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={placeholder as string}\n isDisabled={true}\n />\n )}\n </div>\n ) : null}\n </div>\n );\n },\n);\n\nFreehandCanvas.displayName = \"FreehandCanvas\";\n\nconst Stroke = React.memo(\n ({\n line,\n options,\n strokeColor,\n strokeWidth,\n }: {\n line: FreehandCanvasLine;\n options: StrokeOptions;\n strokeColor: string;\n strokeWidth: number;\n }) => {\n const pathData = useMemo(\n () =>\n getSvgPathFromStroke(\n getStroke(\n // getStroke() requires points to be passed in { x, y, pressure } format\n line.map(([x, y, pressure]) => ({ x, y, pressure })),\n options,\n ),\n ),\n [line, options],\n );\n\n return <path d={pathData} stroke={strokeColor} strokeWidth={strokeWidth} />;\n },\n);\n\nStroke.displayName = \"Stroke\";\n\nconst SplitStroke = React.memo(\n ({\n line,\n options,\n strokeColor,\n strokeWidth,\n }: {\n line: FreehandCanvasLine;\n options: StrokeOptions;\n strokeColor: string;\n strokeWidth: number;\n }) => {\n const pathsData = usePathsData({ line, options });\n\n return (\n <>\n {pathsData.map((d, index) => (\n <path\n key={index}\n d={d}\n stroke={strokeColor}\n strokeWidth={strokeWidth}\n />\n ))}\n </>\n );\n },\n);\n\nSplitStroke.displayName = \"SplitStroke\";\n\nfunction getFlattenedCoalescedEvents(event: PointerEvent): PointerEvent[] {\n // getCoalescedEvents allows to get intermediate drawing points for enhanced precision.\n // see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/getCoalescedEvents\n if (typeof event.getCoalescedEvents === \"function\") {\n const events = event.getCoalescedEvents();\n\n // pointerrawupdate events can also have nested coalesced events\n // we return either the event or the coalesced events but not both, as indicated by the spec\n // https://w3c.github.io/pointerevents/#coalesced-events\n return events.length > 0\n ? events.flatMap((event) => getFlattenedCoalescedEvents(event))\n : [event];\n }\n\n return [event];\n}\n",
|
|
26
|
+
"GridList": "import { useObjectRef } from \"@react-aria/utils\";\nimport React from \"react\";\nimport { DragPreview, mergeProps, useGridList } from \"react-aria\";\nimport { useListState } from \"react-stately\";\n\nimport { classNames, invariant } from \"../../utils\";\nimport { DragPreviewContent } from \"../shared/components/DragPreviewContent\";\nimport { gridListItemPreviewCn } from \"./GridList.css\";\nimport { useDragDrop } from \"../ListBox/hooks/useDragDrop\";\nimport { useCollectionChildren } from \"../shared/collection\";\nimport { GridListItem } from \"./GridListItem\";\nimport { useSharedList } from \"../ListBox/hooks/useSharedList\";\n\nimport type { KeyboardEventHandler } from \"react\";\nimport type { GridListProps } from \"./GridList.types\";\n\nconst stopPropagation: KeyboardEventHandler<HTMLUListElement> = (e) => {\n // even in editing mode some special keys should continue to propagate so an internal input can listen to these events correctly\n if (e.key === \"Enter\" || e.key === \"Escape\") {\n return;\n }\n e.stopPropagation();\n};\n\nexport const GridList = React.forwardRef<HTMLUListElement, GridListProps>(\n (\n {\n className,\n style,\n isEditing = false,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n gridListHandle,\n onReorder,\n enableReorder = false,\n layout = \"stack\",\n orientation = \"vertical\",\n renderGridItem,\n ...rest\n },\n ref,\n ) => {\n const _children = useCollectionChildren();\n const state = useListState({ ...rest, children: _children });\n const previewRef = React.useRef(null);\n const ulRef = useObjectRef(ref);\n const { gridProps } = useGridList(rest, state, ulRef);\n useSharedList({ handle: gridListHandle, type: \"gridlist\" }, state, ulRef);\n\n const { collectionProps, dragState, dropState } = useDragDrop(\n {\n ...rest,\n onReorder,\n enableReorder,\n layout,\n orientation,\n preview: previewRef,\n // TODO [2025-12-15]: Figure out if we can reuse the same logic from\n // useDragDrop without having to provide the getItems function.\n getItems: (keys) => {\n return [...keys].map((key) => {\n const item = state.collection.getItem(key);\n\n invariant(item, \"Expected item to be defined.\");\n return {\n key: item.key.toString(),\n };\n });\n },\n },\n state,\n ulRef,\n );\n\n /**\n * GridList captures focus with its type-ahead feature. In editing mode this\n * stops propagation so an inline editing can work without taking focus away\n * from an input inside. E.g. if you type 'abc' inside an inline input and\n * there is a matching item which starts with 'abc' it would take focus away\n * from the input to the matching item rather than setting 'abc' as input\n * value.\n */\n const onKeyDownCapture = isEditing\n ? stopPropagation\n : gridProps.onKeyDownCapture;\n\n const props = {\n state,\n dragState,\n dropState,\n enableReorder,\n renderGridItem,\n };\n\n return (\n <ul\n {...mergeProps(gridProps, collectionProps)}\n className={classNames(\"BaselineUI-GridList\", className)}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={ulRef}\n aria-rowcount={state.collection.size}\n onKeyDownCapture={onKeyDownCapture}\n >\n {[...state.collection].map((item) => (\n <GridListItem key={item.key} item={item} {...props} />\n ))}\n {enableReorder ? (\n <DragPreview ref={previewRef}>\n {(items) => (\n <DragPreviewContent\n items={items}\n containerClassName=\".BaselineUI-GridList\"\n previewClassName={gridListItemPreviewCn()}\n itemDataAttribute=\"data-grid-list-item-key\"\n />\n )}\n </DragPreview>\n ) : null}\n </ul>\n );\n },\n);\n\nGridList.displayName = \"GridList\";\n",
|
|
27
|
+
"Group": "import React from \"react\";\nimport { useHover } from \"react-aria\";\n\nimport type { GroupProps } from \"./Group.types\";\n\nexport const Group = React.forwardRef<HTMLDivElement, GroupProps>(\n (\n {\n className,\n style,\n children,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlock,\n role = \"group\",\n isDisabled,\n ...ariaLabelling\n },\n ref,\n ) => {\n const { hoverProps, isHovered } = useHover({ isDisabled });\n\n return (\n <div\n {...hoverProps}\n role={role}\n data-hovered={isHovered}\n data-disabled={isDisabled}\n data-block-id={dataBlockId}\n data-block-class={dataBlock}\n className={className}\n style={style}\n {...ariaLabelling}\n ref={ref}\n >\n {children}\n </div>\n );\n },\n);\n\nGroup.displayName = \"Group\";\n",
|
|
28
|
+
"I18nProvider": "import { I18nProvider as Provider } from \"react-aria\";\nimport React, { useEffect, useRef, useState } from \"react\";\n\nimport type { LocalizedStrings } from \"react-aria\";\nimport type { I18nProviderProps } from \"./I18Provider.types\";\n\nexport const IntlMessagesProvider = React.createContext<{\n messages: LocalizedStrings;\n shouldLogMissingMessages: boolean;\n hasMissingErrorLoggedFor: React.MutableRefObject<Set<string>>;\n setLang: (lang: string) => void;\n}>({\n messages: {},\n shouldLogMissingMessages: true,\n hasMissingErrorLoggedFor: { current: new Set() },\n setLang: () => {},\n});\n\nexport const I18nProvider: React.FC<I18nProviderProps> = ({\n children,\n messages = {},\n shouldLogMissingMessages = true,\n ...props\n}) => {\n const hasMissingErrorLoggedFor = useRef<Set<string>>(new Set());\n\n const [lang, setLang] = useState(props.locale || \"\");\n\n useEffect(() => {\n hasMissingErrorLoggedFor.current.clear();\n }, [lang, props.locale]);\n\n const messagesValue = React.useMemo(\n () => ({\n messages,\n shouldLogMissingMessages,\n hasMissingErrorLoggedFor,\n setLang,\n }),\n [messages, shouldLogMissingMessages, setLang],\n );\n\n return (\n <Provider {...props}>\n <IntlMessagesProvider.Provider value={messagesValue}>\n {children}\n </IntlMessagesProvider.Provider>\n </Provider>\n );\n};\n\nI18nProvider.displayName = \"I18nProvider\";\n",
|
|
29
|
+
"Icon": "import * as Dompurify from \"dompurify\";\nimport { getOwnerDocument, mergeRefs } from \"@react-aria/utils\";\nimport React, { useId } from \"react\";\n\nimport { invariant } from \"../../utils\";\n\nimport type { IconComponentProps } from \"./Icon.types\";\n\nconst setSizeAttributes = (svgElement: SVGElement, size: number | string) => {\n svgElement.setAttribute(\n \"height\",\n typeof size === \"number\" ? `${size}px` : size,\n );\n svgElement.setAttribute(\n \"width\",\n typeof size === \"number\" ? `${size}px` : size,\n );\n};\n\nconst updateSvgTitle = (svgElement: SVGElement, title: string, id: string) => {\n const titleElement = svgElement.querySelector(\"title\");\n const ownerDocument = getOwnerDocument(svgElement);\n\n if (titleElement) {\n titleElement.textContent = title;\n titleElement.setAttribute(\"id\", id);\n } else {\n const newTitleElement = ownerDocument.createElement(\"title\");\n newTitleElement.textContent = title;\n newTitleElement.setAttribute(\"id\", id);\n svgElement.prepend(newTitleElement);\n }\n};\n\nconst setClassName = (svgElement: SVGElement, className?: string) => {\n if (className) {\n svgElement.classList.add(className);\n }\n};\n\nconst getSanitizedSvgElement = (_svg: string, ownerDocument: Document) => {\n const span = ownerDocument.createElement(\"span\");\n span.innerHTML = Dompurify.default.sanitize(_svg);\n const svgElement = span.querySelector(\"svg\");\n invariant(svgElement, \"Icon: svg prop must be a valid svg string\");\n return svgElement;\n};\n\nconst Icon = React.forwardRef<HTMLSpanElement, IconComponentProps>(\n ({ size, title, className, style, color, ...rest }, ref) => {\n const id = useId();\n const svgRef = React.useRef<HTMLSpanElement>(null);\n\n const _svg = \"svg\" in rest ? rest.svg : undefined;\n\n const svgString = React.useMemo(() => {\n if (_svg) {\n const ownerDocument = getOwnerDocument(svgRef.current);\n\n const svgElement = getSanitizedSvgElement(_svg, ownerDocument);\n if (size) {\n setSizeAttributes(svgElement, size);\n }\n\n if (title) {\n updateSvgTitle(svgElement, title, id);\n }\n\n setClassName(svgElement, className);\n\n return svgElement.outerHTML;\n }\n }, [_svg, size, title, className, id]);\n\n return (\n <span\n className={className}\n ref={mergeRefs(ref, svgRef)}\n style={{ display: \"contents\", ...style, color }}\n dangerouslySetInnerHTML={svgString ? { __html: svgString } : undefined}\n />\n );\n },\n);\n\nIcon.displayName = \"Icon\";\n\nexport { Icon };\n",
|
|
30
|
+
"ImageDropZone": "import { useControlledState } from \"@react-stately/utils\";\nimport React, { useMemo } from \"react\";\nimport {\n VisuallyHidden,\n mergeProps,\n useClipboard,\n useDrop,\n useField,\n useFocusRing,\n useHover,\n} from \"react-aria\";\n\nimport messages from \"./intl\";\nimport { classNames, filterTruthyValues, invariant } from \"../../utils\";\nimport { buttonCn } from \"../ActionButton/ActionButton.css\";\nimport { Box } from \"../Box\";\nimport { freehandCanvasFooterCn } from \"../FreehandCanvas/FreehandCanvas.css\";\nimport { ProgressSpinner } from \"../ProgressSpinner\";\nimport { onDropOrPaste } from \"../FileUpload/hooks/useFileUpload\";\nimport { imageDropZoneCn, opacityNoneCn, previewCn } from \"./ImageDropZone.css\";\nimport { ActionButton } from \"../ActionButton\";\nimport { useI18n, useImage } from \"../../hooks\";\n\nimport type { ActionButtonProps } from \"../ActionButton\";\nimport type { ChangeEventHandler } from \"react\";\nimport type { ImageDropZoneProps } from \"./ImageDropZone.types\";\n\nexport const ImageDropZone = React.forwardRef<\n HTMLDivElement,\n ImageDropZoneProps\n>(\n (\n {\n className,\n style,\n isInline = true,\n accept = \"image/*\",\n defaultImageSrc,\n imageSrc,\n imageAlt,\n pickerButtonLabel,\n isDisabled,\n onDrop,\n onPaste,\n includeCheckeredBackground = true,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n pickerButtonStyle,\n pickerButtonClassName,\n footerClassName,\n footerStyle,\n clearLabel,\n placeholder,\n labelStyle,\n labelClassName,\n ...rest\n },\n ref,\n ) => {\n const elementRef = React.useRef<HTMLLabelElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const { formatMessage } = useI18n(messages);\n\n const [url, setUrl] = useControlledState(\n imageSrc,\n defaultImageSrc || null,\n (value) => {\n invariant(typeof value !== \"string\");\n\n rest.onValueChange?.(value ? [value] : []);\n },\n );\n\n const _url = useMemo(() => getImageSrc(url || undefined), [url]);\n\n const { fieldProps, labelProps } = useField({\n ...rest,\n ref: elementRef,\n });\n\n const { dropProps, isDropTarget } = useDrop({\n ref: elementRef,\n onDrop: async (e) => {\n if (isDisabled) return;\n const files = await onDropOrPaste({ items: e.items });\n if (files.length) {\n setUrl(files[0]);\n }\n onDrop?.(e);\n },\n ...rest,\n });\n\n const { clipboardProps } = useClipboard({\n onPaste: async (items) => {\n if (isDisabled) return;\n const files = await onDropOrPaste({ items });\n if (files.length) {\n setUrl(files[0]);\n }\n onPaste?.(items);\n },\n ...rest,\n });\n\n const reset = () => {\n setUrl(null);\n if (inputRef.current) {\n inputRef.current.value = \"\";\n }\n };\n\n const inputProps = {\n type: \"file\",\n onChange: ((e) => {\n const files = [...(e.target.files as FileList)];\n setUrl(files[0]);\n\n rest.onChange?.(e);\n }) as ChangeEventHandler<HTMLInputElement>,\n accept,\n disabled: isDisabled,\n } as React.ComponentProps<\"input\">;\n\n const { focusProps, isFocused, isFocusVisible } = useFocusRing({\n within: true,\n });\n const { hoverProps, isHovered } = useHover({ isDisabled });\n\n const { imgProps, isLoading, isLoaded } = useImage({\n src: _url,\n alt: imageAlt || \"\",\n });\n\n const chooseFileButtonProps = {\n label: pickerButtonLabel || formatMessage(\"selectImage\"),\n onPress: () => elementRef.current?.click(),\n variant: \"secondary\",\n isDisabled,\n excludeFromTabOrder: true,\n className: classNames(\n buttonCn({ isFocusVisible }),\n {\n [opacityNoneCn]: isDropTarget && !isDisabled,\n },\n pickerButtonClassName,\n ),\n style: pickerButtonStyle,\n } as ActionButtonProps;\n\n const dataAttrs = filterTruthyValues({\n \"data-loaded\": isLoaded,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-hovered\": isHovered,\n \"data-focused\": isFocused,\n \"data-drop-target\": isDropTarget,\n \"data-disabled\": isDisabled,\n \"data-focus-visible\": isFocusVisible,\n });\n\n return (\n <Box\n {...dataAttrs}\n className={classNames(\"BaselineUI-ImageDropZone\", className)}\n display=\"flex\"\n flexDirection=\"column\"\n alignItems=\"center\"\n style={style}\n ref={ref}\n >\n <label\n {...mergeProps(dropProps, hoverProps, focusProps, labelProps)}\n className={classNames(\n imageDropZoneCn({\n isInline,\n isHovered,\n isDisabled,\n hasLoadedImage: !!_url && isLoaded,\n showFocusRing: isDropTarget || (!!_url && isFocusVisible),\n }),\n labelClassName,\n )}\n style={labelStyle}\n ref={elementRef}\n >\n <VisuallyHidden>\n <input\n {...mergeProps(fieldProps, clipboardProps, inputProps)}\n ref={inputRef}\n />\n </VisuallyHidden>\n\n {!_url && <ActionButton {...chooseFileButtonProps} />}\n\n {!!_url &&\n (isLoading ? (\n <ProgressSpinner />\n ) : (\n <img\n className={previewCn({\n isInline,\n isDisabled,\n hasLoadedImage:\n !!_url && isLoaded && includeCheckeredBackground,\n })}\n {...imgProps}\n />\n ))}\n </label>\n\n {placeholder || clearLabel ? (\n <div\n style={footerStyle}\n className={classNames(\n freehandCanvasFooterCn({ isInline, isDisabled }),\n footerClassName,\n )}\n >\n {(!!_url || !placeholder) && clearLabel ? (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={clearLabel}\n isDisabled={isDisabled}\n onPress={reset}\n />\n ) : (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={placeholder as string}\n isDisabled={true}\n />\n )}\n </div>\n ) : null}\n </Box>\n );\n },\n);\n\nImageDropZone.displayName = \"ImageDropZone\";\n\nfunction getImageSrc(imageSrc: string | File | undefined) {\n if (imageSrc instanceof File) {\n return URL.createObjectURL(imageSrc);\n }\n\n return imageSrc;\n}\n",
|
|
31
|
+
"ImageGallery": "import { useControlledState } from \"@react-stately/utils\";\nimport React, { useCallback, useContext, useEffect, useState } from \"react\";\nimport { mergeRefs } from \"@react-aria/utils\";\nimport {\n CollectionRendererContext,\n DropIndicator,\n} from \"react-aria-components\";\nimport { sprinkles } from \"@baseline-ui/tokens\";\n\nimport { classNames } from \"../../utils\";\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { AlertDialog } from \"../AlertDialog\";\nimport { UNSAFE_ListBox as ListBox } from \"../UNSAFE_ListBox\";\nimport { Modal, ModalContent } from \"../Modal\";\nimport { DragPreviewContent } from \"../shared/components/DragPreviewContent\";\nimport {\n dropIndicatorCn,\n imageGalleryCn,\n imageGalleryOptionCn,\n} from \"./ImageGallery.css\";\nimport { ImageGalleryItem } from \"./ImageGalleryItem\";\nimport {\n getByKey,\n imageItemsToListOption,\n listOptionToImageItems,\n moveAfter,\n moveBefore,\n} from \"./utils\";\nimport { getDynamicClassNameForCollectionItem } from \"../../utils/style\";\nimport { useDragAndDrop } from \"../ListBox/hooks/useDragAndDrop\";\n\nimport type { Key } from \"react-aria\";\nimport type { UNSAFE_ListBoxProps as ListBoxProps } from \"../UNSAFE_ListBox\";\nimport type { DroppableCollectionReorderEvent } from \"@react-types/shared\";\nimport type { ImageGalleryProps } from \"./ImageGallery.types\";\n\nconst Image = {\n src: \"src\",\n alt: \"alt\",\n} as const;\n\nexport const ImageSize = {\n sm: 102,\n md: 170,\n} as const;\n\nexport const ImageGallery = React.forwardRef<HTMLDivElement, ImageGalleryProps>(\n (\n {\n className,\n style,\n fit = \"cover\",\n onReorder,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n onDelete,\n imageWidth = ImageSize.sm,\n aspectRatio = 3 / 4,\n items,\n defaultItems = [],\n onKeyDown,\n imageContainerClassName,\n imageClassName,\n labelClassName,\n onListChange,\n renderImage,\n layoutTransition,\n imageDimensions = {\n width: imageWidth,\n aspectRatio,\n },\n imageContainerStyle,\n deleteConfirmationTitle = \"Are you sure you want to delete this item?\",\n ...rest\n },\n ref,\n ) => {\n const { isVirtualized } = useContext(CollectionRendererContext);\n const divRef = React.useRef<HTMLDivElement>(null);\n const [itemsToDelete, setItemsToDelete] = useState<Key[]>([]);\n const { formatMessage } = useI18n();\n\n useEffect(() => {\n if (aspectRatio || imageWidth) {\n console.warn(\n \"ImageGallery: aspectRatio and imageWidth are deprecated. Use imageDimensions instead.\",\n );\n }\n }, [imageWidth, aspectRatio]);\n\n const [selectedKeys, setSelectedKeys] = useControlledState(\n rest.selectedKeys,\n rest.defaultSelectedKeys ?? [],\n rest.onSelectionChange,\n );\n\n const [list, setList] = useControlledState(\n items ? imageItemsToListOption(items) : undefined,\n imageItemsToListOption(defaultItems),\n onListChange\n ? (_items) => {\n onListChange?.(listOptionToImageItems(_items));\n }\n : undefined,\n );\n\n const _onReorder = useCallback(\n (e: DroppableCollectionReorderEvent) => {\n const referenceKey = e.target.key;\n const keysToMove = e.keys;\n\n if (e.target.dropPosition === \"before\") {\n const newList = moveBefore(list, referenceKey, keysToMove);\n setList(newList);\n } else if (e.target.dropPosition === \"after\") {\n const newList = moveAfter(list, referenceKey, keysToMove);\n setList(newList);\n }\n onReorder?.(e);\n },\n [list, onReorder, setList],\n );\n\n const { dragAndDropHooks } = useDragAndDrop({\n onReorder: _onReorder,\n isDisabled: !onReorder,\n renderDragPreview: (items) => (\n <DragPreviewContent\n items={items}\n containerClassName=\".BaselineUI-ImageGallery\"\n previewClassName={imageGalleryOptionCn()}\n itemDataAttribute=\"data-image-key\"\n showCount={true}\n />\n ),\n getItems: (keys) =>\n [...keys].map((key) => {\n const item = getByKey(list, key);\n\n return {\n [Image.src]: item?.src || \"\",\n [Image.alt]: item?.alt || \"\",\n key: key as string,\n };\n }),\n renderDropIndicator(target) {\n return (\n <DropIndicator\n target={target}\n className={classNames(\n dropIndicatorCn({ isVirtualized: !!isVirtualized }),\n \"BaselineUI-DropIndicator\",\n )}\n />\n );\n },\n });\n\n const _onDelete = useCallback(() => {\n if (!onDelete) return;\n\n if (itemsToDelete.length) {\n setList(list.filter((item) => !itemsToDelete.includes(item.id)));\n\n onDelete(itemsToDelete);\n setItemsToDelete([]);\n }\n }, [itemsToDelete, list, onDelete, setList]);\n\n const renderOption = useCallback<\n Exclude<ListBoxProps[\"renderOption\"], undefined>\n >(\n (item, options) => {\n const galleryItem = {\n id: item.id,\n label: item.label,\n src: item.data?.src,\n alt: item.data?.alt,\n };\n\n return (\n <ImageGalleryItem\n key={item.id}\n item={item}\n options={options}\n fit={fit}\n onDelete={\n onDelete\n ? (id: string) => {\n setItemsToDelete([id]);\n }\n : undefined\n }\n renderImage={renderImage}\n className={getDynamicClassNameForCollectionItem(\n imageContainerClassName,\n galleryItem,\n options,\n )}\n imageClassName={imageClassName}\n imageLabelClassName={labelClassName}\n imageDimensions={imageDimensions}\n style={imageContainerStyle}\n layoutTransition={layoutTransition}\n />\n );\n },\n [\n fit,\n onDelete,\n renderImage,\n imageContainerClassName,\n imageClassName,\n labelClassName,\n imageDimensions,\n imageContainerStyle,\n layoutTransition,\n ],\n );\n\n useEffect(() => {\n const container = divRef.current;\n if (!container) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n onKeyDown?.(e);\n\n if (e.key === \"Backspace\" || e.key === \"Delete\") {\n if (!onDelete) return;\n\n const _selectedKeys = [...selectedKeys];\n // If there are selected items, delete them\n // Otherwise, delete the item that was focused\n if (_selectedKeys.length) {\n setItemsToDelete([...selectedKeys]);\n } else {\n const key = (e.target as HTMLUListElement).dataset.key as string;\n setItemsToDelete([key]);\n }\n }\n };\n\n container.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n container.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [onDelete, onKeyDown, selectedKeys]);\n\n return (\n <>\n <ListBox\n {...rest}\n ref={mergeRefs(ref, divRef)}\n style={style}\n className={classNames(\n imageGalleryCn,\n \"BaselineUI-ImageGallery\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n selectedKeys={selectedKeys}\n items={list}\n dragAndDropHooks={dragAndDropHooks}\n layout=\"grid\"\n orientation=\"vertical\"\n renderOption={renderOption}\n onSelectionChange={setSelectedKeys}\n optionClassName={\n onReorder && !isVirtualized\n ? sprinkles({\n marginLeft: \"sm\",\n })\n : undefined\n }\n />\n <Modal\n isOpen={!![...itemsToDelete].length}\n onOpenChange={(open) => {\n if (!open) {\n setItemsToDelete([]);\n }\n }}\n >\n <ModalContent isDismissable={true}>\n <AlertDialog\n showCloseButton={false}\n title={\n typeof deleteConfirmationTitle === \"function\"\n ? deleteConfirmationTitle(\n selectedKeys === \"all\" ? \"all\" : new Set(selectedKeys),\n )\n : deleteConfirmationTitle\n }\n primaryActionLabel={formatMessage(messages.delete)}\n cancelLabel={formatMessage(messages.cancel)}\n onCancel={() => {\n setItemsToDelete([]);\n }}\n onPrimaryAction={_onDelete}\n autoFocusButton=\"cancel\"\n />\n </ModalContent>\n </Modal>\n </>\n );\n },\n);\n\nImageGallery.displayName = \"ImageGallery\";\n\nexport const messages = defineMessages({\n delete: {\n id: \"delete\",\n defaultMessage: \"Delete\",\n },\n cancel: {\n id: \"cancel\",\n defaultMessage: \"Cancel\",\n },\n loading: {\n id: \"loading\",\n defaultMessage: \"Loading\",\n },\n});\n",
|
|
32
|
+
"InlineAlert": "import {\n CheckmarkCircleFilledIcon,\n ErrorAltCircleFilledIcon,\n InfoCircleFilledIcon,\n WarningFilledIcon,\n XIcon,\n} from \"@baseline-ui/icons/16\";\nimport {\n CheckCircleFilledIcon as CheckFilledIcon20,\n ErrorAltCircleFilledIcon as ErrorAltFilledIcon20,\n InfoCircleFilledIcon as InfoFilledIcon20,\n WarningFilledIcon as WarningFilledIcon20,\n} from \"@baseline-ui/icons/20\";\nimport React from \"react\";\n\nimport messages from \"./intl\";\nimport { classNames } from \"../../utils\";\nimport { ActionButton, ActionIconButton } from \"../\";\nimport {\n contentCn,\n inlineAlertCn,\n inlineAlertDescriptionCn,\n inlineAlertEndCn,\n inlineAlertIconCn,\n inlineAlertTitleCn,\n inlineStartCn,\n} from \"./InlineAlert.css\";\nimport { useI18n } from \"../../hooks\";\n\nimport type { InlineAlertProps } from \"./InlineAlert.types\";\nimport type Messages from \"./intl/en.json\";\n\nconst icons = {\n error: { sm: ErrorAltCircleFilledIcon, md: ErrorAltFilledIcon20 },\n info: { sm: InfoCircleFilledIcon, md: InfoFilledIcon20 },\n success: { sm: CheckmarkCircleFilledIcon, md: CheckFilledIcon20 },\n warning: { sm: WarningFilledIcon, md: WarningFilledIcon20 },\n};\n\nexport const InlineAlert = React.forwardRef<HTMLDivElement, InlineAlertProps>(\n (\n {\n className,\n description,\n actionLabel,\n onAction,\n style,\n variant = \"info\",\n title,\n arrangement = \"single\",\n onClose,\n size = \"sm\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n elementProps,\n },\n ref,\n ) => {\n const intl = useI18n<typeof Messages>(messages);\n\n const actionButton = actionLabel && (\n <ActionButton\n variant={arrangement === \"compact\" ? \"tertiary\" : \"ghost\"}\n label={actionLabel}\n onPress={onAction}\n />\n );\n\n const Icon = icons[variant][size];\n\n return (\n <div\n className={classNames(\n inlineAlertCn({\n variant,\n arrangement,\n hasButton: !!actionLabel || !!onClose,\n hasCloseButton: !!onClose,\n }),\n \"BaselineUI-InlineAlert\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n role=\"alert\"\n ref={ref}\n {...elementProps?.root}\n >\n <div className={inlineStartCn({ arrangement })}>\n <Icon className={inlineAlertIconCn({ variant })} size={16} />\n\n <div\n className={contentCn({ arrangement })}\n {...elementProps?.content}\n >\n <h3\n className={inlineAlertTitleCn({ size })}\n {...elementProps?.title}\n >\n {title}\n </h3>\n <section\n className={inlineAlertDescriptionCn({ size })}\n {...elementProps?.description}\n >\n {description}\n </section>\n </div>\n\n {arrangement === \"compact\" && actionButton}\n </div>\n\n <div className={inlineAlertEndCn({ arrangement })}>\n {arrangement !== \"compact\" && actionButton}\n {onClose ? (\n <ActionIconButton\n variant=\"secondary\"\n icon={XIcon}\n size=\"sm\"\n onPress={onClose}\n aria-label={intl.formatMessage(\"close\")}\n {...elementProps?.close}\n />\n ) : null}\n </div>\n </div>\n );\n },\n);\n\nInlineAlert.displayName = \"InlineAlert\";\n",
|
|
33
|
+
"InlineToolbar": "import { themeVars } from \"@baseline-ui/tokens\";\nimport {\n getActiveElement,\n getOwnerDocument,\n getOwnerWindow,\n mergeRefs,\n nodeContains,\n useViewportSize,\n} from \"@react-aria/utils\";\nimport { useControlledState } from \"@react-stately/utils\";\nimport { useGranularEffect, useGranularLayoutEffect } from \"granular-hooks\";\nimport React, {\n Fragment,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n} from \"react\";\nimport {\n FocusScope,\n mergeProps,\n useInteractOutside,\n useKeyboard,\n useOverlayPosition,\n} from \"react-aria\";\nimport { useOverlayTriggerState } from \"react-stately\";\nimport { useToolbar } from \"@react-aria/toolbar\";\n\nimport { classNames, findFocusableElements, invariant } from \"../../utils\";\nimport { arrowInlineCn, inlineToolbarContentCn } from \"./InlineToolbar.css\";\nimport { InlineToolbarButton } from \"./InlineToolbarButton\";\nimport { Portal } from \"../Portal\";\nimport { Separator } from \"../Separator\";\nimport { useTextSelection } from \"../../hooks\";\n\nimport type { InlineToolbarProps, Item } from \"./InlineToolbar.types\";\n\nexport const InlineToolbar = React.forwardRef<\n HTMLDivElement,\n InlineToolbarProps\n>(\n (\n {\n className,\n onAction,\n items,\n scrollRef,\n placement = \"top\",\n children,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n disabledKeys,\n isDisabled,\n anchorRect,\n ...rest\n },\n ref,\n ) => {\n const divRef = React.useRef<HTMLDivElement>(null);\n const overlayRef = React.useRef<HTMLDivElement>(null);\n const triggerRef = React.useRef<HTMLDivElement>(null);\n const selectionRef = React.useRef<Selection | null>(null);\n const [selectionRect, setSelectionRect] = useControlledState(\n anchorRect,\n null,\n );\n\n const state = useOverlayTriggerState(rest);\n\n const {\n overlayProps,\n arrowProps,\n placement: placementAxis,\n updatePosition,\n } = useOverlayPosition({\n arrowSize: 13,\n targetRef: triggerRef,\n arrowBoundaryOffset: 8,\n offset: 12,\n overlayRef,\n isOpen: state.isOpen,\n placement,\n scrollRef,\n ...rest,\n });\n\n const size = useViewportSize();\n\n useGranularEffect(\n () => {\n if (selectionRef.current) {\n setSelectionRect(\n selectionRef.current.getRangeAt(0).getBoundingClientRect(),\n );\n }\n },\n [size],\n [setSelectionRect],\n );\n\n const toolbarRef = React.useRef<HTMLDivElement>(null);\n\n const { toolbarProps } = useToolbar(\n {\n ...rest,\n isSingleTabStop: false,\n },\n toolbarRef,\n );\n\n useInteractOutside({\n ref: toolbarRef,\n onInteractOutside() {\n state.close();\n setSelectionRect(null);\n },\n });\n\n useEffect(() => {\n if (isDisabled && state.isOpen) {\n state.close();\n }\n }, [isDisabled, state]);\n\n useTextSelection({\n ref: divRef,\n isDisabled,\n onSelectionChange: (selection) => {\n if (selection.isCollapsed) {\n selectionRef.current = null;\n setSelectionRect(null);\n } else {\n selectionRef.current = selection;\n\n setSelectionRect(selection.getRangeAt(0).getBoundingClientRect());\n }\n },\n });\n\n const handleScroll = useCallback(() => {\n const selection = selectionRef.current;\n if (selection) {\n setSelectionRect(selection.getRangeAt(0).getBoundingClientRect());\n }\n }, [setSelectionRect]);\n\n useGranularEffect(\n () => {\n const ownerDocument = getOwnerDocument(divRef.current);\n\n const _scrollRef = scrollRef?.current ?? ownerDocument.body;\n\n _scrollRef?.addEventListener(\"scroll\", handleScroll);\n\n return () => {\n _scrollRef?.removeEventListener(\"scroll\", handleScroll);\n };\n },\n [scrollRef],\n [updatePosition],\n );\n\n useLayoutEffect(() => {\n if (selectionRect && !state.isOpen) {\n state.open();\n } else if (!selectionRect) {\n state.close();\n }\n }, [selectionRect, state]);\n\n useGranularLayoutEffect(updatePosition, [selectionRect], [updatePosition]);\n\n const _placement =\n placementAxis === \"center\" ? \"top\" : placementAxis || \"top\";\n\n const { keyboardProps } = useKeyboard({\n onKeyDown(e) {\n if (e.key === \"Escape\") {\n state.close();\n setSelectionRect(null);\n } else {\n e.continuePropagation();\n }\n },\n });\n\n const handleToolbarPress = useCallback(\n (item: Item) => {\n invariant(selectionRef.current, \"No selection\");\n onAction?.(item.id, selectionRef.current);\n setSelectionRect(null);\n selectionRef.current?.removeAllRanges();\n },\n [onAction, setSelectionRect],\n );\n\n const _items =\n typeof items === \"function\" ? items(selectionRef.current) : items;\n\n const overlayPositionStyle = useMemo<React.CSSProperties>(() => {\n const ownerWindow = getOwnerWindow(triggerRef.current);\n\n return {\n position: \"absolute\",\n top: (selectionRect?.top || 0) + ownerWindow.scrollY,\n left: (selectionRect?.left || 0) + ownerWindow.scrollX,\n width: selectionRect?.width,\n height: selectionRect?.height,\n visibility: \"hidden\",\n };\n }, [selectionRect]);\n\n const _disabledKeys =\n typeof disabledKeys === \"function\"\n ? disabledKeys(selectionRef.current)\n : disabledKeys;\n\n useEffect(\n function moveFocusToToolbarOnTab() {\n if (!state.isOpen) return;\n\n const ownerDocument = getOwnerDocument(divRef.current);\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Tab\" && state.isOpen && toolbarRef.current) {\n const focusableEls = findFocusableElements(toolbarRef.current);\n\n const first = focusableEls[0] as HTMLElement;\n\n if (!first) return;\n\n const activeElement = getActiveElement(ownerDocument);\n\n const isCurrentlyFocusedElementOutsideToolbar = !nodeContains(\n toolbarRef.current,\n activeElement,\n );\n\n if (isCurrentlyFocusedElementOutsideToolbar) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n\n ownerDocument.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n ownerDocument.removeEventListener(\"keydown\", handleKeyDown);\n };\n },\n [state.isOpen],\n );\n\n return (\n <>\n {React.cloneElement(children, { ref: divRef })}\n\n <Portal>\n <div ref={mergeRefs(triggerRef, ref)} style={overlayPositionStyle} />\n </Portal>\n\n {state.isOpen && selectionRect ? (\n <div\n {...overlayProps}\n ref={overlayRef}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n className=\"BaselineUI-InlineToolbar\"\n >\n <FocusScope contain={true} restoreFocus={true}>\n <div\n className={classNames(inlineToolbarContentCn, className)}\n {...mergeProps(toolbarProps, keyboardProps)}\n aria-keyshortcuts=\"Tab Shift+Tab ArrowRight ArrowLeft\"\n ref={toolbarRef}\n >\n {_items.map((item, i) => {\n const Icon = item.icon;\n\n return (\n <Fragment key={item.id}>\n <InlineToolbarButton\n onPress={() => {\n handleToolbarPress(item);\n }}\n aria-label={item[\"aria-label\"]}\n isDisabled={new Set(_disabledKeys).has(item.id)}\n >\n {item.label || (Icon && <Icon size={24} />)}\n </InlineToolbarButton>\n {i !== items.length - 1 && (\n <Separator\n orientation=\"vertical\"\n variant=\"secondary\"\n style={{\n backgroundColor: themeVars.color.border.strong,\n }}\n />\n )}\n </Fragment>\n );\n })}\n </div>\n </FocusScope>\n\n <div\n {...arrowProps}\n className={arrowInlineCn({ placement: _placement })}\n />\n </div>\n ) : null}\n </>\n );\n },\n);\n\nInlineToolbar.displayName = \"InlineToolbar\";\n",
|
|
34
|
+
"Link": "import { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport { mergeProps, useFocusRing, useHover, useLink } from \"react-aria\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { linkCn } from \"./Link.css\";\n\nimport type { LinkProps } from \"./Link.types\";\n\nexport const Link = React.forwardRef<HTMLElement, LinkProps>(\n (\n {\n className,\n style,\n children,\n variant = \"default\",\n size = \"md\",\n elementType = \"a\",\n type = \"body\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n title,\n role,\n ...props\n },\n ref,\n ) => {\n const linkRef = React.useRef<HTMLElement>(null);\n const { linkProps, isPressed } = useLink(\n { ...props, elementType: props.onPress ? \"span\" : elementType },\n linkRef,\n );\n const { isFocusVisible, isFocused, focusProps } = useFocusRing();\n const { hoverProps, isHovered } = useHover({\n isDisabled: props.isDisabled,\n });\n\n const LinkElement = elementType as React.ElementType;\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-disabled\": props.isDisabled,\n \"data-focused\": isFocused,\n \"data-pressed\": isPressed,\n \"data-focus-visible\": isFocusVisible,\n \"data-hovered\": isHovered,\n role,\n title,\n });\n\n return (\n <LinkElement\n {...mergeProps(linkProps, focusProps, hoverProps, dataAttrs)}\n className={classNames(\n linkCn({\n variant,\n isFocusVisible,\n isHovered,\n isDisabled: props.isDisabled,\n size,\n isActive: isPressed,\n type,\n }),\n \"BaselineUI-Link\",\n className,\n )}\n ref={mergeRefs(linkRef, ref)}\n style={style}\n >\n {children}\n </LinkElement>\n );\n },\n);\n\nLink.displayName = \"Link\";\n",
|
|
35
|
+
"ListBox": "import { mergeRefs } from \"@react-aria/utils\";\nimport React, { useContext, useEffect } from \"react\";\nimport {\n DragPreview,\n ListKeyboardDelegate,\n mergeProps,\n useFocusRing,\n useListBox,\n useLocale,\n} from \"react-aria\";\nimport { useListState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { useCollectionChildren } from \"../shared/collection\";\nimport { useDragDrop } from \"./hooks/useDragDrop\";\nimport { listBoxCn } from \"./ListBox.css\";\nimport { ListBoxSection } from \"./ListBoxSection\";\nimport { ListOption } from \"./ListOption\";\nimport { useSharedList } from \"./hooks/useSharedList\";\nimport { useDragAutoScroll } from \"./hooks/useDragAutoScroll\";\n\nimport type { ListItem } from \"../shared/types/List\";\nimport type { ComboBoxState, ListState } from \"react-stately\";\nimport type { DragPreviewRenderer } from \"react-aria\";\nimport type { ListBoxProps } from \"./ListBox.types\";\n\nexport const ListBoxContext = React.createContext<\n ListState<ListItem> | ComboBoxState<ListItem> | null\n>(null);\n\nconst ListBoxCore = React.forwardRef<\n HTMLUListElement,\n ListBoxProps & {\n state: ListState<ListItem> | ComboBoxState<ListItem>;\n }\n>(\n (\n {\n className,\n layout = \"stack\",\n orientation = \"vertical\",\n showSelectedIcon = true,\n style,\n renderOption,\n enableReorder = false,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n renderDragPreview,\n onReorder,\n state,\n listBoxHandle,\n ...rest\n },\n ref,\n ) => {\n const previewRef = React.useRef<DragPreviewRenderer>(null);\n\n const { direction } = useLocale();\n\n const ulRef = React.useRef<HTMLUListElement>(null);\n\n const { listBoxProps } = useListBox(\n {\n ...rest,\n /**\n * When dragging is enabled, we want to select the item on press up\n * otherwise the other items will be deselected when dragging.\n */\n shouldSelectOnPressUp: enableReorder,\n keyboardDelegate: new ListKeyboardDelegate({\n collection: state.collection,\n ref: ulRef,\n layout,\n orientation,\n direction,\n disabledKeys: state.disabledKeys,\n }),\n },\n state,\n ulRef,\n );\n\n useSharedList({ handle: listBoxHandle }, state, ulRef);\n\n const { collectionProps, dragState, dropState } = useDragDrop(\n {\n ...rest,\n onReorder,\n layout,\n orientation,\n enableReorder,\n preview: previewRef,\n },\n state,\n ulRef,\n );\n\n const { isFocusVisible, isFocused, focusProps } = useFocusRing();\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-layout\": layout,\n \"data-empty\": state.collection.size === 0,\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n \"data-orientation\": orientation,\n });\n\n useDragAutoScroll({ ref: ulRef, isDisabled: !enableReorder });\n\n return (\n <ul\n {...mergeProps(listBoxProps, focusProps, dataAttrs, collectionProps)}\n className={classNames(listBoxCn, \"BaselineUI-ListBox\", className)}\n style={style}\n ref={mergeRefs(ref, ulRef)}\n >\n {[...state.collection].map((item) => {\n const props = {\n key: item.key,\n state,\n dragState,\n dropState,\n renderOption,\n renderDropIndicator: rest.renderDropIndicator,\n renderSectionHeader: rest.renderSectionHeader,\n showSelectedIcon,\n orientation,\n dropIndicatorStyle: rest.dropIndicatorStyle,\n dropIndicatorClassName: rest.dropIndicatorClassName,\n optionStyle: rest.optionStyle,\n optionClassName: rest.optionClassName,\n sectionClassName: rest.sectionClassName,\n sectionStyle: rest.sectionStyle,\n showSectionHeader: rest.showSectionHeader,\n withSectionHeaderPadding: rest.withSectionHeaderPadding,\n };\n\n return item.type === \"section\" ? (\n <ListBoxSection {...props} key={item.key} section={item} />\n ) : (\n <ListOption {...props} key={item.key} item={item} />\n );\n })}\n\n {renderDragPreview ? (\n <DragPreview ref={previewRef}>{renderDragPreview}</DragPreview>\n ) : null}\n </ul>\n );\n },\n);\n\nListBoxCore.displayName = \"ListBoxCore\";\n\nconst ListBoxStandalone = React.forwardRef<HTMLUListElement, ListBoxProps>(\n (props, ref) => {\n const _children = useCollectionChildren();\n\n const state = useListState({\n ...props,\n children: _children,\n });\n\n return <ListBoxCore {...props} ref={ref} state={state} />;\n },\n);\n\nListBoxStandalone.displayName = \"ListBoxStandalone\";\n\nexport const ListBox = React.forwardRef<HTMLUListElement, ListBoxProps>(\n (props, ref) => {\n const state = useContext(ListBoxContext);\n\n useEffect(() => {\n if (!props.items && !state) {\n throw new Error(\n \"ListBox: You must provide a `state` or `items` prop to render the listbox.\",\n );\n }\n }, [props.items, state]);\n\n return state ? (\n <ListBoxCore {...props} ref={ref} state={state} />\n ) : (\n <ListBoxStandalone {...props} ref={ref} />\n );\n },\n);\n\nListBox.displayName = \"ListBox\";\n",
|
|
36
|
+
"Markdown": "import React, { useMemo } from \"react\";\nimport markdownit from \"markdown-it\";\n\nimport { classNames } from \"../../utils\";\nimport { blinkingCaretCn, markdownCn, markdownContentCn } from \"./Markdown.css\";\n\nimport type { MarkdownProps } from \"./Markdown.types\";\n\nconst md = markdownit();\n\nexport const Markdown = React.forwardRef<HTMLDivElement, MarkdownProps>(\n (\n {\n className,\n style,\n children,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n showCaret,\n },\n ref,\n ) => {\n const html = useMemo(() => {\n let text = children;\n\n if (!children || (Array.isArray(children) && children.length === 0)) {\n text = \"\";\n }\n\n return md.render(text);\n }, [children]);\n\n return (\n <div\n className={classNames(\n markdownCn,\n markdownContentCn,\n { [blinkingCaretCn]: showCaret },\n \"BaselineUI-Markdown\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={ref}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n );\n },\n);\n\nMarkdown.displayName = \"Markdown\";\n",
|
|
37
|
+
"Menu": "import React, { useEffect, useMemo } from \"react\";\nimport { useMenuTriggerState } from \"react-stately\";\nimport { mergeProps, useMenuTrigger } from \"react-aria\";\n\nimport { usePortalContainer } from \"../../hooks\";\nimport { ActionButton } from \"../ActionButton\";\nimport { PopoverContent } from \"../Popover/PopoverContent\";\nimport { useCollectionChildren } from \"../shared/collection\";\nimport { MenuContent } from \"./MenuContent\";\n\nimport type { MenuItem, MenuProps } from \"./Menu.types\";\n\nexport const Menu = React.forwardRef<HTMLUListElement, MenuProps>(\n (\n {\n defaultOpen,\n onOpenChange,\n isOpen,\n isDisabled,\n contentClassName,\n placement = \"bottom start\",\n isNonModal,\n shouldFlip,\n shouldUpdatePosition,\n boundaryElement,\n crossOffset,\n offset,\n triggerLabel,\n renderTrigger = ({ buttonProps, ref }) => (\n <ActionButton {...buttonProps} variant=\"popover\" ref={ref} />\n ),\n portalContainer,\n ...props\n },\n ref,\n ) => {\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const _portalContainer = usePortalContainer(portalContainer);\n\n const state = useMenuTriggerState({\n defaultOpen,\n isOpen,\n onOpenChange,\n });\n\n const { menuTriggerProps, menuProps } = useMenuTrigger<MenuItem>(\n {\n isDisabled,\n type: \"menu\",\n },\n state,\n buttonRef,\n );\n\n const _children = useCollectionChildren();\n\n const triggerButtonProps = useMemo(\n () =>\n mergeProps(menuTriggerProps, {\n isDisabled,\n isSelected: state.isOpen,\n className: \"BaselineUI-Menu-Trigger\",\n label:\n typeof triggerLabel === \"function\"\n ? triggerLabel(state.isOpen)\n : triggerLabel,\n }),\n [menuTriggerProps, isDisabled, state.isOpen, triggerLabel],\n );\n\n useEffect(() => {\n if (!triggerLabel && !renderTrigger) {\n console.warn(\n \"The `triggerLabel` prop is required when no `renderTrigger` or `children` is provided.\",\n );\n }\n }, [renderTrigger, triggerLabel]);\n\n return (\n <>\n {renderTrigger({ buttonProps: triggerButtonProps, ref: buttonRef })}\n {state.isOpen ? (\n <PopoverContent\n triggerRef={buttonRef}\n state={state}\n placement={placement}\n isNonModal={isNonModal}\n shouldFlip={shouldFlip}\n shouldUpdatePosition={shouldUpdatePosition}\n boundaryElement={boundaryElement}\n crossOffset={crossOffset}\n offset={offset || 2}\n className=\"BaselineUI-Menu-Popover\"\n portalContainer={_portalContainer}\n >\n <MenuContent\n className={contentClassName}\n {...menuProps}\n {...props}\n ref={ref}\n autoFocus={state.focusStrategy || \"first\"}\n >\n {_children}\n </MenuContent>\n </PopoverContent>\n ) : null}\n </>\n );\n },\n);\n\nMenu.displayName = \"Menu\";\n",
|
|
38
|
+
"MessageFormat": "import React, { Fragment } from \"react\";\n\nimport { invariant } from \"../../utils\";\nimport { useI18n } from \"../../hooks/useI18n\";\n\nimport type { MessageFormatProps } from \"./MessageFormat.types\";\n\nexport const MessageFormat: React.FC<MessageFormatProps> = ({\n id,\n defaultMessage,\n elementType: ElementType = Fragment,\n}) => {\n invariant(id || defaultMessage, \"`id` or `defaultMessage` is required.\");\n const intl = useI18n();\n\n return <ElementType>{intl.formatMessage(id) || defaultMessage}</ElementType>;\n};\n\nMessageFormat.displayName = \"MessageFormat\";\n",
|
|
39
|
+
"Modal": "import React, { useMemo } from \"react\";\nimport { useOverlayTrigger } from \"react-aria\";\nimport { useOverlayTriggerState } from \"react-stately\";\n\nimport type { OverlayTriggerState } from \"react-stately\";\nimport type { AriaButtonProps } from \"react-aria\";\nimport type { DOMProps } from \"@react-types/shared\";\nimport type { ModalProps } from \"./Modal.types\";\n\nexport const ModalContext = React.createContext<{\n state: OverlayTriggerState | null;\n triggerProps: AriaButtonProps<\"button\">;\n overlayProps?: DOMProps;\n}>({\n state: null,\n triggerProps: {},\n overlayProps: {},\n});\n\nexport const Modal: React.FC<ModalProps> = ({ children, ...rest }) => {\n const state = useOverlayTriggerState(rest);\n const { triggerProps, overlayProps } = useOverlayTrigger(\n { type: \"dialog\" },\n state,\n );\n\n const modalContextValue = useMemo(\n () => ({\n state,\n triggerProps,\n overlayProps,\n }),\n [state, triggerProps, overlayProps],\n );\n\n return (\n <ModalContext.Provider value={modalContextValue}>\n {children}\n </ModalContext.Provider>\n );\n};\n\nModal.displayName = \"Modal\";\n",
|
|
40
|
+
"NumberFormat": "import React from \"react\";\nimport { useNumberFormatter } from \"react-aria\";\n\nimport type { NumberFormatProps } from \"./NumberFormat.types\";\n\nexport const NumberFormat: React.FC<NumberFormatProps> = ({\n value,\n ...props\n}) => {\n const formatter = useNumberFormatter(props);\n return <>{formatter.format(value)}</>;\n};\n\nNumberFormat.displayName = \"NumberFormat\";\n",
|
|
41
|
+
"NumberInput": "import { ErrorCircleFilledIcon, ReadOnlyIcon } from \"@baseline-ui/icons/16\";\nimport React, { useMemo } from \"react\";\nimport {\n mergeProps,\n useFocusRing,\n useHover,\n useLocale,\n useNumberField,\n} from \"react-aria\";\nimport { MinusIcon, PlusIcon } from \"@baseline-ui/icons/8\";\nimport { useNumberFieldState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { getMessage } from \"../TextInput/utils/getMessage\";\nimport { Separator } from \"../Separator\";\nimport {\n numberActionsContainerCn,\n numberInputContainerLabelStartCn,\n} from \"./NumberInput.css\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport {\n textInputCn,\n textInputIconCn,\n textInputLabelCn,\n textInputLabelContainerCn,\n textInputRootCn,\n textInputWrapperCn,\n} from \"../TextInput/TextInput.css\";\nimport { disabledCn } from \"../../utils/styleMixins.css\";\n\nimport type { NumberInputProps } from \"./NumberInput.types\";\n\nexport const NumberInput = React.forwardRef<HTMLDivElement, NumberInputProps>(\n (\n {\n className,\n style,\n labelPosition = \"top\",\n variant = \"primary\",\n showStepper = true,\n ...rest\n },\n ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null);\n const { locale } = useLocale();\n const state = useNumberFieldState({ ...rest, locale });\n const { hoverProps, isHovered } = useHover({ isDisabled: rest.isDisabled });\n const { focusProps, isFocusVisible, isFocused } = useFocusRing();\n\n const {\n labelProps,\n decrementButtonProps,\n inputProps,\n incrementButtonProps,\n groupProps,\n descriptionProps,\n errorMessageProps,\n } = useNumberField(rest, state, inputRef);\n\n const Icon = useMemo(() => {\n if (rest.isReadOnly) {\n return ReadOnlyIcon;\n } else if (rest.isInvalid) {\n return ErrorCircleFilledIcon;\n }\n\n return null;\n }, [rest.isInvalid, rest.isReadOnly]);\n\n const message = getMessage({\n description: rest.description,\n errorMessageProps,\n descriptionProps,\n errorMessage: rest.errorMessage,\n labelPosition,\n isDisabled: rest.isDisabled,\n });\n\n const dataAttrs = filterTruthyValues({\n \"data-disabled\": rest.isDisabled,\n \"data-readonly\": rest.isReadOnly,\n \"data-hovered\": isHovered,\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n \"data-invalid\": rest.isInvalid,\n \"data-block-id\": rest[\"data-block-id\"],\n \"data-block-class\": rest[\"data-block-class\"],\n });\n\n return (\n <div\n style={style}\n className={classNames(\n textInputRootCn({\n labelPosition,\n }),\n \"BaselineUI-NumberInput\",\n className,\n )}\n {...dataAttrs}\n ref={ref}\n >\n {rest.label ? (\n <div\n className={textInputLabelContainerCn({\n labelPosition,\n hasMessage: !!message,\n })}\n >\n <label {...labelProps} className={textInputLabelCn}>\n {rest.label}\n </label>\n {labelPosition === \"start\" && message}\n </div>\n ) : null}\n\n <div className={classNames({ [disabledCn]: rest.isDisabled })}>\n <div\n {...mergeProps(groupProps, hoverProps)}\n className={classNames(\n textInputWrapperCn({\n isHovered,\n isDisabled: !!rest.isDisabled,\n isReadOnly: !!rest.isReadOnly,\n validationState: rest.isInvalid ? \"error\" : \"valid\",\n isFocused,\n variant,\n }),\n { [numberInputContainerLabelStartCn]: labelPosition === \"start\" },\n \"BaselineUI-NumberInput-Container\",\n )}\n >\n <input\n {...mergeProps(inputProps, focusProps)}\n ref={inputRef}\n className={classNames(\n \"BaselineUI-NumberInput-Input\",\n textInputCn({\n isDisabled: rest.isDisabled,\n labelPosition,\n }),\n )}\n />\n\n {Icon ? (\n <Icon\n className={textInputIconCn({\n validationState: \"error\",\n isReadOnly: rest.isReadOnly,\n })}\n size={16}\n />\n ) : null}\n\n {showStepper ? (\n <div\n className={numberActionsContainerCn({\n isHoveredOrFocused: isHovered || isFocused,\n isInteractive: !rest.isReadOnly && !rest.isDisabled,\n })}\n >\n <ActionIconButton\n variant=\"secondary\"\n size=\"xxs\"\n icon={MinusIcon}\n {...decrementButtonProps}\n className=\"BaselineUI-NumberInput-DecrementButton\"\n isExcludedFromRovingFocus={true}\n />\n <Separator style={{ height: 16 }} orientation=\"vertical\" />\n <ActionIconButton\n variant=\"secondary\"\n size=\"xxs\"\n icon={PlusIcon}\n {...incrementButtonProps}\n className=\"BaselineUI-NumberInput-DecrementButton\"\n isExcludedFromRovingFocus={true}\n />\n </div>\n ) : null}\n </div>\n\n {labelPosition === \"top\" && message}\n </div>\n </div>\n );\n },\n);\n\nNumberInput.displayName = \"NumberInput\";\n",
|
|
42
|
+
"Pagination": "import React from \"react\";\nimport {\n mergeProps,\n useFocusRing,\n useHover,\n useLocale,\n useNumberField,\n} from \"react-aria\";\nimport { useNumberFieldState } from \"react-stately\";\nimport {\n CaretLeftIcon as CL12,\n CaretRightIcon as CR12,\n} from \"@baseline-ui/icons/12\";\nimport {\n CaretLeftIcon as CL16,\n CaretRightIcon as CR16,\n} from \"@baseline-ui/icons/16\";\nimport {\n CaretLeftIcon as CL20,\n CaretRightIcon as CR20,\n} from \"@baseline-ui/icons/20\";\nimport {\n CaretLeftIcon as CL24,\n CaretRightIcon as CR24,\n} from \"@baseline-ui/icons/24\";\n\nimport messages from \"./intl\";\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { useI18n } from \"../../hooks\";\nimport {\n countContainerCn,\n countInputCn,\n paginationCn,\n totalCountCn,\n} from \"./Pagination.css\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { Separator } from \"../Separator\";\nimport { NumberFormat } from \"../NumberFormat\";\n\nimport type Messages from \"./intl/en.json\";\nimport type { AriaNumberFieldProps } from \"react-aria\";\nimport type { PaginationProps } from \"./Pagination.types\";\n\nconst formatOptions = {\n style: \"decimal\",\n} as Intl.NumberFormatOptions;\n\nconst sizeToCaretMapping = {\n xs: [CL12, CR12],\n sm: [CL16, CR16],\n md: [CL20, CR20],\n lg: [CL24, CR24],\n};\n\nexport const Pagination = React.forwardRef<HTMLDivElement, PaginationProps>(\n (\n {\n className,\n style,\n minValue = 1,\n defaultValue = 1,\n incrementAriaLabel,\n decrementAriaLabel,\n \"aria-label\": ariaLabel,\n size = \"md\",\n variant = \"floating\",\n hideActions,\n isDisabled,\n ...props\n },\n ref,\n ) => {\n const { locale } = useLocale();\n const intl = useI18n<typeof Messages>(messages);\n\n const _props = {\n ...props,\n formatOptions,\n minValue,\n incrementAriaLabel: incrementAriaLabel || intl.formatMessage(\"nextPage\"),\n decrementAriaLabel: decrementAriaLabel || intl.formatMessage(\"prevPage\"),\n defaultValue,\n \"aria-label\": ariaLabel || intl.formatMessage(\"goToPage\"),\n } as AriaNumberFieldProps;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const state = useNumberFieldState({ ..._props, locale });\n const {\n inputProps,\n decrementButtonProps,\n incrementButtonProps,\n groupProps,\n } = useNumberField(\n {\n ..._props,\n onKeyDown: (e) => {\n if (e.key === \"Enter\") {\n inputRef.current?.blur();\n }\n props.onKeyDown?.(e);\n },\n isDisabled,\n },\n state,\n inputRef,\n );\n const { hoverProps, isHovered } = useHover({});\n const { focusProps, isFocused, isFocusVisible } = useFocusRing();\n\n const isFloating = variant === \"floating\";\n const [CaretLeftIcon, CaretRightIcon] = sizeToCaretMapping[size];\n\n const dataAttrs = filterTruthyValues({\n \"data-focus-visible\": isFocusVisible,\n \"data-focused\": isFocused,\n \"data-hovered\": isHovered,\n \"data-block-id\": props[\"data-block-id\"],\n \"data-block-class\": props[\"data-block-class\"],\n });\n\n return (\n <div\n {...mergeProps(groupProps, hoverProps, dataAttrs)}\n className={classNames(\n paginationCn({ size, variant }),\n \"BaselineUI-Pagination\",\n className,\n )}\n style={style}\n ref={ref}\n >\n {/**\n * Due to a [bug](https://bugs.webkit.org/show_bug.cgi?id=219188) in Safari on\n * macOS, pointer events may not be dispatched after a <button> element is\n * disabled while the mouse is pressed. This may require the user to click twice\n * when incrementing or decrementing the value from the minimum or maximum\n * value. To work around this issue, we use a <div> element instead of a\n * <button> element for the increment and decrement buttons.\n */}\n {hideActions ? null : (\n <>\n <ActionIconButton\n icon={CaretLeftIcon}\n {...decrementButtonProps}\n elementType=\"div\"\n size={size}\n style={{ height: \"100%\", ...(isFloating && { borderRadius: 0 }) }}\n className=\"BaselineUI-Pagination-DecrementButton\"\n isExcludedFromRovingFocus={true}\n isDisabled={isDisabled || decrementButtonProps.isDisabled}\n />\n {variant === \"floating\" && <Separator orientation=\"vertical\" />}\n </>\n )}\n <div\n className={countContainerCn({ isFocused, variant, size })}\n aria-label={intl.formatMessage(\"pageXofY\", {\n arg0: inputProps.value,\n arg1: state.maxValue,\n })}\n >\n <input\n {...mergeProps(inputProps, focusProps)}\n ref={inputRef}\n className={classNames(\"BaselineUI-Pagination-Input\", countInputCn)}\n style={{\n width: `${inputProps.value?.toString().length || 1}ch`,\n }}\n />\n {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}\n <span className={totalCountCn({ isDisabled })}>\n / <NumberFormat value={state.maxValue as number} />\n </span>\n </div>\n {hideActions ? null : (\n <>\n {variant === \"floating\" && <Separator orientation=\"vertical\" />}\n <ActionIconButton\n icon={CaretRightIcon}\n {...incrementButtonProps}\n elementType=\"div\"\n size={size}\n style={{ height: \"100%\", ...(isFloating && { borderRadius: 0 }) }}\n className=\"BaselineUI-Pagination-IncrementButton\"\n isExcludedFromRovingFocus={true}\n isDisabled={isDisabled || incrementButtonProps.isDisabled}\n />\n </>\n )}\n </div>\n );\n },\n);\n\nPagination.displayName = \"Pagination\";\n",
|
|
43
|
+
"Panel": "import { Panel as ResizablePanel } from \"react-resizable-panels\";\nimport { forwardRef, useImperativeHandle, useRef } from \"react\";\nimport { useId } from \"react-aria\";\n\nimport { classNames } from \"../../utils\";\n\nimport type { ImperativePanelHandle as ResizableImperativePanelHandle } from \"react-resizable-panels\";\nimport type { ImperativePanelHandle, PanelProps } from \"./Panel.types\";\n\nexport const Panel = forwardRef<ImperativePanelHandle, PanelProps>(\n ({ className, isCollapsible = false, ...props }, ref) => {\n const panelRef = useRef<ResizableImperativePanelHandle>(null);\n const id = useId();\n\n // provide only a subset of imperative methods\n useImperativeHandle(ref, () => ({\n resize: panelRef.current?.resize,\n }));\n\n return (\n <ResizablePanel\n className={classNames(\"BaselineUI-Panel\", className)}\n collapsible={isCollapsible}\n ref={panelRef}\n // there is a bug in library where it doesn't correctly apply aria-controls to the resizable panel without an explicit id here\n id={id}\n {...props}\n />\n );\n },\n);\n\nPanel.displayName = \"Panel\";\n",
|
|
44
|
+
"PointPicker": "import React from \"react\";\n\nimport type { PointPickerProps } from \"./PointPicker.types\";\n\nexport interface Coordinates {\n x: number;\n y: number;\n}\n\nexport interface PointPickerContextValue {\n node: HTMLDivElement | null;\n setNode: (node: HTMLDivElement | null) => void;\n coordinates: Coordinates | null;\n setCoordinates: (coordinates: Coordinates | null) => void;\n displayId: string | undefined;\n setDisplayId: (id: string | undefined) => void;\n isDisabled: boolean;\n boundsRef: React.MutableRefObject<DOMRect | null>;\n cursorNode: HTMLElement | null;\n setCursorNode: (node: HTMLElement | null) => void;\n children: React.ReactElement | null;\n setChildren: (children: React.ReactElement | null) => void;\n}\n\nconst defaultContext: PointPickerContextValue = {\n node: null,\n setNode: () => {},\n coordinates: null,\n setCoordinates: () => {},\n displayId: undefined,\n setDisplayId: () => {},\n boundsRef: { current: null },\n isDisabled: false,\n cursorNode: null,\n setCursorNode: () => {},\n children: null,\n setChildren: () => {},\n};\n\nexport const PointPickerContext =\n React.createContext<PointPickerContextValue>(defaultContext);\n\nexport const PointPicker = React.memo<PointPickerProps>(\n ({ children: _children, isDisabled = false }) => {\n const [node, setNode] = React.useState<HTMLDivElement | null>(null);\n const [cursorNode, setCursorNode] = React.useState<HTMLElement | null>(\n null,\n );\n const [children, setChildren] = React.useState<React.ReactElement | null>(\n null,\n );\n\n const boundsRef = React.useRef<DOMRect>(null);\n const [coordinates, setCoordinates] = React.useState<Coordinates | null>(\n null,\n );\n\n const [displayId, setDisplayId] = React.useState<string | undefined>();\n\n const contextValue = React.useMemo(\n () => ({\n node,\n setNode,\n coordinates,\n setCoordinates,\n displayId,\n setDisplayId,\n boundsRef,\n isDisabled,\n cursorNode,\n setCursorNode,\n children,\n setChildren,\n }),\n [\n node,\n coordinates,\n displayId,\n setCoordinates,\n setNode,\n boundsRef,\n isDisabled,\n cursorNode,\n setCursorNode,\n children,\n setChildren,\n ],\n );\n\n return (\n <PointPickerContext.Provider value={contextValue}>\n {_children}\n </PointPickerContext.Provider>\n );\n },\n);\n\nPointPicker.displayName = \"PointPicker\";\n",
|
|
45
|
+
"Popover": "import React, { useMemo } from \"react\";\nimport { useOverlayTrigger } from \"react-aria\";\nimport { useOverlayTriggerState } from \"react-stately\";\n\nimport type { OverlayTriggerAria } from \"react-aria\";\nimport type { PopoverProps } from \"./Popover.types\";\nimport type { MenuTriggerState, OverlayTriggerState } from \"react-stately\";\n\nexport const PopoverContext = React.createContext<{\n state: OverlayTriggerState | MenuTriggerState | null;\n overlayTriggerAria: OverlayTriggerAria;\n triggerRef?: React.RefObject<HTMLElement>;\n}>({\n state: null,\n overlayTriggerAria: {\n triggerProps: {},\n overlayProps: {},\n },\n triggerRef: undefined,\n});\n\nexport const Popover: React.FC<PopoverProps> = ({ children, ...rest }) => {\n const ref = React.useRef<HTMLButtonElement>(null);\n const state = useOverlayTriggerState(rest);\n const overlayTriggerAria = useOverlayTrigger(rest, state);\n\n const popoverValue = useMemo(\n () => ({\n state,\n overlayTriggerAria,\n triggerRef: ref,\n }),\n [state, overlayTriggerAria],\n );\n\n return (\n <PopoverContext.Provider value={popoverValue}>\n {children}\n </PopoverContext.Provider>\n );\n};\n",
|
|
46
|
+
"Portal": "import React, { useContext } from \"react\";\nimport { Overlay } from \"react-aria\";\n\nimport { classNames } from \"../../utils\";\nimport { usePortalContainer } from \"../../hooks/usePortalContainer\";\nimport { ThemeProvider } from \"../ThemeProvider\";\nimport { ThemeProviderContext } from \"../ThemeProvider/ThemeProvider.context\";\n\nimport type { PortalProps } from \"./Portal.types\";\n\nexport const Portal = React.forwardRef<HTMLDivElement, PortalProps>(\n (\n {\n className,\n style,\n children,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n portalContainer,\n ...props\n },\n ref,\n ) => {\n const { theme, shouldHandleVirtualKeyboard } =\n useContext(ThemeProviderContext);\n const _portalContainer = usePortalContainer();\n\n return (\n <Overlay {...props} portalContainer={portalContainer ?? _portalContainer}>\n <ThemeProvider\n ref={ref}\n theme={theme}\n shouldHandleVirtualKeyboard={shouldHandleVirtualKeyboard}\n style={style}\n className={classNames(\"BaselineUI-Portal\", className)}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n >\n {children}\n </ThemeProvider>\n </Overlay>\n );\n },\n);\n\nPortal.displayName = \"Portal\";\n",
|
|
47
|
+
"PortalContainerProvider": "import React from \"react\";\n\nimport type { PortalContainerProviderProps } from \"./PortalContainerProvider.types\";\n\nexport const PortalContainerProviderContext = React.createContext<\n HTMLElement | undefined\n>(undefined);\n\nexport const PortalContainerProvider: React.FC<\n PortalContainerProviderProps\n> = ({ portalContainer, children }) => {\n return (\n <PortalContainerProviderContext.Provider value={portalContainer}>\n {children}\n </PortalContainerProviderContext.Provider>\n );\n};\n\nPortalContainerProvider.displayName = \"PortalContainerProvider\";\n",
|
|
48
|
+
"Preview": "import { TrashIcon } from \"@baseline-ui/icons/16\";\nimport React from \"react\";\nimport { mergeProps, useFocusRing, useHover, usePress } from \"react-aria\";\n\nimport { classNames, filterTruthyValues, invariant } from \"../../utils\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { deleteBtnCn, previewCanvasCn, previewCn } from \"./Preview.css\";\nimport { previewCn as imagePreviewCn } from \"../ImageDropZone/ImageDropZone.css\";\n\nimport type { PreviewProps } from \"./Preview.types\";\n\nexport const Preview = React.forwardRef<HTMLDivElement, PreviewProps>(\n (\n {\n className,\n style,\n isDisabled,\n isInline = true,\n imageAlt,\n imageSrc,\n svgSrc,\n textValue,\n textStyle,\n onPress,\n onDelete,\n deleteAriaLabel,\n addAriaLabel,\n accent = \"positive\",\n \"data-block-id\": blockId,\n \"data-block-class\": blockGroup,\n ...ariaLabellingProps\n },\n ref,\n ) => {\n const { focusProps, isFocused, isFocusVisible } = useFocusRing({\n within: true,\n });\n const { isHovered, hoverProps } = useHover({ isDisabled });\n const { pressProps } = usePress({\n isDisabled,\n onPress,\n });\n\n invariant(\n imageSrc || svgSrc || textValue,\n \"Preview must have content. You must provide either an `imageSrc`, `svgSrc`, or `textValue` prop.\",\n );\n\n const dataAttrs = filterTruthyValues({\n \"data-hovered\": isHovered,\n \"data-focus-visible\": isFocusVisible,\n \"data-focused\": isFocused,\n \"data-disabled\": isDisabled,\n });\n\n return (\n <div\n {...ariaLabellingProps}\n className={classNames(\n previewCn({\n isInline,\n isFocusVisible,\n isDisabled,\n isHovered,\n accent,\n }),\n \"BaselineUI-Preview\",\n className,\n )}\n data-block-id={blockId}\n data-block-class={blockGroup}\n aria-label={ariaLabellingProps[\"aria-label\"] || imageAlt || textValue}\n style={style}\n ref={ref}\n >\n {typeof svgSrc === \"string\" ? (\n <button\n disabled={isDisabled}\n {...mergeProps(hoverProps, focusProps, pressProps, dataAttrs)}\n className={previewCanvasCn}\n aria-label={addAriaLabel}\n dangerouslySetInnerHTML={{ __html: svgSrc }}\n />\n ) : (\n <button\n {...mergeProps(hoverProps, focusProps, pressProps, dataAttrs)}\n disabled={isDisabled}\n style={textStyle}\n aria-label={addAriaLabel}\n className={previewCanvasCn}\n >\n {imageSrc ? (\n <img\n src={imageSrc}\n alt={imageAlt}\n className={imagePreviewCn({ isInline, isDisabled })}\n />\n ) : null}\n\n {!!svgSrc && typeof svgSrc === \"object\" && svgSrc}\n\n {textValue}\n </button>\n )}\n\n {onDelete ? (\n <ActionIconButton\n isDisabled={isDisabled}\n className={deleteBtnCn}\n aria-label={deleteAriaLabel}\n size=\"xs\"\n icon={TrashIcon}\n onPress={onDelete}\n />\n ) : null}\n </div>\n );\n },\n);\n\nPreview.displayName = \"Preview\";\n",
|
|
49
|
+
"ProgressBar": "import React from \"react\";\nimport { useId, useProgressBar } from \"react-aria\";\n\nimport { classNames } from \"../../utils\";\nimport {\n progressBarCn,\n progressBarHeaderCn,\n progressBarHighlightedCn,\n progressBarLabelCn,\n progressBarTrackCn,\n progressBarTrackWrapperCn,\n progressBarWrapperCn,\n progressValueCn,\n} from \"./ProgressBar.css\";\nimport { helperTextCn } from \"../TextInput/TextInput.css\";\n\nimport type { ProgressBarProps } from \"./ProgressBar.types\";\n\nexport const ProgressBar = React.forwardRef<HTMLDivElement, ProgressBarProps>(\n (\n {\n className,\n style,\n variant = \"active\",\n showValue,\n description,\n errorMessage,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const descriptionId = useId();\n\n const { labelProps, progressBarProps } = useProgressBar({\n ...rest,\n \"aria-describedby\": description\n ? descriptionId\n : rest[\"aria-describedby\"],\n });\n const { minValue = 0, maxValue = 100, value = 0, label } = rest;\n\n const percentage = (value - minValue) / (maxValue - minValue);\n const barWidth = `${Math.round(percentage * 100)}%`;\n\n const output = (\n <output className={progressValueCn({ hasLabel: !!label })}>\n {progressBarProps[\"aria-valuetext\"]}\n </output>\n );\n\n const message = variant === \"error\" ? errorMessage : description;\n return (\n <div\n {...progressBarProps}\n style={style}\n className={classNames(\n progressBarCn,\n \"BaselineUI-ProgressBar\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n ref={ref}\n >\n {label ? (\n <div className={progressBarHeaderCn}>\n <label {...labelProps} className={progressBarLabelCn}>\n {label}\n </label>\n {showValue ? output : null}\n </div>\n ) : null}\n <div className={progressBarWrapperCn}>\n <div className={progressBarTrackWrapperCn}>\n <div className={progressBarTrackCn}>\n <div\n style={{ width: barWidth }}\n className={progressBarHighlightedCn({ variant })}\n />\n </div>\n\n {!label && showValue ? output : null}\n </div>\n {message ? (\n <div\n className={helperTextCn({\n hasError: variant === \"error\",\n })}\n id={descriptionId}\n >\n {message}\n </div>\n ) : null}\n </div>\n </div>\n );\n },\n);\n\nProgressBar.displayName = \"ProgressBar\";\n",
|
|
50
|
+
"ProgressSpinner": "import React from \"react\";\nimport {\n CheckmarkCircleFilledIcon as Check16,\n ErrorAltCircleFilledIcon as Error16,\n} from \"@baseline-ui/icons/16\";\nimport {\n CheckCircleFilledIcon as Check20,\n ErrorAltCircleFilledIcon as Error20,\n} from \"@baseline-ui/icons/20\";\nimport { useProgressBar } from \"react-aria\";\nimport { assignInlineVars } from \"@vanilla-extract/dynamic\";\n\nimport { classNames } from \"../../utils\";\nimport {\n circleBackgroundCn,\n circleCn,\n circumferenceVar,\n errorColorCn,\n labelCn,\n radialProgressCn,\n radialProgressContainerCn,\n successColorCn,\n} from \"./ProgressSpinner.css\";\n\nimport type { ProgressSpinnerProps } from \"./ProgressSpinner.types\";\n\nconst svgSizeMap = {\n sm: 16,\n md: 20,\n};\n\nconst iconSizeMap = {\n sm: [Check16, Error16],\n md: [Check20, Error20],\n};\n\nexport const ProgressSpinner = React.forwardRef<\n HTMLDivElement,\n ProgressSpinnerProps\n>(\n (\n {\n className,\n style,\n size = \"md\",\n variant = \"active\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...props\n },\n ref,\n ) => {\n const { labelProps, progressBarProps } = useProgressBar({\n ...props,\n isIndeterminate: true,\n });\n\n const svgSize = svgSizeMap[size];\n const radius = svgSize / 2 - 2;\n\n const c = 2 * radius * Math.PI;\n\n const Icon = iconSizeMap[size][variant === \"success\" ? 0 : 1];\n\n return (\n <div\n className={classNames(\n radialProgressCn,\n \"BaselineUI-ProgressSpinner\",\n className,\n )}\n ref={ref}\n style={{\n ...assignInlineVars({\n [circumferenceVar]: `${c}px`,\n }),\n ...style,\n }}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n >\n <div {...progressBarProps} className={radialProgressContainerCn}>\n {(variant === \"active\" || variant === \"inactive\") && (\n <svg\n height={svgSize}\n width={svgSize}\n viewBox={`0 0 ${svgSize} ${svgSize}`}\n >\n <circle\n role=\"presentation\"\n className={circleBackgroundCn}\n cx={svgSize / 2}\n cy={svgSize / 2}\n r={radius}\n />\n {variant === \"active\" && (\n <circle\n role=\"presentation\"\n className={circleCn}\n cx={svgSize / 2}\n cy={svgSize / 2}\n r={radius}\n />\n )}\n </svg>\n )}\n\n {variant === \"success\" && (\n <Icon className={successColorCn} size={svgSize} />\n )}\n {variant === \"error\" && (\n <Icon className={errorColorCn} size={svgSize} />\n )}\n </div>\n\n {props.label ? (\n <span {...labelProps} className={labelCn}>\n {props.label}\n </span>\n ) : null}\n </div>\n );\n },\n);\n\nProgressSpinner.displayName = \"ProgressSpinner\";\n",
|
|
51
|
+
"RadioGroup": "import React from \"react\";\nimport { mergeProps, useRadioGroup } from \"react-aria\";\nimport { useObjectRef } from \"@react-aria/utils\";\nimport { useRadioGroupState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { radioGroupListContainerCn } from \"./RadioGroup.css\";\nimport { RadioItem } from \"./RadioItem\";\nimport {\n textInputLabelCn,\n textInputLabelContainerCn,\n textInputRootCn,\n} from \"../TextInput/TextInput.css\";\nimport { getMessage } from \"../TextInput/utils/getMessage\";\nimport { disabledCn } from \"../../utils/styleMixins.css\";\n\nimport type { RadioGroupProps } from \"./RadioGroup.types\";\n\nexport const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(\n (\n {\n className,\n style,\n optionsContainerClassName,\n optionsContainerStyle,\n items,\n labelPosition = \"top\",\n renderOption,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n \"aria-details\": ariaDetails,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const divRef = useObjectRef(ref);\n const state = useRadioGroupState(rest);\n const { radioGroupProps, labelProps, descriptionProps, errorMessageProps } =\n useRadioGroup(\n {\n ...rest,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n \"aria-details\": ariaDetails,\n },\n state,\n divRef,\n );\n\n const message = getMessage({\n ...rest,\n descriptionProps,\n errorMessageProps,\n labelPosition,\n });\n\n const dataAttrs = filterTruthyValues({\n \"data-orientation\": rest.orientation,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-disabled\": rest.isDisabled,\n \"data-readonly\": rest.isReadOnly,\n });\n\n return (\n <div\n {...mergeProps(radioGroupProps, dataAttrs)}\n className={classNames(\n textInputRootCn({ labelPosition }),\n \"BaselineUI-RadioGroup\",\n className,\n )}\n style={style}\n ref={divRef}\n >\n {rest.label || message ? (\n <div\n className={textInputLabelContainerCn({\n labelPosition,\n hasMessage: !!message,\n })}\n >\n <span\n {...labelProps}\n className={classNames(\n textInputLabelCn,\n \"BaselineUI-RadioGroup-Label\",\n )}\n >\n {rest.label}\n </span>\n\n {labelPosition === \"start\" && message}\n </div>\n ) : null}\n\n <div\n className={classNames(\n radioGroupListContainerCn,\n { [disabledCn]: rest.isDisabled },\n optionsContainerClassName,\n )}\n style={optionsContainerStyle}\n >\n {items.map((item) => (\n <RadioItem\n key={item.id}\n state={state}\n renderOption={renderOption}\n item={item}\n {...rest}\n value={item.id}\n />\n ))}\n </div>\n\n {labelPosition === \"top\" && message}\n </div>\n );\n },\n);\n\nRadioGroup.displayName = \"RadioGroup\";\n",
|
|
52
|
+
"Reaction": "import { CheckmarkIcon as CMIMed } from \"@baseline-ui/icons/20\";\nimport { CheckmarkIcon as CMISm } from \"@baseline-ui/icons/16\";\nimport React from \"react\";\nimport { VisuallyHidden, useFocusRing, useHover, useSwitch } from \"react-aria\";\nimport { useToggleState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { NumberFormat } from \"../NumberFormat\";\nimport {\n labelCn,\n reactionCn,\n reactionCountCn,\n reactionIconWrapperCn,\n} from \"./Reaction.css\";\n\nimport type { ToggleProps } from \"react-stately\";\nimport type { AriaToggleButtonProps } from \"react-aria\";\nimport type { ReactionProps } from \"./Reaction.types\";\n\nexport const Reaction = React.forwardRef<HTMLLabelElement, ReactionProps>(\n (\n {\n className,\n count,\n style,\n size = \"md\",\n icon,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const shared = {\n isRequired: false,\n } as AriaToggleButtonProps & ToggleProps;\n\n const Icon = icon || (size === \"md\" ? CMIMed : CMISm);\n\n if (!rest[\"aria-label\"] && !rest[\"aria-labelledby\"]) {\n console.warn(\n \"You must provide either an `aria-label` or `aria-labelledby` prop to the `Reaction` component.\",\n );\n }\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const state = useToggleState({ ...rest, ...shared });\n const { inputProps, isPressed, isDisabled, isReadOnly, isSelected } =\n useSwitch({ ...rest, ...shared }, state, inputRef);\n const { focusProps, isFocused, isFocusVisible } = useFocusRing();\n const { hoverProps, isHovered } = useHover({});\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n \"data-disabled\": isDisabled,\n \"data-hovered\": isHovered,\n \"data-pressed\": isPressed,\n \"data-selected\": isSelected,\n });\n\n return (\n <label\n ref={ref}\n style={style}\n className={classNames(labelCn, \"BaselineUI-Reaction\", className)}\n {...dataAttrs}\n >\n <VisuallyHidden>\n <input {...inputProps} {...focusProps} ref={inputRef} />\n </VisuallyHidden>\n <div\n {...hoverProps}\n className={reactionCn({\n isSelected,\n isFocusVisible,\n isHovered,\n isReadOnly,\n isDisabled,\n })}\n >\n <div\n className={reactionIconWrapperCn({\n isDisabled,\n isReadOnly,\n })}\n >\n <Icon size={size === \"md\" ? 20 : 16} />\n </div>\n <div\n className={reactionCountCn({\n isSelected: state.isSelected,\n isDisabled,\n isReadOnly,\n size,\n })}\n >\n <NumberFormat value={count} />\n </div>\n </div>\n </label>\n );\n },\n);\n\nReaction.displayName = \"Reaction\";\n",
|
|
53
|
+
"ScrollControlButton": "import { ArrowDownCircleFilledIcon } from \"@baseline-ui/icons/16\";\nimport { useInteractionModality } from \"@react-aria/interactions\";\nimport { getOwnerDocument, getOwnerWindow, mergeRefs } from \"@react-aria/utils\";\nimport React, { useCallback, useEffect } from \"react\";\nimport { mergeProps, useButton, useFocusRing, useHover } from \"react-aria\";\nimport { AnimatePresence, motion } from \"motion/react\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { iconColorCn, tagCn, textColorCn } from \"../TagGroup/TagGroup.css\";\nimport { scrollControlButtonCn } from \"./ScrollControlButton.css\";\n\nimport type { ScrollControlButtonProps } from \"./ScrollControlButton.types\";\n\n// Since not all browsers calculate scroll height same way, we need to add a buffer to the bottom of the scroll.\nconst SCROLL_BUFFER = 2;\n\nexport const ScrollControlButton = React.forwardRef<\n HTMLButtonElement,\n ScrollControlButtonProps\n>(\n (\n {\n className,\n style,\n hideForKeyboard,\n scrollRef,\n label,\n delay = 1500,\n smoothScroll = true,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n },\n ref,\n ) => {\n const buttonRef = React.useRef<HTMLButtonElement>(null);\n const modality = useInteractionModality();\n const [isScrolling, setIsScrolling] = React.useState(false);\n const getIsAtBottom = useCallback(() => {\n const scrollElement =\n scrollRef?.current || getOwnerDocument(buttonRef.current).body;\n\n // we need to check if scrollElement is using column-reverse direction\n // because scrollHeight is calculated differently in that case\n const scrollElementComputedStyle =\n getOwnerWindow(scrollElement).getComputedStyle(scrollElement);\n\n if (scrollElementComputedStyle.flexDirection === \"column-reverse\") {\n return scrollElement.scrollTop + SCROLL_BUFFER >= 0;\n }\n\n return (\n scrollElement.scrollTop + scrollElement.clientHeight + SCROLL_BUFFER >=\n scrollElement.scrollHeight\n );\n }, [scrollRef]);\n const [isAtBottom, setIsAtBottom] = React.useState(getIsAtBottom);\n\n const { buttonProps, isPressed } = useButton(\n {\n onPress: () => {\n const scrollElement =\n scrollRef?.current || getOwnerDocument(buttonRef.current).body;\n\n scrollElement.scrollTo({\n top: scrollElement.scrollHeight,\n behavior: smoothScroll ? \"smooth\" : \"auto\",\n });\n },\n },\n buttonRef,\n );\n const { focusProps, isFocused, isFocusVisible } = useFocusRing();\n const { hoverProps, isHovered } = useHover({});\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-pressed\": isPressed,\n \"data-focus-visible\": isFocusVisible,\n \"data-hovered\": isHovered,\n \"data-focused\": isFocused,\n });\n\n useEffect(() => {\n setIsAtBottom(getIsAtBottom());\n }, [getIsAtBottom]);\n\n useEffect(() => {\n const scrollElement =\n scrollRef?.current || getOwnerDocument(buttonRef.current).body;\n\n const isScrollendSupported =\n \"onscrollend\" in getOwnerWindow(scrollElement);\n\n const handleScrollend = () => {\n setTimeout(() => {\n setIsScrolling(false);\n setIsAtBottom(getIsAtBottom());\n }, delay);\n };\n\n const scrollHandler = () => {\n setIsScrolling(true);\n if (!isScrollendSupported) {\n handleScrollend();\n }\n };\n\n scrollElement.addEventListener(\"scroll\", scrollHandler);\n scrollElement.addEventListener(\"scrollend\", handleScrollend);\n\n return () => {\n scrollElement.removeEventListener(\"scroll\", scrollHandler);\n scrollElement.removeEventListener(\"scrollend\", handleScrollend);\n };\n }, [scrollRef, delay, getIsAtBottom]);\n\n return (\n <AnimatePresence initial={false}>\n {(hideForKeyboard ? modality !== \"keyboard\" : true) &&\n !isScrolling &&\n !isAtBottom ? (\n <motion.button\n initial={{\n opacity: 0,\n transform: \"translateX(-50%) translateY(10px)\",\n }}\n animate={{\n opacity: 1,\n transform: \"translateX(-50%) translateY(0)\",\n }}\n exit={{\n opacity: 0,\n transform: \"translateX(-50%) translateY(10px)\",\n }}\n transition={{ duration: 0.3 }}\n {...(mergeProps(\n buttonProps,\n focusProps,\n hoverProps,\n dataAttrs,\n ) as React.ComponentProps<typeof motion.button>)}\n className={classNames(\n scrollControlButtonCn,\n tagCn({\n isFocusVisible,\n variant: \"high-contrast\",\n isHovered,\n }),\n textColorCn({ variant: \"high-contrast\" }),\n \"BaselineUI-ScrollControlButton\",\n className,\n )}\n style={style}\n ref={mergeRefs(ref, buttonRef)}\n >\n <ArrowDownCircleFilledIcon\n size={16}\n className={iconColorCn({ variant: \"high-contrast\" })}\n />\n {label}\n </motion.button>\n ) : null}\n </AnimatePresence>\n );\n },\n);\n\nScrollControlButton.displayName = \"ScrollControlButton\";\n",
|
|
54
|
+
"SearchInput": "import { XIcon } from \"@baseline-ui/icons/16\";\nimport { XIcon as XIcon12 } from \"@baseline-ui/icons/12\";\nimport { SearchIcon } from \"@baseline-ui/icons/24\";\nimport { SearchIcon as SI20 } from \"@baseline-ui/icons/20\";\nimport React from \"react\";\nimport { mergeProps, useFocusRing, useSearchField } from \"react-aria\";\nimport { useSearchFieldState } from \"react-stately\";\nimport { FieldInputContext } from \"react-aria-components\";\nimport { filterDOMProps } from \"@react-aria/utils\";\nimport {\n removeDataAttributes,\n useContextProps,\n} from \"react-aria-components/src/utils\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { inputCn, searchCn } from \"./SearchInput.css\";\n\nimport type { SearchInputProps } from \"./SearchInput.types\";\n\nconst sizeToIconMap = {\n sm: SI20,\n md: SearchIcon,\n lg: SearchIcon,\n};\n\nexport const SearchInput = React.forwardRef<HTMLDivElement, SearchInputProps>(\n (\n {\n className,\n size = \"md\",\n variant = \"primary\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n style,\n isClearFocusable = false,\n ...props\n },\n ref,\n ) => {\n let searchFieldRef = React.useRef<HTMLInputElement>(null);\n\n [props, searchFieldRef as unknown] = useContextProps(\n props,\n searchFieldRef,\n FieldInputContext,\n );\n\n const state = useSearchFieldState(props);\n\n const { inputProps, clearButtonProps } = useSearchField(\n {\n ...removeDataAttributes(props),\n \"aria-haspopup\": false,\n \"aria-autocomplete\": \"none\",\n type: \"search\",\n },\n state,\n searchFieldRef,\n );\n\n const { focusProps, isFocusVisible, isFocused } = useFocusRing({\n within: true,\n isTextInput: true,\n });\n\n const Icon = sizeToIconMap[size];\n\n const CloseIcon = size === \"sm\" ? XIcon12 : XIcon;\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-focused\": isFocused,\n \"data-disabled\": props.isDisabled,\n \"data-focus-visible\": isFocusVisible,\n });\n\n const domProps = filterDOMProps(props, { global: true });\n delete domProps.id;\n\n return (\n <div\n {...domProps}\n {...dataAttrs}\n className={classNames(\n searchCn({\n size,\n isFocused,\n variant,\n hasText: !!state.value,\n isDisabled: props.isDisabled,\n }),\n \"BaselineUI-SearchInput\",\n className,\n )}\n style={style}\n ref={ref}\n >\n <Icon size={size === \"sm\" ? 20 : 24} />\n <input\n {...mergeProps(inputProps, focusProps)}\n className={inputCn}\n ref={searchFieldRef}\n />\n {state.value !== \"\" && (\n <ActionIconButton\n icon={CloseIcon}\n {...clearButtonProps}\n size={size === \"sm\" ? \"xs\" : \"sm\"}\n variant=\"secondary\"\n isExcludedFromRovingFocus={!isClearFocusable}\n excludeFromTabOrder={!isClearFocusable}\n {...(isClearFocusable\n ? {\n onPressStart: (e) => {\n clearButtonProps.onPressStart?.(e);\n clearButtonProps.onPress?.(e);\n },\n }\n : {})}\n />\n )}\n </div>\n );\n },\n);\n\nSearchInput.displayName = \"SearchInput\";\n",
|
|
55
|
+
"Select": "import React, { useContext, useEffect } from \"react\";\nimport { HiddenSelect, mergeProps, useSelect } from \"react-aria\";\nimport { useSelectState } from \"react-stately\";\nimport {\n AutocompleteStateContext,\n CollectionRendererContext,\n ListBoxContext,\n ListStateContext,\n} from \"react-aria-components\";\nimport { removeDataAttributes } from \"react-aria-components/src/utils\";\n\nimport { classNames, invariant } from \"../../utils\";\nimport { defineMessages, useI18n, usePortalContainer } from \"../../hooks\";\nimport { UNSAFE_ListBox as ListBox } from \"../UNSAFE_ListBox\";\nimport { PopoverContent } from \"../Popover/PopoverContent\";\nimport {\n actionButtonCn,\n footerContainerCn,\n listBoxCn,\n popoverContentCn,\n searchCn,\n} from \"./Select.css\";\nimport { SelectButton } from \"./SelectButton\";\nimport {\n textInputLabelCn,\n textInputLabelContainerCn,\n textInputRootCn,\n} from \"../TextInput/TextInput.css\";\nimport { getMessage } from \"../TextInput/utils/getMessage\";\nimport { ListCollectionBuilder } from \"../shared/components/ListCollectionBuilder\";\nimport { ListBoxItemContent } from \"../UNSAFE_ListBox/ListBox\";\nimport { ActionButton } from \"../ActionButton\";\nimport { Box } from \"../Box\";\nimport { Separator } from \"../Separator\";\nimport { SearchInput } from \"../SearchInput\";\n\nimport type { ListItem, ListOption } from \"../shared/types/List\";\nimport type { SelectState } from \"react-stately\";\nimport type { SelectProps } from \"./Select.types\";\nimport type { Node } from \"@react-types/shared\";\n\nexport const SelectContext = React.createContext<{\n state: SelectState<ListItem> | null;\n popoverAnchorRef: React.RefObject<HTMLElement> | null;\n}>({ state: null, popoverAnchorRef: null });\n\nconst SelectCore = React.forwardRef<\n HTMLDivElement,\n SelectProps & {\n state: SelectState<ListItem>;\n }\n>(\n (\n {\n className,\n style,\n labelPosition = \"top\",\n variant = \"primary\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n onTriggerPress,\n placement = \"bottom start\",\n triggerClassName,\n triggerStyle,\n state,\n optionStyle,\n optionClassName,\n hideSelectAll,\n hideClear,\n renderTrigger = ({\n buttonProps,\n selectedValue,\n valueProps,\n isOpen,\n ref,\n selectionMode,\n onRemove,\n maxCount,\n }) => {\n return (\n <SelectButton\n {...buttonProps}\n className={triggerClassName}\n style={triggerStyle}\n isOpen={isOpen}\n isReadOnly={rest.isReadOnly}\n ref={ref}\n variant={variant}\n validationState={rest.validationState}\n valueProps={valueProps}\n value={selectedValue}\n fallbackValue={rest.placeholder}\n selectionMode={selectionMode}\n onRemove={onRemove}\n maxCount={maxCount}\n />\n );\n },\n ...rest\n },\n ref,\n ) => {\n const { popoverAnchorRef } = useContext(SelectContext);\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const { formatMessage } = useI18n();\n const autoCompleteContext = useContext(AutocompleteStateContext);\n const { isVirtualized } = useContext(CollectionRendererContext);\n\n const _portalContainer = usePortalContainer(rest.portalContainer);\n const {\n labelProps,\n triggerProps,\n valueProps,\n menuProps,\n descriptionProps,\n errorMessageProps,\n } = useSelect(\n {\n ...removeDataAttributes(rest),\n validationState: rest.validationState === \"error\" ? \"invalid\" : \"valid\",\n },\n state,\n triggerRef,\n );\n\n const message = getMessage({\n ...rest,\n descriptionProps,\n errorMessageProps,\n labelPosition,\n });\n\n const selectedValue =\n (state.selectedItems as Node<ListOption>[])?.map(\n (item) => item.value as ListOption,\n ) ?? null;\n\n return (\n <>\n <div\n style={style}\n className={classNames(\n textInputRootCn({ labelPosition }),\n \"BaselineUI-Select\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n ref={ref}\n >\n {rest.label || message ? (\n <div\n className={textInputLabelContainerCn({\n labelPosition,\n hasMessage: !!message,\n })}\n >\n {rest.label ? (\n <div\n {...labelProps}\n className={classNames(\n textInputLabelCn,\n \"BaselineUI-Select-Label\",\n )}\n >\n {rest.label}\n </div>\n ) : null}\n\n {labelPosition === \"start\" && message}\n </div>\n ) : null}\n <HiddenSelect\n state={state}\n triggerRef={triggerRef}\n isDisabled={rest.isDisabled}\n label={rest.label}\n name={rest.name}\n />\n\n {renderTrigger({\n buttonProps: mergeProps(\n {\n onPressStart: onTriggerPress,\n \"aria-labelledby\": labelProps.id,\n },\n triggerProps,\n ),\n selectedValue,\n valueProps,\n isOpen: state.isOpen,\n ref: triggerRef,\n selectionMode: rest.selectionMode ?? \"single\",\n onRemove: (keys) => {\n state.selectionManager.setSelectedKeys(\n new Set(\n [...state.selectionManager.selectedKeys].filter(\n (key) => !keys.has(key),\n ),\n ),\n );\n },\n maxCount: rest.maxCount ?? 2,\n })}\n\n {labelPosition === \"top\" && message}\n </div>\n\n {state ? (\n <ListStateContext.Provider value={state}>\n <PopoverContent\n placement={placement}\n state={state}\n portalContainer={_portalContainer}\n offset={2}\n triggerRef={popoverAnchorRef || triggerRef}\n style={style}\n className=\"BaselineUI-Select-Popover\"\n {...rest}\n >\n <Box\n display=\"flex\"\n flex={1}\n flexDirection=\"column\"\n className={popoverContentCn}\n >\n {autoCompleteContext ? (\n <>\n <SearchInput\n size=\"sm\"\n aria-label={formatMessage(messages.search)}\n placeholder={formatMessage(messages.search)}\n className={classNames(\n searchCn,\n \"BaselineUI-Select-SearchInput\",\n )}\n autoFocus={true}\n />\n <Separator />\n </>\n ) : null}\n\n <ListBoxContext.Provider value={menuProps}>\n <ListBox\n label={rest.label}\n escapeKeyBehavior=\"none\"\n className={listBoxCn({ isVirtualized })}\n optionStyle={optionStyle}\n optionClassName={optionClassName}\n />\n </ListBoxContext.Provider>\n {state.selectionManager.selectionMode === \"multiple\" &&\n (!hideSelectAll || !hideClear) && (\n <Box\n display=\"flex\"\n flexDirection=\"row\"\n className={footerContainerCn}\n >\n {!hideSelectAll && (\n <ActionButton\n variant=\"ghost\"\n size=\"md\"\n label={formatMessage(messages.selectAll)}\n onPress={() => {\n if (state.selectionManager.isSelectAll) return;\n // Select all enabled items only (exclude disabled keys)\n const allKeys = [...state.collection.getKeys()];\n const enabledKeys = allKeys.filter(\n (key) =>\n !state.selectionManager.disabledKeys.has(key) &&\n state.selectionManager.canSelectItem(key),\n );\n state.selectionManager.setSelectedKeys(\n new Set(enabledKeys),\n );\n }}\n className={actionButtonCn}\n />\n )}\n {!hideSelectAll && !hideClear && (\n <Separator orientation=\"vertical\" />\n )}\n {!hideClear && (\n <ActionButton\n variant=\"ghost\"\n label={formatMessage(messages.clear)}\n size=\"md\"\n onPress={() => {\n if (state.selectionManager.isEmpty) return;\n state.selectionManager.clearSelection();\n }}\n className={actionButtonCn}\n />\n )}\n </Box>\n )}\n </Box>\n </PopoverContent>\n </ListStateContext.Provider>\n ) : null}\n </>\n );\n },\n);\n\nSelectCore.displayName = \"SelectCore\";\n\nconst SelectStandalone = React.forwardRef<HTMLDivElement, SelectProps>(\n ({ collection, ...props }, ref) => {\n const state = useSelectState({\n ...props,\n collection,\n validationState: props.validationState === \"error\" ? \"invalid\" : \"valid\",\n children: undefined,\n });\n\n return <SelectCore {...props} ref={ref} state={state} />;\n },\n);\n\nSelectStandalone.displayName = \"SelectStandalone\";\n\nexport const Select = React.forwardRef<HTMLDivElement, SelectProps>(\n ({ optionStyle, optionClassName, ...props }, ref) => {\n const { state } = useContext(SelectContext);\n\n useEffect(() => {\n invariant(\n state || props.items,\n \"Select: A `state` that can be passed via context or `items` are required.\",\n );\n }, [props.items, state]);\n\n return state ? (\n <SelectCore\n {...props}\n optionStyle={optionStyle}\n optionClassName={optionClassName}\n ref={ref}\n state={state}\n />\n ) : (\n <ListCollectionBuilder\n items={props.items}\n listBoxProps={{\n renderOption: (item, props) => (\n <ListBoxItemContent\n {...props}\n item={item}\n selectionIcon={\n props.selectionMode === \"multiple\" ? \"checkbox\" : \"checkmark\"\n }\n />\n ),\n optionStyle,\n optionClassName,\n }}\n >\n {(collection) => (\n <SelectStandalone collection={collection} {...props} ref={ref} />\n )}\n </ListCollectionBuilder>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n\nconst messages = defineMessages({\n selectAll: {\n id: \"selectAll\",\n defaultMessage: \"Select All\",\n },\n clear: {\n id: \"clear\",\n defaultMessage: \"Clear\",\n },\n search: {\n id: \"search\",\n defaultMessage: \"Search\",\n },\n});\n",
|
|
56
|
+
"Separator": "import React from \"react\";\nimport { useSeparator } from \"react-aria\";\n\nimport { classNames } from \"../../utils\";\nimport { separatorCn } from \"./Separator.css\";\n\nimport type { SeparatorProps } from \"./Separator.types\";\n\nexport const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(\n (\n {\n className,\n style,\n variant = \"primary\",\n orientation = \"horizontal\",\n UNSAFE_omitRole,\n elementType: ElementType = \"div\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const { separatorProps } = useSeparator({\n ...rest,\n orientation,\n elementType: ElementType,\n });\n\n return (\n <ElementType\n {...separatorProps}\n // @ts-expect-error - `className` is not a valid prop for `ElementType`\n className={classNames(\n separatorCn({\n isVertical: orientation === \"vertical\",\n isSecondary: variant === \"secondary\",\n }),\n \"BaselineUI-Separator\",\n className,\n )}\n data-orientation={orientation}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n role={UNSAFE_omitRole ? undefined : separatorProps.role}\n ref={ref}\n />\n );\n },\n);\n\nSeparator.displayName = \"Separator\";\n",
|
|
57
|
+
"Slider": "import { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport {\n VisuallyHidden,\n mergeProps,\n useFocusRing,\n useHover,\n useLocale,\n useNumberFormatter,\n useSlider,\n useSliderThumb,\n} from \"react-aria\";\nimport { useSliderState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport {\n sliderCn,\n sliderContentCn,\n sliderHeaderCn as sliderHeaderCn,\n sliderLabelCn,\n sliderThumbCn,\n sliderThumbHandleCn,\n sliderTrackCn,\n sliderTrackHighlightCn,\n} from \"./Slider.css\";\nimport { NumberInput } from \"../NumberInput\";\n\nimport type { SliderState } from \"react-stately\";\nimport type { AriaSliderProps } from \"react-aria\";\nimport type { RefObject } from \"react\";\nimport type { SliderProps } from \"./Slider.types\";\n\nexport const Slider = React.forwardRef<HTMLDivElement, SliderProps>(\n (\n {\n className,\n style,\n onChange,\n onChangeEnd,\n numberFormatOptions,\n isDisabled,\n step,\n minValue,\n maxValue,\n value,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-details\": ariaDetails,\n \"aria-describedby\": ariaDescribedBy,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n defaultValue,\n isReadOnly,\n label,\n id,\n includeNumberInput,\n },\n ref,\n ) => {\n const trackRef = React.useRef<HTMLDivElement>(null);\n\n const multiProps = {\n onChange:\n onChange === undefined\n ? undefined\n : (vals: number[]) => {\n onChange(vals[0]);\n },\n isDisabled: isDisabled || isReadOnly,\n step,\n minValue,\n maxValue,\n onChangeEnd:\n onChangeEnd === undefined\n ? undefined\n : (vals: number[]) => {\n onChangeEnd(vals[0]);\n },\n value: value === undefined ? undefined : [value],\n defaultValue: defaultValue === undefined ? undefined : [defaultValue],\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-details\": ariaDetails,\n \"aria-describedby\": ariaDescribedBy,\n id,\n label,\n } as AriaSliderProps<number[]>;\n\n const numberFormatter = useNumberFormatter(numberFormatOptions);\n const state = useSliderState({\n ...multiProps,\n numberFormatter,\n });\n\n const { trackProps, groupProps, labelProps } = useSlider(\n multiProps,\n state,\n trackRef,\n );\n\n const numberInput = (\n <NumberInput\n variant=\"ghost\"\n showStepper={false}\n aria-label={ariaLabel}\n aria-labelledby={labelProps.id}\n value={state.getThumbValue(0)}\n formatOptions={numberFormatOptions}\n onChange={(val) => {\n state.setThumbValue(0, val);\n }}\n style={{ width: 55, textAlign: \"right\", flexShrink: 0 }}\n />\n );\n\n return (\n <div\n {...groupProps}\n style={style}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n className={classNames(sliderCn, \"BaselineUI-Slider\", className)}\n >\n {label ? (\n <div className={sliderHeaderCn}>\n <label\n {...labelProps}\n className={classNames(\n sliderLabelCn({\n isDisabled,\n hasNumberInput: !!includeNumberInput,\n }),\n \"BaselineUI-Slider-Label\",\n )}\n >\n {label}\n </label>\n\n {includeNumberInput ? numberInput : null}\n </div>\n ) : null}\n <div className={sliderContentCn}>\n <div\n {...trackProps}\n className={classNames(sliderTrackCn, \"BaselineUI-Slider-Track\")}\n ref={mergeRefs(trackRef, ref)}\n data-testid=\"track\"\n >\n <Thumb\n state={state}\n trackRef={trackRef}\n isDisabled={isDisabled}\n isReadOnly={isReadOnly}\n />\n </div>\n\n {includeNumberInput && !label ? numberInput : null}\n </div>\n </div>\n );\n },\n);\n\nSlider.displayName = \"Slider\";\n\nfunction Thumb({\n state,\n trackRef,\n isDisabled,\n isReadOnly,\n}: {\n state: SliderState;\n trackRef: RefObject<HTMLDivElement>;\n isDisabled?: boolean;\n isReadOnly?: boolean;\n}) {\n const inputRef = React.useRef(null);\n const { thumbProps, inputProps, isDragging } = useSliderThumb(\n {\n trackRef,\n inputRef,\n },\n state,\n );\n\n const { focusProps, isFocused, isFocusVisible } = useFocusRing();\n const { hoverProps, isHovered } = useHover({ isDisabled });\n const { direction } = useLocale();\n\n const dataAttrs = filterTruthyValues({\n \"data-disabled\": isDisabled,\n \"data-readonly\": isReadOnly,\n \"data-hovered\": isHovered,\n \"data-focus-visible\": isFocusVisible,\n \"data-dragging\": isDragging,\n \"data-focused\": isFocused,\n });\n\n return (\n <>\n <div\n className={classNames(\n sliderTrackHighlightCn({\n isDisabled,\n isFocusVisible,\n active: isDragging,\n isHovered,\n }),\n \"BaselineUI-Slider-TrackHighlight\",\n )}\n style={{\n width:\n direction === \"rtl\"\n ? `calc(100% - ${thumbProps.style?.left})`\n : thumbProps.style?.left,\n }}\n data-testid=\"track-highlight\"\n />\n <div\n {...mergeProps(thumbProps, hoverProps, dataAttrs)}\n className={classNames(\n sliderThumbHandleCn,\n \"BaselineUI-Slider-ThumbHandle\",\n )}\n data-testid=\"thumb\"\n >\n <div\n className={classNames(\n sliderThumbCn({\n isDisabled,\n isFocusVisible,\n active: isDragging,\n isReadOnly,\n isHovered,\n }),\n \"BaselineUI-Slider-Thumb\",\n )}\n />\n <VisuallyHidden>\n <input ref={inputRef} {...mergeProps(inputProps, focusProps)} />\n </VisuallyHidden>\n </div>\n </>\n );\n}\n",
|
|
58
|
+
"Switch": "import React from \"react\";\nimport { useToggleState } from \"react-stately\";\nimport {\n VisuallyHidden,\n mergeProps,\n useFocusRing,\n useHover,\n useSwitch,\n} from \"react-aria\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport {\n switchCn,\n switchKnobCn,\n switchLabelCn,\n switchRootCn,\n switchStatusLabelCn,\n switchWrapperCn,\n} from \"./Switch.css\";\n\nimport type { SwitchProps } from \"./Switch.types\";\n\nexport const Switch = React.forwardRef<HTMLLabelElement, SwitchProps>(\n (\n {\n className,\n label,\n labelPosition = \"top\",\n statusLabel,\n style,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const switchRef = React.useRef<HTMLInputElement>(null);\n const state = useToggleState(rest);\n const { inputProps, isPressed } = useSwitch(\n { ...rest, children: label },\n state,\n switchRef,\n );\n const { isFocusVisible, focusProps } = useFocusRing();\n const { hoverProps, isHovered } = useHover({ isDisabled: rest.isDisabled });\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-focused\": isFocusVisible,\n \"data-disabled\": rest.isDisabled,\n \"data-focus-visible\": isFocusVisible,\n \"data-selected\": state.isSelected,\n \"data-readonly\": rest.isReadOnly,\n \"data-hovered\": isHovered,\n \"data-pressed\": isPressed,\n });\n\n const switchComponent = (\n <div\n className={classNames(\n switchCn({\n isSelected: state.isSelected,\n isDisabled: !!rest.isDisabled,\n isFocused: isFocusVisible,\n isReadOnly: !!rest.isReadOnly,\n }),\n \"BaselineUI-Switch-Track\",\n )}\n >\n <div\n className={classNames(\n switchKnobCn({\n isSelected: state.isSelected,\n isDisabled: rest.isDisabled,\n isReadOnly: rest.isReadOnly,\n }),\n \"BaselineUI-Switch-Knob\",\n )}\n data-testid=\"switch-knob\"\n />\n </div>\n );\n\n return (\n <label\n {...mergeProps(dataAttrs, hoverProps)}\n className={classNames(\n switchRootCn({\n labelPosition,\n }),\n \"BaselineUI-Switch\",\n className,\n )}\n style={style}\n ref={ref}\n >\n {label ? (\n <span\n className={classNames(switchLabelCn, \"BaselineUI-Switch-Label\")}\n >\n {label}\n </span>\n ) : null}\n <VisuallyHidden>\n <input {...mergeProps(inputProps, focusProps)} ref={switchRef} />\n </VisuallyHidden>\n\n {statusLabel ? (\n <div className={switchWrapperCn({ labelPosition })}>\n <span\n className={classNames(\n \"BaselineUI-Switch-Status-Label\",\n switchStatusLabelCn,\n )}\n >\n {state.isSelected ? statusLabel.on : statusLabel.off}\n </span>\n {switchComponent}\n </div>\n ) : (\n switchComponent\n )}\n </label>\n );\n },\n);\n\nSwitch.displayName = \"Switch\";\n",
|
|
59
|
+
"Tabs": "import React, { useMemo } from \"react\";\nimport { Item } from \"react-stately\";\n\nimport { TabsBase } from \"./TabsBase\";\n\nimport type { ItemProps, TabsProps } from \"./Tabs.types\";\n\nexport const Tabs: React.ForwardRefExoticComponent<\n TabsProps & React.RefAttributes<HTMLDivElement>\n> = React.forwardRef<HTMLDivElement, TabsProps>(function Tabs(\n { children, ...rest }: TabsProps,\n ref: React.Ref<HTMLDivElement>,\n): React.ReactElement | null {\n const items = useMemo(\n () =>\n React.Children.map(\n children as React.ReactElement<ItemProps>[],\n (child) => {\n if (\n !child ||\n !React.isValidElement(child) ||\n typeof child.type === \"string\"\n ) {\n return null;\n }\n\n return {\n ...child.props,\n id: child.props.value,\n };\n },\n ).filter(Boolean) as ItemProps[],\n [children],\n );\n\n return (\n <TabsBase {...rest} ref={ref} items={items}>\n {(item) => (\n <Item title={item.title} key={item.key}>\n {item.children}\n </Item>\n )}\n </TabsBase>\n );\n});\n\nTabs.displayName = \"Tabs\";\n",
|
|
60
|
+
"TagGroup": "import { mergeRefs } from \"@react-aria/utils\";\nimport React, { useMemo } from \"react\";\nimport { useTagGroup } from \"react-aria\";\nimport { useListState } from \"react-stately\";\n\nimport { classNames } from \"../../utils\";\nimport { useCollectionChildren } from \"../shared/collection\";\nimport { tagGroupCn } from \"./TagGroup.css\";\nimport { Tag } from \"./Tag\";\n\nimport type { ListOption } from \"../ListBox\";\nimport type { TagGroupProps } from \"./TagGroup.types\";\n\nexport const TagGroup = React.forwardRef<HTMLDivElement, TagGroupProps>(\n (\n {\n className,\n style,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n items,\n size = \"md\",\n isDisabled = false,\n ...props\n },\n ref,\n ) => {\n const _items = useMemo(\n () =>\n items.map((item) => ({\n ...item,\n data: {\n variant: item.variant,\n },\n })),\n [items],\n );\n\n const _children = useCollectionChildren<ListOption>();\n\n const tagGroupRef = React.useRef<HTMLDivElement>(null);\n const state = useListState({\n ...props,\n items: _items,\n children: _children,\n disabledKeys: isDisabled\n ? items.map((item) => item.id)\n : props.disabledKeys,\n });\n const { gridProps } = useTagGroup(props, state, tagGroupRef);\n\n return (\n <div\n {...gridProps}\n className={classNames(tagGroupCn, \"BaselineUI-TagGroup\", className)}\n style={style}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n ref={mergeRefs(ref, tagGroupRef)}\n >\n {[...state.collection].map((item) => {\n return (\n <Tag\n key={item.key}\n state={state}\n item={item}\n variant={props.variant}\n size={size}\n />\n );\n })}\n </div>\n );\n },\n);\n\nTagGroup.displayName = \"TagGroup\";\n",
|
|
61
|
+
"TaggedPagination": "import { CaretLeftIcon, CaretRightIcon } from \"@baseline-ui/icons/24\";\nimport React, { useCallback, useState } from \"react\";\nimport { useGranularEffect } from \"granular-hooks\";\nimport { useId, useTextField } from \"react-aria\";\nimport { useControlledState } from \"@react-stately/utils\";\n\nimport { inputCn, labelCn, taggedPaginationCn } from \"./TaggedPagination.css\";\nimport { Box } from \"../Box\";\nimport { classNames } from \"../../utils\";\nimport { ActionIconButton } from \"../ActionIconButton\";\nimport { Separator } from \"../Separator\";\n\nimport type { TaggedPaginationProps } from \"./TaggedPagination.types\";\nimport type { KeyboardEvent } from \"@react-types/shared\";\n\nexport const TaggedPagination = React.forwardRef<\n HTMLDivElement,\n TaggedPaginationProps\n>(\n (\n {\n className,\n style,\n size = \"md\",\n isDisabled,\n decrementAriaLabel,\n incrementAriaLabel,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n label,\n onChange,\n onBlur,\n onKeyDown,\n minValue = Number.NEGATIVE_INFINITY,\n maxValue = Number.POSITIVE_INFINITY,\n defaultValue,\n value,\n description = (value) => `${value} / ${maxValue}`,\n onInputChange,\n onChangeSuccess,\n step = 1,\n valueToLabelMap,\n ...rest\n },\n ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null);\n const labelId = useId();\n const descriptionId = useId();\n\n const getLabelFromValue = useCallback(\n (value: number) => {\n return valueToLabelMap\n ? (valueToLabelMap.get(value) ?? value.toString())\n : value.toString();\n },\n [valueToLabelMap],\n );\n\n const getValueFromLabel = useCallback(\n (label: string): number => {\n if (valueToLabelMap) {\n const entries = [...valueToLabelMap.entries()];\n const match = entries.find(([, mapped]) => mapped === label);\n if (match) return match[0];\n }\n return Number.parseInt(label);\n },\n [valueToLabelMap],\n );\n\n const [currentCount, setCurrentCount] = useControlledState(\n value,\n defaultValue ?? 1,\n onChangeSuccess,\n );\n const [textboxValue, setTextboxValue] = useState(\n getLabelFromValue(currentCount) ?? \"\",\n );\n\n const isDecrementButtonDisabled = currentCount <= minValue || isDisabled;\n const isIncrementButtonDisabled = currentCount >= maxValue || isDisabled;\n\n const handleSubmit = useCallback(\n (_textboxValue: string) => {\n if (!_textboxValue) return;\n\n onChange?.(_textboxValue);\n const parsedCount = getValueFromLabel(_textboxValue);\n\n if (Number.isNaN(parsedCount)) {\n setTextboxValue(getLabelFromValue(currentCount) ?? \"\");\n return;\n }\n\n if (\n parsedCount >= minValue &&\n parsedCount <= maxValue &&\n !isDisabled &&\n !rest.isReadOnly\n ) {\n setCurrentCount(parsedCount);\n } else {\n setTextboxValue(getLabelFromValue(currentCount));\n }\n },\n [\n onChange,\n getValueFromLabel,\n minValue,\n maxValue,\n isDisabled,\n rest.isReadOnly,\n getLabelFromValue,\n currentCount,\n setCurrentCount,\n ],\n );\n\n const isValidValue = useCallback(\n (value: number) => {\n return value >= minValue && value <= maxValue;\n },\n [minValue, maxValue],\n );\n\n const handleBlur = useCallback(\n (e: React.FocusEvent<HTMLInputElement, Element>) => {\n handleSubmit(textboxValue);\n\n onBlur?.(e);\n },\n [textboxValue, handleSubmit, onBlur],\n );\n\n const increment = useCallback(() => {\n if (isIncrementButtonDisabled) return;\n const newValue = Math.min(currentCount + step, maxValue);\n if (isValidValue(newValue)) {\n setCurrentCount(newValue);\n }\n }, [\n currentCount,\n isIncrementButtonDisabled,\n isValidValue,\n maxValue,\n setCurrentCount,\n step,\n ]);\n\n const decrement = useCallback(() => {\n if (isDecrementButtonDisabled) return;\n const newValue = Math.max(currentCount - step, minValue);\n if (isValidValue(newValue)) {\n setCurrentCount(newValue);\n }\n }, [\n currentCount,\n isDecrementButtonDisabled,\n isValidValue,\n minValue,\n setCurrentCount,\n step,\n ]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isDisabled || rest.isReadOnly) return;\n\n switch (e.key) {\n case \"Enter\": {\n handleSubmit(textboxValue);\n\n break;\n }\n case \"ArrowUp\": {\n increment();\n break;\n }\n case \"ArrowDown\": {\n decrement();\n\n break;\n }\n }\n\n onKeyDown?.(e);\n },\n [\n isDisabled,\n rest.isReadOnly,\n onKeyDown,\n handleSubmit,\n textboxValue,\n increment,\n decrement,\n ],\n );\n\n useGranularEffect(\n () => {\n setTextboxValue(getLabelFromValue(currentCount));\n },\n [currentCount],\n [getLabelFromValue],\n );\n\n const { inputProps } = useTextField(\n {\n ...rest,\n value: textboxValue?.toString(),\n isDisabled,\n onBlur: handleBlur,\n onChange: (text) => {\n setTextboxValue(text);\n onInputChange?.(text);\n },\n onKeyDown: handleKeyDown,\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n },\n inputRef,\n );\n\n const _description =\n typeof description === \"function\"\n ? description(currentCount)\n : description;\n\n return (\n <Box\n className={classNames(taggedPaginationCn[size], className)}\n display=\"flex\"\n flexDirection=\"row\"\n alignItems=\"center\"\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n ref={ref}\n aria-disabled={isDisabled}\n >\n <span id={labelId} className={labelCn({ size, isDisabled })}>\n {label}\n </span>\n <Box\n display=\"flex\"\n flexDirection=\"row\"\n borderRadius=\"sm\"\n borderColor=\"border.subtle\"\n borderStyle=\"solid\"\n borderWidth={1}\n style={{ height: \"inherit\" }}\n >\n <ActionIconButton\n icon={CaretLeftIcon}\n elementType=\"div\"\n size={size}\n variant=\"secondary\"\n style={{ height: \"100%\" }}\n className=\"BaselineUI-TaggedPagination-DecrementButton\"\n isExcludedFromRovingFocus={true}\n isDisabled={\n isDisabled || isDecrementButtonDisabled || rest.isReadOnly\n }\n excludeFromTabOrder={true}\n onPress={decrement}\n aria-label={decrementAriaLabel}\n tooltip={true}\n />\n <Separator orientation=\"vertical\" />\n <input\n {...inputProps}\n className={inputCn({ size })}\n min={minValue}\n max={maxValue}\n />\n <Separator orientation=\"vertical\" />\n <ActionIconButton\n icon={CaretRightIcon}\n elementType=\"div\"\n size={size}\n variant=\"secondary\"\n style={{ height: \"100%\" }}\n className=\"BaselineUI-TaggedPagination-IncrementButton\"\n isExcludedFromRovingFocus={true}\n isDisabled={\n isDisabled || isIncrementButtonDisabled || rest.isReadOnly\n }\n excludeFromTabOrder={true}\n onPress={increment}\n aria-label={incrementAriaLabel}\n tooltip={true}\n />\n </Box>\n\n {_description ? (\n <span id={descriptionId} className={labelCn({ size, isDisabled })}>\n {_description}\n </span>\n ) : null}\n </Box>\n );\n },\n);\n\nTaggedPagination.displayName = \"TaggedPagination\";\n",
|
|
62
|
+
"Text": "import React from \"react\";\n\nimport { classNames } from \"../../utils\";\nimport { textCn } from \"./Text.css\";\n\nimport type { TextProps } from \"./Text.types\";\n\nexport const Text = React.forwardRef<HTMLDivElement, TextProps>(\n (\n {\n elementType: ElementType = \"span\",\n type = \"title\",\n children,\n size = \"md\",\n ...domProps\n },\n ref,\n ) => {\n return (\n <ElementType\n {...domProps}\n className={classNames(\n textCn({ size, type }),\n \"BaselineUI-Text\",\n domProps.className,\n )}\n ref={ref}\n >\n {children}\n </ElementType>\n );\n },\n);\n\nText.displayName = \"Text\";\n",
|
|
63
|
+
"TextInput": "import React from \"react\";\nimport { mergeProps, useFocusRing, useTextField } from \"react-aria\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { SharedInput } from \"../shared/components/SharedInput\";\nimport { getMessage } from \"./utils/getMessage\";\nimport { textInputCn } from \"./TextInput.css\";\n\nimport type { TextInputProps } from \"./TextInput.types\";\n\nexport const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(\n (\n {\n className,\n validationState,\n variant = \"primary\",\n labelPosition = \"top\",\n inputStyle,\n inputClassName,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null);\n const {\n inputProps,\n labelProps,\n descriptionProps,\n errorMessageProps,\n validationErrors,\n } = useTextField(\n {\n ...rest,\n validationState: validationState\n ? validationState === \"error\"\n ? \"invalid\"\n : \"valid\"\n : undefined,\n },\n inputRef,\n );\n\n const _errorMessage = rest.errorMessage || validationErrors.join(\" \");\n\n const message = getMessage({\n ...rest,\n errorMessage: _errorMessage,\n descriptionProps,\n errorMessageProps,\n labelPosition,\n });\n\n if (_errorMessage) {\n validationState = \"error\";\n }\n\n const { isFocused, focusProps, isFocusVisible } = useFocusRing();\n\n const dataAttrs = filterTruthyValues({\n \"data-readonly\": rest.isReadOnly,\n \"data-disabled\": rest.isDisabled,\n \"data-validation-state\": validationState,\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n });\n\n return (\n <SharedInput\n variant={variant}\n labelProps={labelProps}\n isFocused={isFocused}\n message={message}\n ref={ref}\n labelPosition={labelPosition}\n className={classNames(\"BaselineUI-TextInput\", className)}\n validationState={validationState}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n {...rest}\n >\n <input\n {...mergeProps(inputProps, focusProps, dataAttrs)}\n className={classNames(\n textInputCn({\n isDisabled: rest.isDisabled,\n labelPosition,\n }),\n \"BaselineUI-TextInput-Input\",\n inputClassName,\n )}\n style={inputStyle}\n ref={inputRef}\n />\n </SharedInput>\n );\n },\n);\n\nTextInput.displayName = \"TextInput\";\n",
|
|
64
|
+
"ThemeProvider": "import React, { useEffect, useMemo } from \"react\";\nimport { assignInlineVars } from \"@vanilla-extract/dynamic\";\nimport { themeVars, themes } from \"@baseline-ui/tokens\";\nimport { mergeProps, useLocale } from \"react-aria\";\nimport { MotionConfig } from \"motion/react\";\n\nimport { classNames } from \"../../utils\";\nimport { useDevice } from \"../../hooks\";\nimport { contentsCn } from \"../../utils/styleMixins.css\";\nimport { useUserPreferences } from \"./hooks/useUserPreferences\";\nimport { ThemeProviderContext } from \"./ThemeProvider.context\";\nimport { minTouchTargetSizeVar } from \"./ThemeProvider.css\";\nimport { directionVar } from \"../I18nProvider\";\n\nimport type { ThemeProviderProps } from \"./ThemeProvider.types\";\n\nconst DEFAULT_THEMES = {\n LIGHT: themes.base.light,\n DARK: themes.base.dark,\n};\n\nexport const ThemeProvider = React.forwardRef<\n HTMLDivElement,\n ThemeProviderProps\n>(\n (\n {\n theme = \"system\",\n children,\n setTheme,\n onThemeChange,\n className,\n style,\n UNSAFE_domProps,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n reducedMotion: _reducedMotion = \"user\",\n shouldHandleVirtualKeyboard = true,\n minTouchTargetSize = 24,\n },\n ref,\n ) => {\n const { direction: dir } = useLocale();\n\n const { colorScheme, reducedMotion, highContrast, transparency } =\n useUserPreferences();\n\n const { name, ..._theme } = useMemo(() => {\n switch (theme) {\n case \"system\": {\n return colorScheme === \"light\"\n ? DEFAULT_THEMES.LIGHT\n : DEFAULT_THEMES.DARK;\n }\n case \"light\": {\n return DEFAULT_THEMES.LIGHT;\n }\n case \"dark\": {\n return DEFAULT_THEMES.DARK;\n }\n default: {\n return theme;\n }\n }\n }, [colorScheme, theme]);\n\n const device = useDevice();\n\n const dataAttrs = {\n \"data-theme\": name,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-reduced-motion\":\n _reducedMotion === \"user\" ? reducedMotion : _reducedMotion === \"always\",\n \"data-high-contrast\": highContrast,\n \"data-transparency\": transparency,\n \"data-device\": device,\n };\n\n const themeValue = useMemo(\n () => ({\n theme: _theme,\n setTheme: (\n theme: Parameters<\n Exclude<ThemeProviderProps[\"onThemeChange\"], undefined>\n >[0],\n ) => {\n setTheme?.(theme);\n onThemeChange?.(theme);\n },\n shouldHandleVirtualKeyboard,\n }),\n [_theme, setTheme, onThemeChange, shouldHandleVirtualKeyboard],\n );\n\n useEffect(() => {\n if (setTheme) {\n console.warn(\n \"ThemeProvider: The `setTheme` prop is deprecated and will be removed in a future version of Baseline UI. Use the `onThemeChange` prop instead.\",\n );\n }\n }, [setTheme]);\n\n return (\n <ThemeProviderContext.Provider value={themeValue}>\n <MotionConfig reducedMotion={_reducedMotion}>\n <div\n {...mergeProps(UNSAFE_domProps, dataAttrs)}\n className={classNames(\n contentsCn,\n \"BaselineUI-ThemeProvider\",\n className,\n )}\n style={{\n ...assignInlineVars(themeVars, _theme),\n ...assignInlineVars({\n [directionVar]: dir === \"rtl\" ? \"-1\" : \"1\",\n [minTouchTargetSizeVar]: `${minTouchTargetSize}px`,\n }),\n ...style,\n }}\n dir={dir}\n ref={ref}\n >\n {children}\n </div>\n </MotionConfig>\n </ThemeProviderContext.Provider>\n );\n },\n);\n\nThemeProvider.displayName = \"ThemeProvider\";\n",
|
|
65
|
+
"TimeField": "import React from \"react\";\nimport {\n VisuallyHidden,\n useFocusRing,\n useLocale,\n useTimeField,\n} from \"react-aria\";\nimport { useTimeFieldState } from \"react-stately\";\n\nimport { classNames } from \"../../utils\";\nimport { Box } from \"../Box\";\nimport { SharedInput } from \"../shared/components/SharedInput\";\nimport { getMessage } from \"../TextInput/utils/getMessage\";\nimport { DateTimeSegment } from \"./DateTimeSegment\";\nimport { timeFieldCn } from \"./TimeField.css\";\n\nimport type { TimeFieldStateOptions } from \"react-stately\";\nimport type { TimeFieldProps } from \"./TimeField.types\";\n\nexport const TimeField = React.forwardRef<HTMLDivElement, TimeFieldProps>(\n (\n {\n className,\n labelPosition,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const { locale } = useLocale();\n\n const props = {\n ...rest,\n validationState: undefined,\n isInvalid: rest.validationState === \"error\" || undefined,\n } as TimeFieldStateOptions;\n\n const state = useTimeFieldState({\n ...props,\n locale,\n });\n\n const wrapperRef = React.useRef<HTMLDivElement>(null);\n const {\n labelProps,\n fieldProps,\n descriptionProps,\n errorMessageProps,\n validationErrors,\n isInvalid,\n inputProps,\n } = useTimeField(props, state, wrapperRef);\n\n const _errorMessage = rest.errorMessage || validationErrors.join(\" \");\n\n const message = getMessage({\n ...rest,\n errorMessage: _errorMessage,\n descriptionProps,\n errorMessageProps,\n labelPosition,\n });\n\n const { focusProps, isFocused } = useFocusRing({ within: true });\n\n return (\n <SharedInput\n className={classNames(\"BaselineUI-TimeField\", className)}\n labelProps={labelProps}\n fieldProps={fieldProps}\n ref={ref}\n wrapperRef={wrapperRef}\n wrapperClassName={timeFieldCn}\n labelPosition={labelPosition}\n message={message}\n isFocused={isFocused}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n isInvalid={isInvalid}\n {...rest}\n >\n <VisuallyHidden>\n <input {...inputProps} />\n </VisuallyHidden>\n <Box\n display=\"flex\"\n flexDirection=\"row\"\n paddingRight=\"md\"\n {...focusProps}\n >\n {state.segments.map((segment, i) => (\n <DateTimeSegment key={i} segment={segment} state={state} />\n ))}\n </Box>\n </SharedInput>\n );\n },\n);\n\nTimeField.displayName = \"TimeField\";\n",
|
|
66
|
+
"Toast": "import React, { useMemo } from \"react\";\nimport { useToastQueue } from \"react-stately\";\nimport { useToast, useToastRegion } from \"react-aria\";\nimport { useObjectRef } from \"@react-aria/utils\";\n\nimport { classNames } from \"../../utils\";\nimport { Portal } from \"../Portal\";\nimport { InlineAlert } from \"../InlineAlert\";\nimport { toastCn, toastRegionCn } from \"./Toast.css\";\nimport { usePortalContainer } from \"../../hooks\";\n\nimport type { StylingProps } from \"../../utils\";\nimport type { GlobalToastRegionProps, ToastContent } from \"./Toast.types\";\nimport type { ToastState } from \"react-stately\";\nimport type { AriaToastProps, AriaToastRegionProps } from \"react-aria\";\n\ninterface ToastProps<T> extends AriaToastProps<T> {\n state: ToastState<T>;\n}\n\nconst noop = () => {};\n\nfunction Toast<T extends ToastContent>({ state, ...props }: ToastProps<T>) {\n const ref = React.useRef(null);\n const {\n toastProps,\n contentProps,\n titleProps,\n descriptionProps,\n closeButtonProps,\n } = useToast(props, state, ref);\n\n const elementProps = useMemo(() => {\n return {\n root: toastProps,\n title: titleProps,\n content: contentProps,\n description: descriptionProps,\n close: closeButtonProps,\n };\n }, [\n toastProps,\n titleProps,\n contentProps,\n descriptionProps,\n closeButtonProps,\n ]);\n\n const { canClose = true, ...rest } = props.toast.content;\n\n return (\n <InlineAlert\n elementProps={elementProps}\n {...rest}\n className={classNames(\n toastCn({\n variant: rest.variant || \"info\",\n }),\n rest.className,\n )}\n onClose={canClose ? noop : undefined}\n />\n );\n}\n\ninterface ToastRegionProps<T> extends AriaToastRegionProps, StylingProps {\n state: ToastState<T>;\n}\n\nconst ToastRegion = React.forwardRef<\n HTMLDivElement,\n ToastRegionProps<ToastContent>\n>(({ state, style, className, ...props }, ref) => {\n const divRef = useObjectRef(ref);\n\n const { regionProps } = useToastRegion(props, state, divRef);\n\n const _style = { zIndex: 1, ...style, ...regionProps.style };\n\n return (\n <div\n {...regionProps}\n ref={divRef}\n className={classNames(toastRegionCn, className)}\n style={_style}\n >\n {state.visibleToasts.map((toast) => (\n <Toast key={toast.key} toast={toast} state={state} />\n ))}\n </div>\n );\n});\n\nToastRegion.displayName = \"ToastRegion\";\n\nexport const GlobalToastRegion = React.forwardRef<\n HTMLDivElement,\n GlobalToastRegionProps\n>(({ toastQueue, portalContainer, ...props }, ref) => {\n const state = useToastQueue<ToastContent>(toastQueue);\n const _portalContainer = usePortalContainer(portalContainer);\n\n return state.visibleToasts.length > 0 ? (\n <Portal portalContainer={_portalContainer} ref={ref}>\n <ToastRegion state={state} {...props} />\n </Portal>\n ) : null;\n});\n\nGlobalToastRegion.displayName = \"GlobalToastRegion\";\n",
|
|
67
|
+
"ToggleButton": "import { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport { mergeProps } from \"react-aria\";\nimport { useToggleState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport { useCustomToggleButton } from \"../shared/buttons\";\nimport { toggleButtonCn, toggleButtonIconCn } from \"./ToggleButton.css\";\n\nimport type { ToggleButtonProps } from \"./ToggleButton.types\";\n\nconst sizeToSvgSize = {\n sm: 16,\n md: 20,\n lg: 24,\n};\n\nexport const ToggleButton = React.forwardRef<\n HTMLButtonElement,\n ToggleButtonProps\n>(\n (\n {\n className,\n style,\n iconStart: IconStart,\n size = \"md\",\n elementType: ElementType = \"button\",\n variant = \"toolbar\",\n ...rest\n },\n ref,\n ) => {\n const state = useToggleState(rest);\n const {\n buttonProps,\n isPressed,\n ref: _ref,\n isFocusVisible,\n isFocused,\n isHovered,\n uiStateOptions,\n } = useCustomToggleButton(rest, state);\n\n const dataAttrs = filterTruthyValues({\n \"data-block-id\": rest[\"data-block-id\"],\n \"data-block-class\": rest[\"data-block-class\"],\n \"data-focused\": isFocused,\n \"data-focus-visible\": isFocusVisible,\n \"data-disabled\": rest.isDisabled,\n \"data-hovered\": isHovered,\n \"data-pressed\": isPressed,\n \"data-selected\": state.isSelected,\n });\n\n return (\n <ElementType\n {...mergeProps(buttonProps, dataAttrs)}\n className={classNames(\n toggleButtonCn({\n isHovered,\n isFocusVisible,\n isSelected: state.isSelected,\n size,\n isDisabled: rest.isDisabled,\n variant,\n }),\n \"BaselineUI-ToggleButton\",\n typeof className === \"function\"\n ? className(uiStateOptions)\n : className,\n )}\n style={typeof style === \"function\" ? style(uiStateOptions) : style}\n ref={mergeRefs(_ref, ref)}\n >\n {IconStart ? (\n <IconStart\n size={sizeToSvgSize[size]}\n className={toggleButtonIconCn({\n isDisabled: rest.isDisabled,\n isSelected: state.isSelected,\n })}\n />\n ) : null}\n {rest.label}\n </ElementType>\n );\n },\n);\n\nToggleButton.displayName = \"ToggleButton\";\n",
|
|
68
|
+
"ToggleIconButton": "import { mergeRefs, useObjectRef } from \"@react-aria/utils\";\nimport React, { useMemo } from \"react\";\nimport { mergeProps } from \"react-aria\";\nimport { useToggleState } from \"react-stately\";\n\nimport { classNames, filterTruthyValues } from \"../../utils\";\nimport {\n useButtonTooltipProps,\n useCustomToggleButton,\n} from \"../shared/buttons\";\nimport {\n btnSizeCn,\n primaryIconButtonCn,\n secondaryIconButtonCn,\n tertiaryIconButtonCn,\n toolbarIconButtonCn,\n} from \"./ToggleIconButton.css\";\nimport { Tooltip } from \"../Tooltip\";\n\nimport type { ToggleIconButtonProps } from \"./ToggleIconButton.types\";\n\nconst sizeToSvgSize = {\n xxs: 8,\n xs: 12,\n sm: 16,\n md: 20,\n lg: 24,\n};\n\nexport const ToggleIconButton = React.forwardRef<\n HTMLButtonElement,\n ToggleIconButtonProps\n>((props, ref) => {\n const buttonRef = useObjectRef(ref);\n const { tooltipProps } = useButtonTooltipProps(props, buttonRef);\n\n return tooltipProps ? (\n <Tooltip {...tooltipProps}>\n <ToggleIconButtonCore {...props} ref={ref} />\n </Tooltip>\n ) : (\n <ToggleIconButtonCore {...props} ref={ref} />\n );\n});\n\nToggleIconButton.displayName = \"ToggleIconButton\";\n\nconst ToggleIconButtonCore = React.forwardRef<\n HTMLButtonElement,\n ToggleIconButtonProps\n>(\n (\n {\n className,\n style,\n icon: Icon,\n size = \"md\",\n variant = \"primary\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...rest\n },\n ref,\n ) => {\n const state = useToggleState(rest);\n const {\n buttonProps,\n isPressed,\n ref: _ref,\n isFocusVisible,\n isFocused,\n isHovered,\n uiStateOptions,\n } = useCustomToggleButton(rest, state);\n\n const StateIcon = useMemo(() => {\n if (typeof Icon === \"function\") {\n return Icon;\n }\n\n return state.isSelected ? Icon.selected : Icon.unselected;\n }, [Icon, state.isSelected]);\n\n const cssVariants = {\n isSelected: state.isSelected,\n isHovered,\n isFocusVisible,\n isDisabled: !!rest.isDisabled,\n };\n\n const dataAttrs = filterTruthyValues({\n \"data-hovered\": isHovered,\n \"data-selected\": state.isSelected,\n \"data-focus-visible\": isFocusVisible,\n \"data-disabled\": rest.isDisabled,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-focused\": isFocused,\n \"data-pressed\": isPressed,\n });\n\n return (\n <button\n {...mergeProps(buttonProps, dataAttrs)}\n className={classNames(\n variant === \"toolbar\" && toolbarIconButtonCn(cssVariants),\n variant === \"primary\" && primaryIconButtonCn(cssVariants),\n variant === \"secondary\" && secondaryIconButtonCn(cssVariants),\n variant === \"tertiary\" && tertiaryIconButtonCn(cssVariants),\n btnSizeCn({ size }),\n \"BaselineUI-ToggleIconButton\",\n typeof className === \"function\"\n ? className(uiStateOptions)\n : className,\n )}\n style={typeof style === \"function\" ? style(uiStateOptions) : style}\n ref={mergeRefs(_ref, ref)}\n >\n <StateIcon size={sizeToSvgSize[size]} />\n </button>\n );\n },\n);\n\nToggleIconButtonCore.displayName = \"ToggleIconButtonCore\";\n",
|
|
69
|
+
"Toolbar": "import React from \"react\";\n\nimport { CollapsibleToolbar } from \"./CollapsibleToolbar\";\nimport { WrappedToolbar } from \"./WrappedToolbar\";\n\nimport type { ToolbarProps } from \"./Toolbar.types\";\n\nexport const Toolbar = React.forwardRef<HTMLDivElement, ToolbarProps>(\n (props, ref) => {\n const Component = props.isCollapsible ? CollapsibleToolbar : WrappedToolbar;\n\n return <Component {...props} ref={ref} />;\n },\n);\n\nToolbar.displayName = \"Toolbar\";\n",
|
|
70
|
+
"Tooltip": "import { mergeRefs } from \"@react-aria/utils\";\nimport React from \"react\";\nimport { useOverlayPosition, useTooltipTrigger } from \"react-aria\";\nimport { useTooltipTriggerState } from \"react-stately\";\nimport { FocusableProvider } from \"@react-aria/focus\";\n\nimport { TooltipContent } from \"./TooltipContent\";\nimport { Portal } from \"../Portal\";\nimport {\n useIntersectionObserver,\n usePortalContainer,\n useResizeObserver,\n} from \"../../hooks\";\n\nimport type { TooltipProps } from \"./Tooltip.types\";\n\nexport const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(\n (\n {\n children,\n placement = \"bottom\",\n delay = 1000,\n closeDelay = 500,\n variant = \"default\",\n size = \"md\",\n includeArrow = true,\n portalContainer,\n className,\n style,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n ...props\n },\n ref,\n ) => {\n const triggerRef = React.useRef<HTMLButtonElement>(null);\n const overlayRef = React.useRef<HTMLDivElement>(null);\n const isDisabled = !props.text || props.isDisabled;\n const state = useTooltipTriggerState({\n ...props,\n delay,\n closeDelay,\n isDisabled,\n });\n const { triggerProps, tooltipProps } = useTooltipTrigger(\n {\n ...props,\n isDisabled,\n delay,\n closeDelay,\n },\n state,\n triggerRef,\n );\n const _portalContainer = usePortalContainer(portalContainer);\n\n const {\n overlayProps,\n arrowProps,\n placement: placementAxis,\n updatePosition,\n } = useOverlayPosition({\n arrowSize: includeArrow ? 13 : 0,\n targetRef: triggerRef,\n arrowBoundaryOffset: includeArrow ? 8 : 0,\n offset: includeArrow ? 12 : 2,\n overlayRef,\n placement,\n isOpen: state.isOpen,\n ...props,\n });\n\n /**\n * Update the position of the tooltip when the intersection observer detects\n * that the tooltip is entering or leaving the viewport.\n */\n useIntersectionObserver({\n ref: overlayRef,\n threshold: 1,\n onIntersect: updatePosition,\n isDisabled: !state.isOpen,\n });\n\n /** Update the position of the tooltip when the trigger is resized. */\n useResizeObserver({\n ref: triggerRef,\n onResize: updatePosition,\n isDisabled: !state.isOpen,\n });\n\n const childrenElement = React.isValidElement(children) ? (\n <FocusableProvider {...triggerProps} ref={triggerRef}>\n {children}\n </FocusableProvider>\n ) : typeof children === \"function\" ? (\n children({ triggerProps, triggerRef })\n ) : null;\n\n return (\n <>\n {childrenElement}\n\n {state.isOpen ? (\n <Portal\n disableFocusManagement={true}\n portalContainer={_portalContainer}\n className={className}\n style={style}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n >\n <TooltipContent\n ref={mergeRefs(ref, overlayRef)}\n state={state}\n arrowProps={arrowProps}\n overlayProps={overlayProps}\n placement={placementAxis ?? \"bottom\"}\n tooltipProps={tooltipProps}\n variant={variant}\n size={size}\n includeArrow={includeArrow}\n >\n {props.text}\n </TooltipContent>\n </Portal>\n ) : null}\n </>\n );\n },\n);\n\nTooltip.displayName = \"Tooltip\";\n",
|
|
71
|
+
"Transform": "import { getOwnerDocument, mergeRefs } from \"@react-aria/utils\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useKeyboard } from \"react-aria\";\nimport {\n Draggable,\n Resizable,\n Rotatable,\n Snappable,\n makeMoveable,\n} from \"react-moveable\";\n\nimport { classNames, getHTMLElement } from \"../../utils\";\nimport { contentCn, transformCn } from \"./Transform.css\";\nimport { useGuidelines } from \"./hooks/useGuidelines\";\nimport { useScroll } from \"./hooks/useScroll\";\n\nimport type { CSSProperties, ComponentRef } from \"react\";\nimport type {\n BoundType,\n DraggableProps,\n OnDragEnd,\n OnDragStart,\n OnRender,\n ResizableProps,\n RotatableProps,\n SnappableProps,\n} from \"react-moveable\";\nimport type { TransformProps } from \"./Transform.types\";\n\nconst Moveable = makeMoveable<\n DraggableProps & ResizableProps & RotatableProps & SnappableProps\n // @ts-expect-error The types for Snappable are incorrect\n>([Draggable, Resizable, Rotatable, Snappable]);\n\nexport const Transform = React.forwardRef<HTMLDivElement, TransformProps>(\n (\n {\n className,\n children,\n isRotatable,\n isResizable,\n isDraggable,\n style,\n onTransform,\n onTransformStart,\n onTransformEnd,\n onDoubleClick,\n onDragStart,\n onDragEnd,\n onDrag,\n onResizeStart,\n onResizeEnd,\n onResize,\n onRotateStart,\n onRotateEnd,\n onRotate,\n onSnap,\n isSnappable,\n elementGuidelines,\n hideAnchorOnDrag = true,\n title,\n rotationPosition,\n autoUpdate,\n bound,\n snapRotationDegrees,\n snapRotationThreshold,\n scrollableAncestor,\n renderDirections,\n stopPropagation,\n transformOrigin,\n preventDefault,\n },\n ref,\n ) => {\n const targetRef = useRef<HTMLDivElement>(null);\n const moveableRef = useRef<ComponentRef<typeof Moveable>>(null);\n\n // eslint-disable-next-line no-restricted-globals\n const [ownerDocument, setOwnerDocument] = useState(document);\n const rootElementRef = useRef<HTMLDivElement>(\n ownerDocument.createElement(\"div\"),\n );\n\n const [transformStyle, setTransformStyle] = useState<CSSProperties>({});\n const [isDragging, setIsDragging] = useState(false);\n const [isShiftPressed, setIsShiftPressed] = useState(false);\n const [boundPosition, setBoundPosition] = useState<BoundType | undefined>();\n\n const guidelineElements = useGuidelines(elementGuidelines, ownerDocument);\n\n useLayoutEffect(() => {\n const oldRootElement = rootElementRef.current;\n rootElementRef.current = ownerDocument.createElement(\"div\");\n\n return () => {\n oldRootElement.remove();\n };\n }, [ownerDocument]);\n\n const { keyboardProps } = useKeyboard({\n onKeyDown: (e) => {\n let multiplier = e.shiftKey ? 10 : 1;\n const shouldRotate = e.metaKey || e.ctrlKey;\n\n if (shouldRotate && e.shiftKey) {\n multiplier = 45;\n }\n\n function transformOnKeyPress(offset: number, axis: \"x\" | \"y\" = \"x\") {\n if (shouldRotate) {\n moveableRef.current?.request(\n \"rotatable\",\n { deltaRotate: offset * multiplier },\n true,\n );\n } else {\n moveableRef.current?.request(\n \"draggable\",\n axis === \"x\"\n ? { deltaX: offset * multiplier }\n : { deltaY: offset * multiplier },\n true,\n );\n }\n }\n\n switch (e.key) {\n case \"ArrowRight\": {\n transformOnKeyPress(1);\n break;\n }\n case \"ArrowLeft\": {\n transformOnKeyPress(-1);\n break;\n }\n case \"ArrowUp\": {\n transformOnKeyPress(-1, \"y\");\n break;\n }\n case \"ArrowDown\": {\n transformOnKeyPress(1, \"y\");\n }\n }\n\n requestAnimationFrame(() => {\n moveableRef.current?.updateRect();\n });\n },\n });\n\n useEffect(() => {\n const ownerDocument = getOwnerDocument(targetRef.current);\n setOwnerDocument(ownerDocument);\n\n const rootElement = rootElementRef.current;\n ownerDocument.body.append(rootElementRef.current);\n\n function handleKeyDown(e: KeyboardEvent) {\n setIsShiftPressed(e.shiftKey);\n }\n\n function handleKeyUp() {\n setIsShiftPressed(false);\n }\n\n ownerDocument.addEventListener(\"keydown\", handleKeyDown);\n ownerDocument.addEventListener(\"keyup\", handleKeyUp);\n\n return () => {\n ownerDocument.removeEventListener(\"keydown\", handleKeyDown);\n ownerDocument.removeEventListener(\"keyup\", handleKeyUp);\n\n rootElement.remove();\n };\n }, []);\n\n useScroll({\n scrollable: scrollableAncestor,\n onScroll: () => {\n moveableRef.current?.updateRect();\n },\n ownerDocument,\n });\n\n const handleRender = useCallback(\n (e: OnRender) => {\n setTransformStyle((_style) => ({\n ..._style,\n ...(e.style as React.CSSProperties),\n }));\n onTransform?.(e);\n },\n [onTransform],\n );\n\n const handleDragStart = useCallback(\n (e: OnDragStart) => {\n setIsDragging(true);\n targetRef.current?.focus();\n\n if (bound) {\n const boundElement = getHTMLElement(bound, ownerDocument);\n const boundsRect = boundElement?.getBoundingClientRect();\n\n setBoundPosition({\n position: \"client\",\n left: boundsRect?.left ?? 0,\n top: boundsRect?.top ?? 0,\n right: boundsRect?.right ?? 0,\n bottom: boundsRect?.bottom ?? 0,\n });\n }\n\n onDragStart?.(e);\n },\n [bound, ownerDocument, onDragStart],\n );\n\n const handleDragEnd = useCallback(\n (e: OnDragEnd) => {\n setIsDragging(false);\n\n onDragEnd?.(e);\n },\n [onDragEnd],\n );\n\n const _children = children({\n style: {\n ...(transformStyle.width && { width: transformStyle.width }),\n ...(transformStyle.height && { height: transformStyle.height }),\n },\n });\n\n return (\n <>\n <div\n role=\"button\"\n {...keyboardProps}\n ref={mergeRefs(targetRef, ref)}\n style={{\n display: \"inline-block\",\n ...style,\n ...transformStyle,\n }}\n className={classNames(contentCn, className)}\n onDoubleClick={onDoubleClick}\n tabIndex={0}\n title={title}\n >\n {_children}\n </div>\n {createPortal(\n <Moveable\n ref={moveableRef}\n target={targetRef}\n draggable={isDraggable}\n resizable={isResizable}\n rotatable={isRotatable}\n keepRatio={isShiftPressed}\n throttleDrag={1}\n snappable={isSnappable || !!bound}\n elementGuidelines={guidelineElements}\n snapRenderThreshold={0.9}\n className={transformCn({\n isDragging: isDragging && hideAnchorOnDrag,\n })}\n onRender={handleRender}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n useResizeObserver={autoUpdate}\n useMutationObserver={autoUpdate}\n onRenderEnd={onTransformEnd}\n onDrag={onDrag}\n onResizeStart={onResizeStart}\n onResizeEnd={onResizeEnd}\n onResize={onResize}\n onRotateStart={onRotateStart}\n onRotateEnd={onRotateEnd}\n onRotate={onRotate}\n onRenderStart={onTransformStart}\n onSnap={onSnap}\n rotationPosition={rotationPosition}\n bounds={boundPosition}\n snapRotationDegrees={snapRotationDegrees}\n snapRotationThreshold={snapRotationThreshold}\n renderDirections={renderDirections}\n stopPropagation={stopPropagation}\n preventDefault={preventDefault}\n transformOrigin={transformOrigin}\n />,\n rootElementRef.current,\n )}\n </>\n );\n },\n);\n\nTransform.displayName = \"Transform\";\n",
|
|
72
|
+
"TreeView": "import { mergeProps, mergeRefs } from \"@react-aria/utils\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport { VisuallyHidden, useId } from \"react-aria\";\nimport { useTreeState } from \"react-stately\";\nimport { ControlledTreeEnvironment, Tree } from \"react-complex-tree\";\n\nimport { classNames } from \"../../utils\";\nimport { useCollectionChildren } from \"../shared/collection\";\nimport { TreeLeaf } from \"./TreeItem\";\nimport { Portal } from \"../Portal\";\n\nimport type { TreeItem, TreeItemIndex } from \"react-complex-tree\";\nimport type { TreeListItem, TreeViewProps } from \"./TreeView.types\";\n\nexport type ReactComplexTreeItem = TreeItem<{\n label: string;\n description?: string;\n icon?: React.ElementType;\n posInset: number;\n setSize: number;\n [key: string]: unknown;\n}>;\n\nfunction nodeMapper(\n node: TreeListItem,\n {\n posInset,\n setSize,\n }: {\n posInset: number;\n setSize: number;\n },\n): ReactComplexTreeItem {\n return {\n index: node.id,\n children: node.children ? node.children.map((child) => child.id) : [],\n data: {\n label: node.label,\n description: node.description,\n icon: node.icon,\n ...node.data,\n posInset,\n setSize,\n },\n isFolder: !!node.children?.length,\n };\n}\n\nfunction flattenTree(node: TreeListItem, mapper: typeof nodeMapper) {\n const result: Record<TreeItemIndex, ReactComplexTreeItem> = {};\n\n function traverse(\n currentNode: TreeListItem,\n posInset: number,\n setSize: number,\n ) {\n result[currentNode.id] = mapper(currentNode, { posInset, setSize });\n if (currentNode.children) {\n for (let i = 0; i < currentNode.children.length; i++) {\n traverse(currentNode.children[i], i + 1, currentNode.children.length);\n }\n }\n }\n\n traverse(node, 0, 0);\n return result;\n}\n\nexport const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(\n (\n {\n className,\n style,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n items,\n rootId = items.id,\n onPrimaryAction,\n onSecondaryAction,\n primaryActionIcon,\n secondaryActionIcon,\n primaryActionAriaLabel,\n secondaryActionAriaLabel,\n actionsPosition = \"start\",\n onRename,\n onAction,\n renderItemTitle,\n ...rest\n },\n ref,\n ) => {\n const _children = useCollectionChildren<TreeListItem>();\n const containerRef = useRef<HTMLDivElement>(null);\n\n const state = useTreeState({\n ...rest,\n selectionMode: \"multiple\",\n items: items.children,\n children: _children,\n });\n\n const _items = useMemo(() => {\n return Object.fromEntries(\n Object.entries(flattenTree(items, nodeMapper)).map(([key, value]) => [\n key,\n {\n ...value,\n canRename: !!onRename,\n },\n ]),\n );\n }, [items, onRename]);\n\n const treeId = useId();\n const [focusedItem, setFocusedItem] = React.useState<TreeItemIndex>(\n items.children?.[0]?.id ?? \"\",\n );\n\n const currentFocusedItem = useRef<TreeItemIndex | null>(focusedItem);\n\n const viewState = useMemo(() => {\n return {\n [`tree-${treeId}`]: {\n focusedItem,\n expandedItems: [...state.expandedKeys],\n selectedItems: [...state.selectionManager.selectedKeys],\n },\n };\n }, [\n focusedItem,\n state.expandedKeys,\n state.selectionManager.selectedKeys,\n treeId,\n ]);\n\n useEffect(() => {\n if (!containerRef.current) return;\n // There is an issue in react-complex-tree where the focus is not set correctly when the tree is within a shadow DOM.\n // This is a workaround to set the focus manually.\n const rootNode = containerRef.current.getRootNode() as ShadowRoot;\n\n const isWithinShadowDOM = rootNode instanceof ShadowRoot;\n\n const isFocusWithinTree = containerRef.current?.contains(\n rootNode?.activeElement ?? null,\n );\n\n if (isWithinShadowDOM && isFocusWithinTree) {\n rootNode\n ?.querySelector<HTMLElement>(`[data-rct-item-id=\"${focusedItem}\"]`)\n ?.focus();\n }\n }, [focusedItem]);\n\n return (\n <ControlledTreeEnvironment\n items={_items}\n getItemTitle={(item) => item.data?.label}\n viewState={viewState}\n onExpandItem={(item) => {\n state.setExpandedKeys(new Set([...state.expandedKeys, item.index]));\n rest.onExpand?.(item.index.toString());\n }}\n onFocusItem={(item) => {\n setFocusedItem(item.index);\n\n if (item.index) {\n currentFocusedItem.current = item.index;\n }\n }}\n onCollapseItem={(item) => {\n state.setExpandedKeys(\n new Set(\n [...state.expandedKeys].filter((key) => key !== item.index),\n ),\n );\n\n rest.onCollapse?.(item.index.toString());\n }}\n onSelectItems={(items) => {\n state.selectionManager.setSelectedKeys(new Set(items));\n }}\n renderItem={(props) => (\n <TreeLeaf\n {...props}\n onPrimaryAction={onPrimaryAction}\n onSecondaryAction={onSecondaryAction}\n actionsPosition={actionsPosition}\n primaryActionIcon={primaryActionIcon}\n secondaryActionIcon={secondaryActionIcon}\n primaryActionAriaLabel={primaryActionAriaLabel}\n secondaryActionAriaLabel={secondaryActionAriaLabel}\n onRename={onRename}\n onAction={onAction}\n renderItemTitle={renderItemTitle}\n />\n )}\n renderLiveDescriptorContainer={({ children }) => {\n return (\n <Portal>\n <VisuallyHidden>{children}</VisuallyHidden>\n </Portal>\n );\n }}\n renderTreeContainer={({ containerProps, children }) => {\n return (\n <div\n {...mergeProps(containerProps, {\n onBlur: (e: React.FocusEvent<HTMLDivElement>) => {\n // If the focus moves away from the container and the existing focused DOM element is one of the\n // action buttons, then the tabIndex of the `row` element should be set to 0 so that it can be focused\n // again when the user brings focus back to the container.\n if (!e.currentTarget.contains(e.relatedTarget)) {\n e.currentTarget\n .querySelector(\n `[data-key=\"${currentFocusedItem.current}\"]`,\n )\n ?.setAttribute(\"tabindex\", \"0\");\n }\n },\n })}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={{ ...style, ...containerProps.style }}\n className={classNames(className, \"BaselineUI-TreeView\")}\n ref={mergeRefs(\n ref,\n containerProps.ref as React.RefObject<HTMLDivElement>,\n containerRef,\n )}\n role=\"treegrid\"\n >\n {children}\n </div>\n );\n }}\n // @ts-expect-error Types are wrong\n renderItemsContainer={({ children }) => {\n return children;\n }}\n >\n <Tree\n treeId={`tree-${treeId}`}\n rootItem={rootId}\n treeLabel={rest[\"aria-label\"]}\n treeLabelledBy={rest[\"aria-labelledby\"]}\n />\n </ControlledTreeEnvironment>\n );\n },\n);\n\nTreeView.displayName = \"TreeView\";\n",
|
|
73
|
+
"UNSAFE_ListBox": "export { UNSAFE_ListBox } from \"./ListBox\";\nexport type { UNSAFE_ListBoxProps } from \"./ListBox.types\";\nexport { useDragAndDrop } from \"./hooks/useDragAndDrop\";\n",
|
|
74
|
+
"Virtualizer": "import { GridLayout, ListLayout, Size } from \"react-aria-components\";\n\nexport {\n ListLayout,\n GridLayout,\n WaterfallLayout,\n TableLayout,\n Size,\n Virtualizer,\n Autocomplete,\n type VirtualizerProps,\n type AutocompleteProps,\n} from \"react-aria-components\";\n\nexport const VIRTUALIZER_LAYOUT_DEFAULT_OPTIONS = {\n LIST_BOX: {\n layout: ListLayout,\n layoutOptions: {\n rowHeight: 36,\n },\n },\n IMAGE_GALLERY: {\n layout: GridLayout,\n layoutOptions: {\n minItemSize: new Size(114, 167),\n maxItemSize: new Size(114, 167),\n dropIndicatorThickness: 2,\n maxHorizontalSpace: 0,\n minSpace: new Size(4, 16),\n preserveAspectRatio: true,\n },\n },\n};\n"
|
|
75
|
+
}
|