@baseline-ui/mcp 0.46.2 → 0.47.0
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
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
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
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
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",
|
|
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 { ListOptionContext } from \"../UNSAFE_ListBox/ListBox\";\nimport { ListOptionContent } from \"../UNSAFE_ListBox/ListBoxUI\";\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 <ListOptionContent {...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
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
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
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",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
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
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
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",
|
|
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 setSvgCoordinates(undefined);\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
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
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
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",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
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
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
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, {
|
|
37
|
+
"Menu": "import React, { useContext, useEffect } from \"react\";\nimport {\n Menu as AriaMenu,\n MenuTrigger,\n OverlayTriggerStateContext,\n PopoverContext,\n useContextProps,\n} from \"react-aria-components\";\n\nimport { usePortalContainer } from \"../../hooks\";\nimport { ActionButton } from \"../ActionButton\";\nimport { PopoverContent } from \"../Popover/PopoverContent\";\nimport { classNames } from \"../../utils\";\nimport { menuContentCn } from \"./Menu.css\";\nimport { ListOption, ListSection } from \"../UNSAFE_ListBox/ListBoxUI\";\n\nimport type { MenuProps } from \"./Menu.types\";\n\nexport const Menu = React.forwardRef<HTMLDivElement, 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 _portalContainer = usePortalContainer(portalContainer);\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 <MenuTrigger\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n isOpen={isOpen}\n >\n <MyTrigger\n isDisabled={isDisabled}\n renderTrigger={renderTrigger}\n triggerLabel={triggerLabel}\n />\n <MyPopover\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 <AriaMenu\n {...props}\n className={classNames(menuContentCn, contentClassName)}\n ref={ref}\n >\n {props.items.map((item) =>\n \"children\" in item ? (\n <ListSection\n key={item.id}\n section={item}\n componentName=\"Menu\"\n />\n ) : (\n <ListOption key={item.id} item={item} componentName=\"Menu\" />\n ),\n )}\n </AriaMenu>\n </MyPopover>\n </MenuTrigger>\n );\n },\n);\n\nconst MyTrigger = React.forwardRef<\n HTMLButtonElement,\n Pick<MenuProps, \"renderTrigger\" | \"triggerLabel\" | \"isDisabled\">\n>(({ renderTrigger, triggerLabel, isDisabled, ...props }, ref) => {\n const state = useContext(OverlayTriggerStateContext);\n\n return renderTrigger?.({\n buttonProps: {\n ...props,\n isDisabled,\n label:\n typeof triggerLabel === \"function\"\n ? triggerLabel(state?.isOpen || false)\n : triggerLabel,\n isOpen: state?.isOpen || false,\n className: \"BaselineUI-Menu-Trigger\",\n },\n ref: ref as React.RefObject<HTMLButtonElement>,\n });\n});\n\nMyTrigger.displayName = \"MyTrigger\";\n\nconst MyPopover = ({ children, ...props }) => {\n const state = useContext(OverlayTriggerStateContext);\n const [popoverProps] = useContextProps(props, undefined, PopoverContext);\n\n return state?.isOpen ? (\n <PopoverContent {...popoverProps} state={state}>\n {children}\n </PopoverContent>\n ) : null;\n};\n\nMenu.displayName = \"Menu\";\n",
|
|
38
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
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
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",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
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
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
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",
|
|
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 { ActionButton } from \"../ActionButton\";\nimport { Box } from \"../Box\";\nimport { Separator } from \"../Separator\";\nimport { SearchInput } from \"../SearchInput\";\nimport { ListOptionContent } from \"../UNSAFE_ListBox/ListBoxUI\";\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 <ListOptionContent\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
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
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
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",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"Link": "type LinkProps = {\n/** Identifies the element (or elements) that describes the object. */\n \"aria-describedby\"?: string;\n/** Identifies the element (or elements) that provide a detailed, extended description for the object. */\n \"aria-details\"?: string;\n/** Defines a string value that labels the current element. */\n \"aria-label\"?: string;\n/** Identifies the element (or elements) that labels the current element. */\n \"aria-labelledby\"?: string;\n/** Whether the element should receive focus on render. */\n \"autoFocus\"?: boolean;\n/** The children to render. */\n \"children\": ReactNode;\n/** The className applied to the root element of the component. */\n \"className\"?: string;\n/** Represents a data block group. This is similar to `data-block-id` but it\ndoesn't have to be unique just like `class`. This is used to group blocks\ntogether in the DOM and in the block map. It is added as a data attribute\n`data-block-class` to the root element of the block if a DOM node is\nrendered. */\n \"data-block-class\"?: string;\n/** The unique identifier for the block. This is used to identify the block in\nthe DOM and in the block map. It is added as a data attribute\n`data-block-id` to the root element of the block if a DOM node is\nrendered. */\n \"data-block-id\"?: string;\n/** Causes the browser to download the linked URL. A string may be provided to suggest a file name. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download). */\n \"download\"?: string | boolean;\n/** The HTML element used to render the link, e.g. 'a', or 'span'. */\n/** @default a */\n \"elementType\"?: string;\n/** A URL to link to. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#href). */\n \"href\"?: string;\n/** Hints at the human language of the linked URL. See[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#hreflang). */\n \"hrefLang\"?: string;\n/** Whether the link is disabled. */\n \"isDisabled\"?: boolean;\n \"key\"?: Key | null;\n/** Handler that is called when the element loses focus. */\n \"onBlur\"?: ((e: FocusEvent<Element, Element>) => void);\n/** **Not recommended – use `onPress` instead.** `onClick` is an alias for `onPress`\nprovided for compatibility with other libraries. `onPress` provides \nadditional event details for non-mouse interactions. */\n \"onClick\"?: ((e: MouseEvent<FocusableElement, MouseEvent>) => void);\n/** Handler that is called when the element receives focus. */\n \"onFocus\"?: ((e: FocusEvent<Element, Element>) => void);\n/** Handler that is called when the element's focus status changes. */\n \"onFocusChange\"?: ((isFocused: boolean) => void);\n/** Handler that is called when a key is pressed. */\n \"onKeyDown\"?: ((e: KeyboardEvent) => void);\n/** Handler that is called when a key is released. */\n \"onKeyUp\"?: ((e: KeyboardEvent) => void);\n/** Handler that is called when the press is released over the target. */\n \"onPress\"?: ((e: PressEvent) => void);\n/** Handler that is called when the press state changes. */\n \"onPressChange\"?: ((isPressed: boolean) => void);\n/** Handler that is called when a press interaction ends, either\nover the target or when the pointer leaves the target. */\n \"onPressEnd\"?: ((e: PressEvent) => void);\n/** Handler that is called when a press interaction starts. */\n \"onPressStart\"?: ((e: PressEvent) => void);\n/** Handler that is called when a press is released over the target, regardless of\nwhether it started on the target or not. */\n \"onPressUp\"?: ((e: PressEvent) => void);\n/** A space-separated list of URLs to ping when the link is followed. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#ping). */\n \"ping\"?: string;\n/** Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} */\n \"ref\"?: LegacyRef<HTMLElement>;\n/** How much of the referrer to send when following the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#referrerpolicy). */\n \"referrerPolicy\"?: HTMLAttributeReferrerPolicy;\n/** The relationship between the linked resource and the current page. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel). */\n \"rel\"?: string;\n/** Optional ARIA role, useful for specifying a different role for the link. */\n/** @default \"link\" */\n \"role\"?: AriaRole;\n/** Options for the configured client side router. */\n \"routerOptions\"?: undefined;\n/** The size of the link. */\n/** @default md */\n \"size\"?: \"sm\" | \"md\" | \"lg\";\n/** The style applied to the root element of the component. */\n \"style\"?: CSSProperties;\n/** The target window for the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target). */\n \"target\"?: HTMLAttributeAnchorTarget;\n/** The title of the link, for providing additional information. */\n \"title\"?: string;\n/** The type of the link. */\n/** @default body */\n \"type\"?: \"body\" | \"label\";\n/** The link's style variant. */\n/** @default default */\n \"variant\"?: \"default\" | \"inline\";\n}",
|
|
35
35
|
"ListBox": "type ListBoxProps = {\n/** The drag types that the droppable collection accepts. If the collection accepts directories, include `DIRECTORY_DRAG_TYPE` in your array of allowed types. */\n/** @default 'all' */\n \"acceptedDragTypes\"?: \"all\" | (string | symbol)[];\n/** Identifies the element (or elements) that describes the object. */\n \"aria-describedby\"?: string;\n/** Identifies the element (or elements) that provide a detailed, extended description for the object. */\n \"aria-details\"?: string;\n/** Defines a string value that labels the current element. */\n \"aria-label\"?: string;\n/** Identifies the element (or elements) that labels the current element. */\n \"aria-labelledby\"?: string;\n/** Whether to auto focus the listbox or an option. */\n \"autoFocus\"?: boolean | FocusStrategy;\n/** The className applied to the root element of the component. */\n \"className\"?: string;\n/** Represents a data block group. This is similar to `data-block-id` but it\ndoesn't have to be unique just like `class`. This is used to group blocks\ntogether in the DOM and in the block map. It is added as a data attribute\n`data-block-class` to the root element of the block if a DOM node is\nrendered. */\n \"data-block-class\"?: string;\n/** The unique identifier for the block. This is used to identify the block in\nthe DOM and in the block map. It is added as a data attribute\n`data-block-id` to the root element of the block if a DOM node is\nrendered. */\n \"data-block-id\"?: string;\n/** The initial selected keys in the collection (uncontrolled). */\n \"defaultSelectedKeys\"?: Iterable<Key> | \"all\";\n/** The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. */\n \"disabledKeys\"?: Iterable<Key>;\n/** Whether the collection allows empty selection. */\n \"disallowEmptySelection\"?: boolean;\n/** The CSS class name for the drop indicator. */\n \"dropIndicatorClassName\"?: string;\n/** The style of the drop indicator used in a component. */\n \"dropIndicatorStyle\"?: CSSProperties;\n/** Indicates whether reordering is enabled. */\n/** @default false */\n \"enableReorder\"?: boolean;\n/** Whether pressing the escape key should clear selection in the listbox or not.\n\nMost experiences should not modify this option as it eliminates a keyboard user's ability to\neasily clear selection. Only use if the escape key is being handled externally or should not\ntrigger selection clearing contextually. */\n/** @default 'clearSelection' */\n \"escapeKeyBehavior\"?: \"clearSelection\" | \"none\";\n/** A function that returns the items being dragged. */\n \"getItems\"?: ((keys: Set<Key>, items: object[]) => DragItem[]);\n/** The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id). */\n \"id\"?: string;\n/** Whether the drag events should be disabled.\nWhether the drop events should be disabled. */\n \"isDisabled\"?: boolean;\n/** The items to render in the listbox. Items have the following shape:\n\n```ts\nexport type ListOption = {\n id: string;\n label: string;\n description?: string;\n icon?: React.FC<IconProps>;\n};\n\nexport type ListSection = {\n id: string;\n title?: string;\n type: \"section\";\n children: ListOption[];\n};\n\ntype ListItem = ListOption | ListSection;\n``` */\n \"items\"?: ListItem[];\n \"key\"?: Key | null;\n/** The label for the listbox. */\n \"label\"?: ReactNode;\n/** Whether the items are arranged in a stack or grid. */\n/** @default stack */\n \"layout\"?: \"grid\" | \"stack\";\n/** A delegate object that provides layout information for items in the collection.\nBy default this uses the DOM, but this can be overridden to implement things like\nvirtualized scrolling. */\n \"layoutDelegate\"?: LayoutDelegate;\n/** Handle to access the listbox methods. */\n \"listBoxHandle\"?: RefObject<ListHandle>;\n/** Handler that is called when a user performs an action on an item. The exact user event depends on\nthe collection's `selectionBehavior` prop and the interaction modality. */\n \"onAction\"?: ((key: Key) => void);\n/** Handler that is called when the element loses focus. */\n \"onBlur\"?: ((e: FocusEvent<Element, Element>) => void);\n/** Handler that is called when the drag operation is ended, either as a result of a drop or a cancellation. */\n \"onDragEnd\"?: ((e: DraggableCollectionEndEvent) => void);\n/** Handler that is called when the drag is moved. */\n \"onDragMove\"?: ((e: DraggableCollectionMoveEvent) => void);\n/** Handler that is called when a drag operation is started. */\n \"onDragStart\"?: ((e: DraggableCollectionStartEvent) => void);\n/** Handler that is called when a valid drag is dropped on a drop target. When defined, this overrides other\ndrop handlers such as `onInsert`, and `onItemDrop`. */\n \"onDrop\"?: ((e: DroppableCollectionDropEvent) => void);\n/** Handler that is called when the element receives focus. */\n \"onFocus\"?: ((e: FocusEvent<Element, Element>) => void);\n/** Handler that is called when the element's focus status changes. */\n \"onFocusChange\"?: ((isFocused: boolean) => void);\n/** A custom keyboard event handler for drop targets. */\n \"onKeyDown\"?: ((e: KeyboardEvent) => void);\n/** Handler that is called when items are moved within the source collection.\nThis handler allows dropping both on or between items, and items may be\nmoved to a different parent item within a tree. */\n \"onMove\"?: ((e: DroppableCollectionReorderEvent) => void);\n/** Handler that is called when items are reordered within the collection.\nThis handler only allows dropping between items, not on items.\nIt does not allow moving items to a different parent item within a tree. */\n \"onReorder\"?: ((e: DroppableCollectionReorderEvent) => void);\n/** Handler that is called when the selection changes. */\n \"onSelectionChange\"?: ((keys: Selection) => void);\n/** The CSS class name for the option. */\n \"optionClassName\"?: string | ((item: ListOption<Record<string, any>>, state: UIState) => string);\n/** The style of the option. */\n \"optionStyle\"?: CSSProperties | ((item: ListOption<Record<string, any>>, state: UIState) => CSSProperties);\n/** The primary orientation of the items. Usually this is the direction that\nthe collection scrolls. */\n/** @default vertical */\n \"orientation\"?: Orientation;\n/** Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} */\n \"ref\"?: LegacyRef<HTMLUListElement>;\n/** The custom render function for the drag preview. This can be used to render\na custom drag preview when dragging items. */\n \"renderDragPreview\"?: ((items: DragItem[]) => Element);\n/** Renders the drop indicator component. */\n \"renderDropIndicator\"?: ((options: { dropIndicatorProps: HTMLAttributes<HTMLLIElement>; isDropTarget: boolean; isHidden: boolean; orientation: Orientation; }, ref: RefObject<...>) => ReactNode);\n/** The custom render function for the listbox options.\n@param item Node<ListItem>\n@param options [OptionAria]()\n@param ref React.RefObject<HTMLLIElement> */\n \"renderOption\"?: ((item: Node<ListItem>, options: OptionAria & { showSelectedIcon?: boolean; _state: ListState<ListItem>; }, ref: Ref<...>) => ReactNode);\n/** The custom render function for the listbox sections.\n@param section ListSection\n@param ref React.RefObject<HTMLDivElement> */\n \"renderSectionHeader\"?: ((section: Node<ListItem>, ref: Ref<HTMLSpanElement>) => ReactNode);\n/** The CSS class name for the section. */\n \"sectionClassName\"?: string | ((item: ListSection<Record<string, any>>) => string);\n/** The style of the section. */\n \"sectionStyle\"?: CSSProperties | ((item: ListSection<Record<string, any>>) => CSSProperties);\n/** The currently selected keys in the collection (controlled). */\n \"selectedKeys\"?: Iterable<Key> | \"all\";\n/** How multiple selection should behave in the collection. */\n \"selectionBehavior\"?: SelectionBehavior;\n/** The type of selection that is allowed in the collection. */\n \"selectionMode\"?: SelectionMode;\n/** Whether options should be focused when the user hovers over them. */\n \"shouldFocusOnHover\"?: boolean;\n/** Whether focus should wrap around when the end/start is reached. */\n \"shouldFocusWrap\"?: boolean;\n/** Whether selection should occur on press up instead of press down. */\n \"shouldSelectOnPressUp\"?: boolean;\n/** Whether the listbox items should use virtual focus instead of being focused directly. */\n \"shouldUseVirtualFocus\"?: boolean;\n/** Whether to show each section title when provided. */\n/** @default false */\n \"showSectionHeader\"?: boolean;\n/** Whether to show the selected checkmark icon. */\n/** @default true */\n \"showSelectedIcon\"?: boolean;\n/** The style applied to the root element of the component. */\n \"style\"?: CSSProperties;\n/** Wether to add initial padding to section titles if shown. */\n/** @default false */\n \"withSectionHeaderPadding\"?: boolean;\n}",
|
|
36
36
|
"Markdown": "type MarkdownProps = {\n/** The markdown to render. */\n \"children\": string;\n/** The className applied to the root element of the component. */\n \"className\"?: string;\n/** Represents a data block group. This is similar to `data-block-id` but it\ndoesn't have to be unique just like `class`. This is used to group blocks\ntogether in the DOM and in the block map. It is added as a data attribute\n`data-block-class` to the root element of the block if a DOM node is\nrendered. */\n \"data-block-class\"?: string;\n/** The unique identifier for the block. This is used to identify the block in\nthe DOM and in the block map. It is added as a data attribute\n`data-block-id` to the root element of the block if a DOM node is\nrendered. */\n \"data-block-id\"?: string;\n \"key\"?: Key | null;\n/** Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} */\n \"ref\"?: LegacyRef<HTMLDivElement>;\n/** The flag to show the caret at the end */\n \"showCaret\"?: boolean;\n/** The style applied to the root element of the component. */\n \"style\"?: CSSProperties;\n}",
|
|
37
|
-
"Menu": "type MenuProps = {\n/** Identifies the element (or elements) that describes the object. */\n \"aria-describedby\"?: string;\n/** Identifies the element (or elements) that provide a detailed, extended description for the object. */\n \"aria-details\"?: string;\n/** Defines a string value that labels the current element. */\n \"aria-label\"?: string;\n/** Identifies the element (or elements) that labels the current element. */\n \"aria-labelledby\"?: string;\n/** Where the focus should be set. */\n \"autoFocus\"?: boolean | FocusStrategy;\n/** Element that that serves as the positioning boundary. */\n/** @default document.body */\n \"boundaryElement\"?: Element;\n/** The `className` property assigned to the root element of the component. */\n \"className\"?: string;\n/** The `className` property assigned to the content element of the component. */\n \"contentClassName\"?: string;\n/** The additional offset applied along the cross axis between the element and its\nanchor element. */\n/** @default 0 */\n \"crossOffset\"?: number;\n/** Whether the overlay is open by default (uncontrolled). */\n \"defaultOpen\"?: boolean;\n/** The initial selected keys in the collection (uncontrolled). */\n \"defaultSelectedKeys\"?: Iterable<Key> | \"all\";\n/** The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. */\n \"disabledKeys\"?: Iterable<Key>;\n/** Whether the collection allows empty selection. */\n \"disallowEmptySelection\"?: boolean;\n/** Whether pressing the escape key should clear selection in the menu or not.\n\nMost experiences should not modify this option as it eliminates a keyboard user's ability to\neasily clear selection. Only use if the escape key is being handled externally or should not\ntrigger selection clearing contextually. */\n/** @default 'clearSelection' */\n \"escapeKeyBehavior\"?: \"clearSelection\" | \"none\";\n/** The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id). */\n \"id\"?: string;\n/** Whether menu trigger is disabled. */\n \"isDisabled\"?: boolean;\n/** Whether the popover is non-modal, i.e. elements outside the popover may be\ninteracted with by assistive technologies.\n\nMost popovers should not use this option as it may negatively impact the screen\nreader experience. Only use with components such as combobox, which are designed\nto handle this situation carefully. */\n \"isNonModal\"?: boolean;\n/** Whether the overlay is open by default (controlled). */\n \"isOpen\"?: boolean;\n/** The `className` property assigned to the item element of the component. */\n \"itemClassName\"?: string;\n/** A list of items to render in the menu. Items have the following shape:\n\n```ts\nexport type MenuOption = {\n id: string;\n label: string;\n keyboardShortcut?: string;\n icon?: React.FC<IconProps>;\n};\n\nexport type MenuSection = {\n id: string;\n title?: string;\n type: \"section\";\n children: MenuOption[];\n};\n\nexport type MenuItem = MenuOption | MenuSection;\n``` */\n \"items\": MenuItem[];\n \"key\"?: Key | null;\n/** The additional offset applied along the main axis between the element and its\nanchor element. */\n/** @default 0 */\n \"offset\"?: number;\n/** Handler that is called when an item is selected. */\n \"onAction\"?: ((key: Key) => void);\n/** Handler that is called when the menu should close after selecting an item. */\n \"onClose\"?: (() => void);\n/** Handler that is called when the overlay's open state changes. */\n \"onOpenChange\"?: ((isOpen: boolean) => void);\n/** Handler that is called when the selection changes. */\n \"onSelectionChange\"?: ((keys: Selection) => void);\n/** The placement of the element with respect to its anchor element. */\n/** @default bottom start */\n \"placement\"?: Placement;\n/** The container element for the popover. By default, the modal is rendered as\na child of the body element. */\n/** @default document.body */\n \"portalContainer\"?: HTMLElement;\n/** Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} */\n \"ref\"?: LegacyRef<
|
|
37
|
+
"Menu": "type MenuProps = {\n/** Identifies the element (or elements) that describes the object. */\n \"aria-describedby\"?: string;\n/** Identifies the element (or elements) that provide a detailed, extended description for the object. */\n \"aria-details\"?: string;\n/** Defines a string value that labels the current element. */\n \"aria-label\"?: string;\n/** Identifies the element (or elements) that labels the current element. */\n \"aria-labelledby\"?: string;\n/** Where the focus should be set. */\n \"autoFocus\"?: boolean | FocusStrategy;\n/** Element that that serves as the positioning boundary. */\n/** @default document.body */\n \"boundaryElement\"?: Element;\n/** The `className` property assigned to the root element of the component. */\n \"className\"?: string;\n/** The `className` property assigned to the content element of the component. */\n \"contentClassName\"?: string;\n/** The additional offset applied along the cross axis between the element and its\nanchor element. */\n/** @default 0 */\n \"crossOffset\"?: number;\n/** Whether the overlay is open by default (uncontrolled). */\n \"defaultOpen\"?: boolean;\n/** The initial selected keys in the collection (uncontrolled). */\n \"defaultSelectedKeys\"?: Iterable<Key> | \"all\";\n/** The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. */\n \"disabledKeys\"?: Iterable<Key>;\n/** Whether the collection allows empty selection. */\n \"disallowEmptySelection\"?: boolean;\n/** Whether pressing the escape key should clear selection in the menu or not.\n\nMost experiences should not modify this option as it eliminates a keyboard user's ability to\neasily clear selection. Only use if the escape key is being handled externally or should not\ntrigger selection clearing contextually. */\n/** @default 'clearSelection' */\n \"escapeKeyBehavior\"?: \"clearSelection\" | \"none\";\n/** The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id). */\n \"id\"?: string;\n/** Whether menu trigger is disabled. */\n \"isDisabled\"?: boolean;\n/** Whether the popover is non-modal, i.e. elements outside the popover may be\ninteracted with by assistive technologies.\n\nMost popovers should not use this option as it may negatively impact the screen\nreader experience. Only use with components such as combobox, which are designed\nto handle this situation carefully. */\n \"isNonModal\"?: boolean;\n/** Whether the overlay is open by default (controlled). */\n \"isOpen\"?: boolean;\n/** The `className` property assigned to the item element of the component. */\n \"itemClassName\"?: string;\n/** A list of items to render in the menu. Items have the following shape:\n\n```ts\nexport type MenuOption = {\n id: string;\n label: string;\n keyboardShortcut?: string;\n icon?: React.FC<IconProps>;\n};\n\nexport type MenuSection = {\n id: string;\n title?: string;\n type: \"section\";\n children: MenuOption[];\n};\n\nexport type MenuItem = MenuOption | MenuSection;\n``` */\n \"items\": MenuItem[];\n \"key\"?: Key | null;\n/** The additional offset applied along the main axis between the element and its\nanchor element. */\n/** @default 0 */\n \"offset\"?: number;\n/** Handler that is called when an item is selected. */\n \"onAction\"?: ((key: Key) => void);\n/** Handler that is called when the menu should close after selecting an item. */\n \"onClose\"?: (() => void);\n/** Handler that is called when the overlay's open state changes. */\n \"onOpenChange\"?: ((isOpen: boolean) => void);\n/** Handler that is called when the selection changes. */\n \"onSelectionChange\"?: ((keys: Selection) => void);\n/** The placement of the element with respect to its anchor element. */\n/** @default bottom start */\n \"placement\"?: Placement;\n/** The container element for the popover. By default, the modal is rendered as\na child of the body element. */\n/** @default document.body */\n \"portalContainer\"?: HTMLElement;\n/** Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null`\n(or call the ref with `null` if you passed a callback ref).\n@see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} */\n \"ref\"?: LegacyRef<HTMLDivElement>;\n/** A function that renders the trigger element of the component. The default\nimplementation renders an `ActionButton` component.\n\n```tsx\n<Menu renderTrigger={({ buttonProps, ref }) => <ActionButton {...buttonProps} label=\"Label\" ref={ref} />\n``` */\n/** @default Function */\n \"renderTrigger\"?: ((options: { buttonProps: ActionButtonProps & { isOpen: boolean; }; ref: RefObject<HTMLButtonElement>; }) => ReactNode);\n/** The currently selected keys in the collection (controlled). */\n \"selectedKeys\"?: Iterable<Key> | \"all\";\n/** The type of selection that is allowed in the collection. */\n \"selectionMode\"?: SelectionMode;\n/** Whether the element should flip its orientation (e.g. top to bottom or left to right) when\nthere is insufficient room for it to render completely. */\n/** @default true */\n \"shouldFlip\"?: boolean;\n/** Whether keyboard navigation is circular. */\n \"shouldFocusWrap\"?: boolean;\n/** Whether the overlay should update its position automatically. */\n/** @default true */\n \"shouldUpdatePosition\"?: boolean;\n/** How the menu is triggered. */\n/** @default 'press' */\n \"trigger\"?: MenuTriggerType;\n/** The label of the trigger element. This can be a string, a React node, or a\nfunction that accepts a boolean indicating whether the menu is open. */\n \"triggerLabel\"?: ReactNode | ((isOpen: boolean) => ReactNode);\n}",
|
|
38
38
|
"MessageFormat": "type MessageFormatProps = {\n/** The default message to use if the message id is not found. */\n \"defaultMessage\"?: string;\n/** By default `<MessageFormat>` will render the formatted string into a\n`<React.Fragment>`. If you need to customize rendering, you can either wrap\nit with another React element (recommended), specify a different tagName\n(e.g., 'div') */\n \"elementType\"?: ElementType<any, keyof IntrinsicElements>;\n/** The id of the message to format. */\n \"id\": string;\n}",
|
|
39
39
|
"Modal": "type ModalProps = {\n/** The contents of the modal. */\n \"children\": ReactNode;\n/** Whether the overlay is open by default (uncontrolled). */\n \"defaultOpen\"?: boolean;\n/** Whether the overlay is open by default (controlled). */\n \"isOpen\"?: boolean;\n/** Handler that is called when the overlay's open state changes. */\n \"onOpenChange\"?: ((isOpen: boolean) => void);\n}",
|
|
40
40
|
"NumberFormat": "type NumberFormatProps = {\n \"compactDisplay\"?: \"short\" | \"long\";\n \"currency\"?: string;\n \"currencyDisplay\"?: keyof NumberFormatOptionsCurrencyDisplayRegistry;\n \"currencySign\"?: \"standard\" | \"accounting\";\n \"localeMatcher\"?: \"lookup\" | \"best fit\";\n \"maximumFractionDigits\"?: number;\n \"maximumSignificantDigits\"?: number;\n \"minimumFractionDigits\"?: number;\n \"minimumIntegerDigits\"?: number;\n \"minimumSignificantDigits\"?: number;\n \"notation\"?: \"standard\" | \"scientific\" | \"engineering\" | \"compact\";\n/** Overrides default numbering system for the current locale. */\n \"numberingSystem\"?: string;\n \"roundingIncrement\"?: 1 | 2 | 5 | 10 | 20 | 25 | 50 | 100 | 200 | 250 | 500 | 1000 | 2000 | 2500 | 5000;\n \"roundingMode\"?: \"ceil\" | \"floor\" | \"expand\" | \"trunc\" | \"halfCeil\" | \"halfFloor\" | \"halfExpand\" | \"halfTrunc\" | \"halfEven\";\n \"roundingPriority\"?: \"auto\" | \"morePrecision\" | \"lessPrecision\";\n \"signDisplay\"?: keyof NumberFormatOptionsSignDisplayRegistry;\n \"style\"?: keyof NumberFormatOptionsStyleRegistry;\n \"trailingZeroDisplay\"?: \"auto\" | \"stripIfInteger\";\n \"unit\"?: string;\n \"unitDisplay\"?: \"short\" | \"long\" | \"narrow\";\n \"useGrouping\"?: boolean | keyof NumberFormatOptionsUseGroupingRegistry | \"true\" | \"false\";\n/** The number to format. */\n \"value\": number;\n}",
|
package/dist/data/icons.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"12/checkmark-icon",
|
|
7
7
|
"12/drag-indicator-icon",
|
|
8
8
|
"12/drag-indicator-vertical-icon",
|
|
9
|
+
"12/edit-icon",
|
|
9
10
|
"12/ellipse-icon",
|
|
10
11
|
"12/enter-key-icon",
|
|
11
12
|
"12/lock-filled-icon",
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
"16/customize-icon",
|
|
52
53
|
"16/document-edit-icon",
|
|
53
54
|
"16/download-icon",
|
|
55
|
+
"16/duplicate-icon",
|
|
54
56
|
"16/edit-icon",
|
|
55
57
|
"16/elipse-area-icon",
|
|
56
58
|
"16/ellipse-cloudy-icon",
|
|
@@ -82,6 +84,7 @@
|
|
|
82
84
|
"16/horizontal-scroll-icon",
|
|
83
85
|
"16/image-icon",
|
|
84
86
|
"16/info-circle-filled-icon",
|
|
87
|
+
"16/insert-icon",
|
|
85
88
|
"16/italic-icon",
|
|
86
89
|
"16/light-bulb-icon",
|
|
87
90
|
"16/line-icon",
|
|
@@ -142,6 +145,7 @@
|
|
|
142
145
|
"16/settings-icon",
|
|
143
146
|
"16/show-icon",
|
|
144
147
|
"16/slash-commands-icon",
|
|
148
|
+
"16/sound-record-icon",
|
|
145
149
|
"16/stamp-icon",
|
|
146
150
|
"16/star-filled-icon",
|
|
147
151
|
"16/star-icon",
|
|
@@ -173,6 +177,7 @@
|
|
|
173
177
|
"16/warning-filled-icon",
|
|
174
178
|
"16/warning-icon",
|
|
175
179
|
"16/windowed-icon",
|
|
180
|
+
"16/workflow-icon",
|
|
176
181
|
"16/x-circle-filled-icon",
|
|
177
182
|
"16/x-icon",
|
|
178
183
|
"20/add-page-icon",
|
|
@@ -241,6 +246,7 @@
|
|
|
241
246
|
"20/open-icon",
|
|
242
247
|
"20/page-move-left-icon",
|
|
243
248
|
"20/page-move-right-icon",
|
|
249
|
+
"20/pages-insert-icon",
|
|
244
250
|
"20/paste-icon",
|
|
245
251
|
"20/pipette-icon",
|
|
246
252
|
"20/plus-icon",
|
|
@@ -607,6 +613,7 @@
|
|
|
607
613
|
"24/warning-filled-icon",
|
|
608
614
|
"24/warning-icon",
|
|
609
615
|
"24/widget-icon",
|
|
616
|
+
"24/workflow-icon",
|
|
610
617
|
"24/x-circle-filled-icon",
|
|
611
618
|
"24/x-circle-icon",
|
|
612
619
|
"24/x-icon",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"Drawer": "export const DrawerWithActionExample: React.FC<{\n onPress?: () => void;\n}> = ({ onPress }) => {\n return (\n <Drawer\n action={{ icon: EllipseIcon, \"aria-label\": \"Action\", onPress }}\n title=\"Drawer Title\"\n >\n Drawer Content\n </Drawer>\n );\n};",
|
|
15
15
|
"Editor": "export const EditorWithSetCaretButton = ({\n caretIndex,\n onChange,\n}: { caretIndex: number } & EditorProps) => {\n const editorHandleRef = React.useRef<EditorHandle>(null);\n\n return (\n <>\n <button\n onClick={() => editorHandleRef.current?.setCaretPosition(caretIndex)}\n data-testid=\"set-caret\"\n >\n Set caret\n </button>\n <Editor onChange={onChange} editorHandle={editorHandleRef} />\n </>\n );\n};\n\nexport const EditorWithFocusButton = (args: EditorProps) => {\n const editorHandleRef = React.useRef<EditorHandle>(null);\n\n return (\n <>\n <button\n onClick={() => editorHandleRef.current?.focus()}\n data-testid=\"focus\"\n >\n Focus\n </button>\n <Editor editorHandle={editorHandleRef} {...args} />\n </>\n );\n};\n\nexport const EditorAutoFocusOnMount: React.FC<EditorProps> = (args) => {\n const [isMounted, setIsMounted] = React.useState(false);\n\n return (\n <Box style={{ width: 260 }} display=\"flex\" gap=\"2xl\" flexDirection=\"column\">\n <ActionButton\n onPress={() => {\n setIsMounted(!isMounted);\n }}\n label={isMounted ? \"Unmount\" : \"Mount\"}\n />\n\n {isMounted ? <Editor autoFocus={true} {...args} /> : null}\n </Box>\n );\n};",
|
|
16
16
|
"FrameProvider": "export function TestFrameProvider({\n shouldRenderInCustomContainer = true,\n onTopButtonPress,\n onBottomButtonPress,\n isScrollable = false,\n shouldContainOverlays = \"auto\",\n defaultOpen = false,\n portalContainer,\n appendChildren,\n containerStyle,\n containerTestId = \"container\",\n buttonLabel = \"Open Modal\",\n dialogText = \"This is a dialog. \".repeat(100),\n position = \"relative\",\n}: {\n shouldRenderInCustomContainer?: boolean;\n onTopButtonPress?: () => void;\n onBottomButtonPress?: () => void;\n isScrollable?: boolean;\n shouldContainOverlays?: \"always\" | \"auto\" | \"never\";\n defaultOpen?: boolean;\n showOutsideButtons?: boolean;\n portalContainer?: HTMLElement | null;\n appendChildren?: React.ReactNode;\n containerStyle?: React.CSSProperties;\n containerTestId?: string;\n buttonLabel?: string;\n dialogText?: string;\n position?: \"static\" | \"relative\";\n}) {\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n\n return (\n <FrameProvider\n container={\n shouldRenderInCustomContainer && container ? container : undefined\n }\n shouldContainOverlays={shouldContainOverlays}\n portalContainer={portalContainer}\n >\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n gap=\"md\"\n padding=\"2xl\"\n alignItems=\"center\"\n >\n {onTopButtonPress ? (\n <ActionButton label=\"Button At Top\" onPress={onTopButtonPress} />\n ) : null}\n\n <Box\n ref={(el) => {\n setContainer(el);\n }}\n data-testid={containerTestId}\n style={{\n height: isScrollable ? \"100vh\" : \"70vh\",\n width: \"80vw\",\n position,\n ...containerStyle,\n }}\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n flexDirection=\"column\"\n borderWidth={1}\n borderColor=\"border.medium\"\n borderStyle=\"solid\"\n backgroundColor=\"background.positive.medium\"\n >\n <Modal defaultOpen={defaultOpen}>\n <ModalTrigger>\n <ActionButton label={buttonLabel} />\n </ModalTrigger>\n <ModalContent>\n <Dialog>\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n padding=\"xl\"\n >\n <Text>{dialogText}</Text>\n </Box>\n </Dialog>\n </ModalContent>\n </Modal>\n\n {appendChildren}\n </Box>\n\n {onBottomButtonPress ? (\n <ActionButton\n label=\"Button At Bottom\"\n onPress={onBottomButtonPress}\n />\n ) : null}\n </Box>\n </FrameProvider>\n );\n}\n\nexport function TestFrameProviderWithZeroSizedContainer() {\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n\n return (\n <FrameProvider container={container} shouldContainOverlays=\"always\">\n <div\n ref={(el) => {\n setContainer(el);\n }}\n />\n </FrameProvider>\n );\n}\n\nexport function TestFrameProviderWithCustomPortalContainer({\n position = \"relative\",\n}: {\n position?: \"static\" | \"relative\";\n}) {\n const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(\n null,\n );\n\n return (\n <Box padding=\"md\">\n <Box\n ref={setPortalContainer}\n backgroundColor=\"support.warning.subtler\"\n padding=\"md\"\n borderRadius=\"sm\"\n marginX=\"2xl\"\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n >\n <Text>Custom Portal Container</Text>\n </Box>\n <TestFrameProvider\n portalContainer={portalContainer}\n position={position}\n />\n </Box>\n );\n}\n\nexport function NestedTestFrameProvider(\n props: React.ComponentProps<typeof TestFrameProvider>,\n) {\n return (\n <TestFrameProvider\n {...props}\n containerTestId=\"outer-container\"\n buttonLabel=\"Open Outer Modal\"\n dialogText={\"This is the outer dialog. \".repeat(100)}\n appendChildren={\n <Box margin=\"lg\">\n <TestFrameProvider\n {...props}\n containerTestId=\"inner-container\"\n buttonLabel=\"Open Inner Modal\"\n dialogText={\"This is the inner dialog. \".repeat(100)}\n containerStyle={{\n width: `calc(100vw - 400px)`,\n height: \"50vh\",\n backgroundColor: themeVars.color.background.positive.strong,\n }}\n />\n </Box>\n }\n />\n );\n}\n\nexport const TestFrameProviderInsideShadowRoot = ({\n position = \"static\",\n}: {\n position?: \"static\" | \"relative\";\n}) => {\n return (\n <ShadowRoot\n style={{\n backgroundColor: themeVars.color.support.warning.subtler,\n position: \"absolute\",\n inset: 50,\n bottom: 0,\n }}\n >\n <TestFrameProvider position={position} />\n </ShadowRoot>\n );\n};\n\nexport const WithPortalContainerOutsideContainer: React.FC<{\n position?: \"static\" | \"relative\";\n shouldContainOverlays?: \"always\" | \"auto\" | \"never\";\n}> = ({ position = \"relative\", shouldContainOverlays }) => {\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(\n null,\n );\n\n return (\n <FrameProvider\n container={container}\n shouldContainOverlays={shouldContainOverlays}\n portalContainer={portalContainer}\n >\n <Box\n style={{\n width: \"100vw\",\n height: \"100vh\",\n }}\n backgroundColor=\"background.positive.medium\"\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n >\n <Box\n ref={setContainer}\n style={{\n width: \"80vw\",\n height: \"80vh\",\n position,\n }}\n backgroundColor=\"support.warning.subtler\"\n data-testid=\"container\"\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n >\n <Modal>\n <ModalTrigger>\n <ActionButton label=\"Open Modal\" />\n </ModalTrigger>\n <ModalContent>\n <Dialog size=\"content\">\n <Box\n style={{\n width: 2000,\n height: 2000,\n }}\n padding=\"2xl\"\n >\n <Text>\n This is a dialog which is larger than the container.\n </Text>\n </Box>\n </Dialog>\n </ModalContent>\n </Modal>\n </Box>\n <Box ref={setPortalContainer} />\n </Box>\n </FrameProvider>\n );\n};",
|
|
17
|
-
"FreehandCanvas": "export const ControlledFreehandCanvas = () => {\n const [value, setValue] = useState(defaultValue);\n\n return <FreehandCanvas value={value} onChange={setValue} />;\n};\n\nexport const TrackedControlledFreehandCanvas = ({\n onChange,\n onChangeEnd,\n}: {\n onChange?: (value: FreehandCanvasProps[\"value\"]) => void;\n onChangeEnd?: (value: FreehandCanvasProps[\"value\"]) => void;\n}) => {\n const [value, setValue] = useState<FreehandCanvasProps[\"value\"]>([]);\n\n return (\n <FreehandCanvas\n value={value}\n onChange={(value) => {\n setValue(value);\n onChange?.(value);\n }}\n onChangeEnd={(value) => {\n setValue(value);\n onChangeEnd?.(value);\n }}\n />\n );\n};",
|
|
17
|
+
"FreehandCanvas": "export const ControlledFreehandCanvas = ({\n newValue,\n}: {\n newValue?: FreehandCanvasProps[\"value\"];\n}) => {\n const [value, setValue] = useState(defaultValue);\n\n useEffect(() => {\n if (newValue !== undefined) {\n setValue(newValue);\n }\n }, [newValue]);\n\n return <FreehandCanvas value={value} onChange={setValue} />;\n};\n\nexport const TrackedControlledFreehandCanvas = ({\n onChange,\n onChangeEnd,\n}: {\n onChange?: (value: FreehandCanvasProps[\"value\"]) => void;\n onChangeEnd?: (value: FreehandCanvasProps[\"value\"]) => void;\n}) => {\n const [value, setValue] = useState<FreehandCanvasProps[\"value\"]>([]);\n\n return (\n <FreehandCanvas\n value={value}\n onChange={(value) => {\n setValue(value);\n onChange?.(value);\n }}\n onChangeEnd={(value) => {\n setValue(value);\n onChangeEnd?.(value);\n }}\n />\n );\n};",
|
|
18
18
|
"GridList": "export const GridListItemRenderer: GridListProps[\"renderGridItem\"] = (\n { textValue },\n { checkBoxProps, rowProps, isDisabled, gridCellProps, ...rest },\n ref,\n) => {\n return (\n <GridListContainer\n ref={ref}\n rowProps={rowProps}\n gridCellProps={gridCellProps}\n isDisabled={isDisabled}\n {...rest}\n >\n <Box\n display=\"flex\"\n flexDirection=\"row\"\n alignItems=\"center\"\n gap=\"xl\"\n paddingLeft=\"md\"\n >\n {checkBoxProps ? (\n <Checkbox {...checkBoxProps} onChange={console.log} />\n ) : null}\n <Text type=\"label\" size=\"sm\">\n {textValue}\n </Text>\n <ActionIconButton\n icon={EllipseIcon}\n variant=\"secondary\"\n size=\"sm\"\n aria-label=\"Button\"\n onPress={() => {\n alert(`Button for ${textValue} clicked`);\n }}\n isDisabled={checkBoxProps?.isDisabled}\n />\n </Box>\n </GridListContainer>\n );\n};\n\nexport const GridListExample: React.FC<GridListExampleProps> = (props) => {\n return (\n <GridList\n renderGridItem={GridListItemRenderer}\n items={gridItems}\n data-block-id=\"grid-list\"\n {...props}\n />\n );\n};\n\nexport const GridListControlledExample: React.FC<GridListExampleProps> = (\n props,\n) => {\n const handleRef = useRef<ListHandle>(null);\n\n return (\n <Box display=\"flex\" flexDirection=\"column\" gap=\"md\">\n <ActionButton\n label=\"Scroll to bottom\"\n onPress={() => {\n handleRef.current?.scrollIntoView(last(gridItems)?.id ?? \"\", {\n behavior: \"smooth\",\n block: \"end\",\n });\n }}\n />\n\n <ActionButton\n label=\"Focus Last Item\"\n onPress={() => {\n handleRef.current?.setFocusedKey(last(gridItems)?.id ?? \"\");\n }}\n preventFocusOnPress={true}\n />\n <div\n style={{\n maxHeight: 90,\n overflow: \"auto\",\n padding: 10,\n }}\n >\n <GridListExample {...props} gridListHandle={handleRef} />\n </div>\n </Box>\n );\n};\n\nexport const DynamicGridListExample: React.FC<GridListExampleProps> = (\n props,\n) => {\n const list = useListData({\n initialItems: gridItems,\n });\n\n const onReorder = useCallback(\n (e: DroppableCollectionReorderEvent) => {\n if (e.target.dropPosition === \"before\") {\n list.moveBefore(e.target.key, e.keys);\n } else if (e.target.dropPosition === \"after\") {\n list.moveAfter(e.target.key, e.keys);\n }\n },\n [list],\n );\n\n const renderGridItem: GridListProps[\"renderGridItem\"] = useCallback(\n (\n item,\n { dragHandleProps, rowProps, gridCellProps, isDisabled, ...rest },\n ref,\n ) => {\n return (\n <GridListContainer\n ref={ref}\n rowProps={rowProps}\n gridCellProps={gridCellProps}\n isDisabled={isDisabled}\n {...rest}\n >\n <Box\n display=\"flex\"\n flexDirection=\"row\"\n alignItems=\"center\"\n gap=\"xl\"\n paddingLeft=\"md\"\n >\n <GridListDragHandle dragHandleProps={dragHandleProps} />\n <Text type=\"label\" size=\"sm\">\n {item.textValue}\n </Text>\n <ActionIconButton\n icon={XIcon}\n variant=\"secondary\"\n size=\"sm\"\n aria-label=\"Button\"\n onPress={() => {\n // Delete that item from the list\n list.remove(item.key);\n }}\n />\n </Box>\n </GridListContainer>\n );\n },\n [list],\n );\n\n return (\n <GridList\n aria-label=\"Grid List\"\n renderGridItem={renderGridItem}\n items={list.items}\n data-block-id=\"grid-list\"\n enableReorder={true}\n onReorder={onReorder}\n selectionMode=\"multiple\"\n selectionBehavior=\"toggle\"\n {...props}\n />\n );\n};\n\nexport const GridListDragHandle = ({\n dragHandleProps,\n}: GridListDragHandleProps) => {\n invariant(\n dragHandleProps,\n \"GridListDragHandle must be used within a GridListItem with `enableReorder` set to true\",\n );\n\n const { dragProps, dragButtonProps } = dragHandleProps || {};\n\n const dragButtonRef = React.useRef<HTMLDivElement | null>(null);\n\n const { buttonProps } = useButton(\n {\n ...dragButtonProps,\n elementType: \"div\",\n },\n dragButtonRef,\n );\n\n return (\n <div {...mergeProps(dragProps, buttonProps)} ref={dragButtonRef}>\n <DragIndicatorVerticalIcon size={12} className={dragIndicatorCn} />\n </div>\n );\n};\n\nexport const EditableGridListExample: React.FC<GridListExampleProps> = (\n props,\n) => {\n const [isEditing, setIsEditing] = useState(false);\n const [selectedKey, setSelectedKey] = useState(\"1\");\n\n const itemList = useListData({\n initialItems: gridItems,\n });\n\n const onItemUpdate = useCallback(\n (id: string, value: string) => {\n itemList.update(id, { id, label: value });\n },\n [itemList],\n );\n\n const renderGridItem: GridListProps[\"renderGridItem\"] = useCallback(\n ({ textValue, value }, options, ref) => {\n const isSelected = value?.id === selectedKey;\n const isEditMode = isSelected && isEditing;\n\n return (\n <li\n {...options.rowProps}\n ref={ref}\n style={{\n listStyleType: \"none\",\n background: isSelected ? \"#E6F5FF\" : \"transparent\",\n borderRadius: 2,\n }}\n >\n <Box\n display=\"flex\"\n flexDirection=\"row\"\n alignItems=\"center\"\n gap=\"xl\"\n paddingLeft=\"md\"\n {...options.gridCellProps}\n >\n {isEditMode ? (\n <TextInput\n className=\"edit-item-input\"\n autoFocus={true}\n defaultValue={textValue}\n onBlur={(e) => {\n const blurEvent = e;\n onItemUpdate(value?.id, blurEvent.currentTarget.value);\n setIsEditing(false);\n }}\n />\n ) : (\n <Text type=\"label\" size=\"sm\">\n {textValue}\n </Text>\n )}\n </Box>\n </li>\n );\n },\n [selectedKey, isEditing, onItemUpdate],\n );\n\n return (\n <>\n <GridList\n items={itemList.items}\n renderGridItem={renderGridItem}\n data-block-id=\"grid-list\"\n selectionMode=\"single\"\n onSelectionChange={(keys) => {\n setSelectedKey(String([...keys][0]));\n }}\n isEditing={isEditing}\n {...props}\n />\n\n <button\n className=\"edit-selected-item-button\"\n onClick={() => {\n setIsEditing(!isEditing);\n }}\n >\n Edit selected item\n </button>\n </>\n );\n};",
|
|
19
19
|
"I18nProvider": "export const messages = {\n en: { greeting: \"Hello, {name} !\" },\n \"en-Gb\": { greeting: \"Hello, {name} !\" },\n es: { greeting: \"Hola, {name} !\" },\n fi: { greeting: \"Hei, {name} !\" },\n fr: { greeting: \"Bonjour, {name} !\" },\n \"fr-CA\": { greeting: \"Bonjour {name} !\" },\n de: { greeting: \"Hallo, {name} !\" },\n it: { greeting: \"Ciao, {name} !\" },\n cs: { greeting: \"Ahoj, {name} !\" },\n cy: { greeting: \"Helo, {name} !\" },\n da: { greeting: \"Hej, {name} !\" },\n el: { greeting: \"Γεια σου, {name} !\" },\n he: { greeting: \"שלום, {name} !\" },\n hr: { greeting: \"Bok, {name} !\" },\n id: { greeting: \"Halo, {name} !\" },\n ja: { greeting: \"こんにちは、{name} さん!\" },\n ko: { greeting: \"안녕하세요, {name} !\" },\n ms: { greeting: \"Hai, {name} !\" },\n nl: { greeting: \"Hallo, {name} !\" },\n nb: { greeting: \"Hei, {name} !\" },\n pl: { greeting: \"Cześć, {name} !\" },\n pt: { greeting: \"Olá, {name} !\" },\n \"pt-PT\": { greeting: \"Olá, {name} !\" },\n ru: { greeting: \"Привет, {name} !\" },\n sk: { greeting: \"Ahoj, {name} !\" },\n sl: { greeting: \"Zdravo, {name} !\" },\n sv: { greeting: \"Hej, {name} !\" },\n th: { greeting: \"สวัสดี, {name} !\" },\n tr: { greeting: \"Merhaba, {name} !\" },\n uk: { greeting: \"Привіт, {name} !\" },\n \"zn-Hans\": { greeting: \"你好,{name} !\" },\n \"zn-Hant\": { greeting: \"你好,{name} !\" },\n};\n\nexport const LocaleStringExample: React.FC<{\n name: string;\n}> = ({ name }) => {\n const formatter = useI18n(messages);\n\n return (\n <div\n style={{\n color: themeVars.color.text.primary,\n ...themeVars.typography.heading.h3.medium,\n }}\n >\n {formatter.formatMessage(\"greeting\", { name })}\n </div>\n );\n};",
|
|
20
20
|
"ImageGallery": "export function ImageGalleryExample({\n onDelete,\n ...props\n}: Omit<React.ComponentProps<typeof ImageGallery>, \"onDelete\"> & {\n onDelete?: (keys: Key[]) => void;\n}) {\n return (\n <ImageGallery\n {...props}\n onDelete={\n onDelete\n ? (keys) => {\n onDelete?.(keys);\n }\n : undefined\n }\n />\n );\n}\n\nexport function CustomSizeImageGalleryExample({\n ...props\n}: Omit<React.ComponentProps<typeof ImageGallery>, \"onDelete\">) {\n const [imageSize, setImageSize] = useState<number>(ImageSize.sm);\n\n const handleZoomIn = useCallback(() => {\n setImageSize((prevSize) => {\n return prevSize * SCALE_FACTOR;\n });\n }, []);\n\n const handleZoomOut = useCallback(() => {\n setImageSize((prevSize) => {\n const newSize = prevSize / SCALE_FACTOR;\n return Math.max(newSize, ImageSize.sm);\n });\n }, []);\n\n const handleSizeChange = useCallback((value: number) => {\n setImageSize(value);\n }, []);\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n event.preventDefault();\n if (\n (event.key === \"+\" || event.key === \"=\") &&\n (event.metaKey || event.ctrlKey)\n ) {\n handleZoomIn();\n }\n\n if (event.key === \"-\" && (event.metaKey || event.ctrlKey)) {\n handleZoomOut();\n }\n\n if (event.key === \"0\" && (event.metaKey || event.ctrlKey)) {\n setImageSize(ImageSize.sm);\n }\n },\n [handleZoomIn, handleZoomOut],\n );\n\n return (\n <div>\n <div style={buttonContainerStyle}>\n <ActionButton onPress={handleZoomOut} label=\"Zoom Out\"></ActionButton>\n <NumberInput\n value={imageSize}\n label=\"Size\"\n labelPosition=\"start\"\n showStepper={false}\n onChange={handleSizeChange}\n minValue={ImageSize.sm}\n style={{ width: 100 }}\n />\n <ActionButton onPress={handleZoomIn} label=\"Zoom In\"></ActionButton>\n </div>\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n }}\n >\n <Text type=\"helper\">\n To zoom in, press CMD or CTRL + "+".\n </Text>\n <Text type=\"helper\">\n To zoom out, press CMD or CTRL + "-"\n </Text>\n <Text type=\"helper\">To reset, press CMD or CTRL + "0".</Text>\n </div>\n <div style={galleryContainerStyle}>\n <ImageGallery\n {...props}\n items={items}\n imageWidth={imageSize}\n onKeyDown={handleKeyDown}\n />\n </div>\n </div>\n );\n}\n\nexport function DynamicImageGalleryExample({\n defaultItems: _items = [],\n ...props\n}: Omit<React.ComponentProps<typeof ImageGallery>, \"onDelete\">) {\n const [items, setItems] = useState(_items);\n\n const handleAppend = useCallback(() => {\n const newItem = createNewItem(items.length + 1);\n setItems([...items, newItem]);\n }, [items]);\n\n const handlePrepend = useCallback(() => {\n const newItem = createNewItem(items.length + 1);\n setItems([newItem, ...items]);\n }, [items]);\n\n const handleInsert = useCallback(() => {\n const newItem = createNewItem(items.length + 1);\n const newItems = [...items];\n newItems.splice(Math.floor(newItems.length / 2), 0, newItem);\n setItems(newItems);\n }, [items]);\n\n return (\n <Box display=\"flex\" flexDirection=\"column\" alignItems=\"center\" gap=\"xl\">\n <Box display=\"inline-flex\" flexDirection=\"row\" gap=\"lg\">\n <ActionButton onPress={handlePrepend} label=\"Prepend Item\" />\n <ActionButton onPress={handleInsert} label=\"Insert Item\" />\n <ActionButton onPress={handleAppend} label=\"Append Item\" />\n </Box>\n <div>\n <ImageGallery {...props} items={items} />\n </div>\n </Box>\n );\n}\n\nexport function ControlledImageGalleryExample(\n props: Omit<React.ComponentProps<typeof ImageGallery>, \"onDelete\" | \"items\">,\n) {\n const [dynamicItems, setDynamicItems] = React.useState(items.slice(0, 4));\n\n const [selectedIndex, setSelectedIndex] = React.useState(0);\n\n const lastIndexRef = React.useRef(4);\n\n const handleAddItem = () => {\n setDynamicItems((prevItems) => [\n ...prevItems,\n createNewItem(lastIndexRef.current + 1),\n ]);\n\n lastIndexRef.current += 1;\n };\n\n const handleDelete = (keys: Key[]) => {\n setDynamicItems((prevItems) => {\n return prevItems.filter((item) => item.id !== keys[0]);\n });\n };\n\n return (\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n gap=\"xl\"\n alignItems=\"center\"\n style={{\n width: 456,\n }}\n >\n <ActionButton\n onPress={handleAddItem}\n label=\"Add Image\"\n style={{ width: \"100%\", justifyContent: \"center\" }}\n />\n\n <NumberInput\n value={selectedIndex}\n label=\"Selected Index\"\n onChange={setSelectedIndex}\n minValue={0}\n maxValue={dynamicItems.length - 1}\n />\n\n <ImageGallery\n {...props}\n selectedKeys={\n dynamicItems[selectedIndex] ? [dynamicItems[selectedIndex].id] : []\n }\n items={dynamicItems}\n onSelectionChange={(keys) => {\n setSelectedIndex(\n dynamicItems.findIndex((i) => i.id === [...keys][0]),\n );\n }}\n onDelete={handleDelete}\n />\n </Box>\n );\n}\n\nexport function ScrollIntoViewImageGalleryExample() {\n const listBoxHandle = React.useRef<ListHandle>(null);\n const galleryItems = Array.from({ length: 20 }, (_, i) => {\n const index = i + 1;\n return {\n id: `image-${i}`,\n src: resolvePublicUrl(`gallery/${(index % 10) + 1}.jpg`),\n alt: `Image ${index}`,\n label: index.toString(),\n };\n });\n\n return (\n <Box display=\"flex\" flexDirection=\"column\" gap=\"md\" style={{ width: 500 }}>\n <Box display=\"flex\" gap=\"sm\">\n <ActionButton\n label=\"Scroll to Image 0\"\n onPress={() => listBoxHandle.current?.scrollIntoView(\"image-0\")}\n />\n <ActionButton\n label=\"Scroll to Image 15\"\n onPress={() => listBoxHandle.current?.scrollIntoView(\"image-15\")}\n />\n </Box>\n\n <div\n style={{\n maxHeight: 300,\n overflow: \"auto\",\n border: \"1px solid #ccc\",\n }}\n >\n <ImageGallery items={galleryItems} listBoxHandle={listBoxHandle} />\n </div>\n </Box>\n );\n}",
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Baseline UI MCP Server Guidelines
|
|
2
|
+
|
|
3
|
+
This MCP server provides AI assistants with access to Baseline UI's component documentation, icons, and design resources.
|
|
4
|
+
|
|
5
|
+
- Baseline UI is a design system for building accessible and consistent user interfaces.
|
|
6
|
+
- It is built with React and TypeScript and styled with Vanilla Extract.
|
|
7
|
+
- It is also known as BUI.
|
|
8
|
+
- It is a client side design system that depends on global variables like document, window, etc.
|
|
9
|
+
|
|
10
|
+
## Available Tools
|
|
11
|
+
|
|
12
|
+
### Component Documentation
|
|
13
|
+
|
|
14
|
+
**`list_baseline_components`**
|
|
15
|
+
|
|
16
|
+
- Lists all available components in the Baseline UI design system
|
|
17
|
+
- Optional parameter: `includeDescription` (boolean) - includes component descriptions
|
|
18
|
+
- Use this when you need to discover what components are available
|
|
19
|
+
|
|
20
|
+
**`get_baseline_component_info`**
|
|
21
|
+
|
|
22
|
+
- Returns high-level component information: name, description, and available documentation sections
|
|
23
|
+
- Requires: `componentName` (string)
|
|
24
|
+
- Use this before diving into detailed documentation to understand what sections are available
|
|
25
|
+
|
|
26
|
+
**`get_baseline_component_docs`**
|
|
27
|
+
|
|
28
|
+
- Returns complete or section-specific component documentation in markdown format
|
|
29
|
+
- Requires: `componentName` (string)
|
|
30
|
+
- Optional: `sectionName` (string) - get a specific section (e.g., "Usage", "Examples")
|
|
31
|
+
- Use this to read component documentation, examples, and usage patterns
|
|
32
|
+
|
|
33
|
+
**`get_baseline_component_types`**
|
|
34
|
+
|
|
35
|
+
- Returns TypeScript type definitions and prop interfaces for a component
|
|
36
|
+
- Requires: `componentName` (string)
|
|
37
|
+
- Use this when you need to understand component props, their types, and constraints
|
|
38
|
+
|
|
39
|
+
**`get_baseline_component_source`**
|
|
40
|
+
|
|
41
|
+
- Returns the source code implementation of a component
|
|
42
|
+
- Requires: `componentName` (string)
|
|
43
|
+
- Use this to understand how a component is implemented
|
|
44
|
+
|
|
45
|
+
### Icon System
|
|
46
|
+
|
|
47
|
+
**`search_baseline_icons`**
|
|
48
|
+
|
|
49
|
+
- Searches the Baseline UI icon set by name
|
|
50
|
+
- Requires: `terms` (string or array of strings)
|
|
51
|
+
- Returns icon names matching the search terms
|
|
52
|
+
- Import syntax: `import { IconName } from '@baseline-ui/icons/[size]'`
|
|
53
|
+
|
|
54
|
+
### Getting Started
|
|
55
|
+
|
|
56
|
+
**`get_baseline_getting_started`**
|
|
57
|
+
|
|
58
|
+
- Returns complete setup and installation guide for Baseline UI
|
|
59
|
+
- Use this when helping users set up Baseline UI in a new project
|
|
60
|
+
- Always use this for setup-related questions or issues
|
|
61
|
+
|
|
62
|
+
## Recommended Workflow
|
|
63
|
+
|
|
64
|
+
1. **For component discovery**: Use `list_baseline_components` to see available components
|
|
65
|
+
2. **For understanding a component**: Use `get_baseline_component_info` to see available sections
|
|
66
|
+
3. **For detailed documentation**: Use `get_baseline_component_docs` to read full documentation or specific sections
|
|
67
|
+
4. **For implementation details**: Use `get_baseline_component_types` and `get_baseline_component_source`
|
|
68
|
+
5. **For icon selection**: Use `search_baseline_icons` with relevant terms
|
|
69
|
+
6. **For setup**: Use `get_baseline_getting_started` for new projects or setup issues
|
|
70
|
+
|
|
71
|
+
## Styling Guidelines
|
|
72
|
+
|
|
73
|
+
When recommending styling or customization approaches:
|
|
74
|
+
|
|
75
|
+
- **Always prioritize using sprinkles from `@baseline-ui/tokens`** for styling
|
|
76
|
+
- Sprinkles provides a type-safe, zero-runtime CSS utility layer built with Vanilla Extract
|
|
77
|
+
- Use sprinkles for spacing, colors, typography, shadows, and other design tokens
|
|
78
|
+
- Example: `<div className={sprinkles({ padding: 'md', color: 'text.primary' })}>Content</div>`
|
|
79
|
+
- Only use custom CSS when sprinkles doesn't provide the necessary utilities
|
|
80
|
+
- This ensures consistency with the design system and maintains design token governance
|
|
81
|
+
|
|
82
|
+
## Tips for Better Results
|
|
83
|
+
|
|
84
|
+
- When presenting component information to users, format tool results as readable lists or tables, not raw JSON
|
|
85
|
+
- For component documentation, render markdown content with proper formatting and syntax highlighting
|
|
86
|
+
- When suggesting components, mention available sections that users can explore
|
|
87
|
+
- For props and types, highlight required vs optional properties and their constraints
|
|
88
|
+
- When searching icons, try multiple terms if the first search doesn't yield results (e.g., "search", "find", "magnifying")
|
package/dist/index.js
CHANGED
|
@@ -164,6 +164,26 @@ async function startServer() {
|
|
|
164
164
|
name: "baseline-ui-docs-server",
|
|
165
165
|
version: packageJson.version,
|
|
166
166
|
});
|
|
167
|
+
// Register guidelines resource
|
|
168
|
+
server.registerResource("guidelines", "resource://baseline-ui/guidelines.md", {
|
|
169
|
+
description: "Guidelines for using the Baseline UI MCP server",
|
|
170
|
+
mimeType: "text/markdown",
|
|
171
|
+
}, () => {
|
|
172
|
+
const guidelinesPath = path.resolve(__dirname, "guidelines.md");
|
|
173
|
+
if (!fs.existsSync(guidelinesPath)) {
|
|
174
|
+
throw new Error("Guidelines file not found");
|
|
175
|
+
}
|
|
176
|
+
const content = fs.readFileSync(guidelinesPath, "utf8");
|
|
177
|
+
return {
|
|
178
|
+
contents: [
|
|
179
|
+
{
|
|
180
|
+
uri: "resource://baseline-ui/guidelines.md",
|
|
181
|
+
mimeType: "text/markdown",
|
|
182
|
+
text: content,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
});
|
|
167
187
|
// List components tool
|
|
168
188
|
server.registerTool("list_baseline_components", {
|
|
169
189
|
title: "List Baseline UI components",
|