@leafygreen-ui/combobox 0.9.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/ComboboxContext.tsx","../src/ComboboxOption.tsx","../src/Chip.tsx","../src/util.tsx","../src/Combobox.styles.ts","../src/ComboboxGroup.tsx","../src/Combobox.tsx","../src/Combobox.types.ts"],"sourcesContent":["import { createContext } from 'react';\nimport { ComboboxSize, TrunctationLocation } from './Combobox.types';\n\ninterface ComboboxData {\n multiselect: boolean;\n darkMode: boolean;\n size: keyof typeof ComboboxSize;\n withIcons: boolean;\n disabled: boolean;\n chipTruncationLocation?: TrunctationLocation;\n chipCharacterLimit?: number;\n inputValue?: string;\n}\n\nexport const ComboboxContext = createContext<ComboboxData>({\n multiselect: false,\n darkMode: false,\n size: 'default',\n withIcons: false,\n disabled: false,\n});\n","import { css, cx } from '@leafygreen-ui/emotion';\nimport React, { useCallback, useContext, useMemo } from 'react';\nimport { uiColors } from '@leafygreen-ui/palette';\nimport { isComponentType } from '@leafygreen-ui/lib';\nimport { useForwardedRef, useIdAllocator } from '@leafygreen-ui/hooks';\nimport Checkbox from '@leafygreen-ui/checkbox';\nimport Icon, { isComponentGlyph } from '@leafygreen-ui/icon';\nimport {\n ComboboxOptionProps,\n InternalComboboxOptionProps,\n} from './Combobox.types';\nimport { ComboboxContext } from './ComboboxContext';\nimport { wrapJSX } from './util';\n\n/**\n * Styles\n */\n\nconst comboboxOptionStyle = () => css`\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n list-style: none;\n color: inherit;\n cursor: pointer;\n overflow: hidden;\n font-size: var(--lg-combobox-item-font-size);\n line-height: var(--lg-combobox-item-line-height);\n padding: var(--lg-combobox-item-padding-y) var(--lg-combobox-item-padding-x);\n\n &:before {\n content: '';\n position: absolute;\n left: 0;\n width: 3px;\n height: var(--lg-combobox-item-wedge-height);\n background-color: transparent;\n border-radius: 0 2px 2px 0;\n transform: scaleY(0.3);\n transition: 150ms ease-in-out;\n transition-property: transform, background-color;\n }\n\n &:hover {\n outline: none;\n background-color: var(--lg-combobox-item-hover-color);\n }\n\n &[aria-selected='true'] {\n outline: none;\n background-color: var(--lg-combobox-item-active-color);\n\n &:before {\n background-color: var(--lg-combobox-item-wedge-color);\n transform: scaleY(1);\n }\n }\n`;\n\nconst flexSpan = css`\n display: inline-flex;\n gap: 8px;\n justify-content: start;\n align-items: inherit;\n`;\n\nconst displayNameStyle = (isSelected: boolean) => css`\n font-weight: ${isSelected ? 'bold' : 'normal'};\n`;\n/**\n * Component\n */\n\nconst InternalComboboxOption = React.forwardRef<\n HTMLLIElement,\n InternalComboboxOptionProps\n>(\n (\n {\n displayName,\n glyph,\n isSelected,\n isFocused,\n setSelected,\n className,\n }: InternalComboboxOptionProps,\n forwardedRef,\n ) => {\n const { multiselect, darkMode, withIcons, inputValue } = useContext(\n ComboboxContext,\n );\n const optionTextId = useIdAllocator({ prefix: 'combobox-option-text' });\n const optionRef = useForwardedRef(forwardedRef, null);\n\n const handleOptionClick = useCallback(\n (e: React.SyntheticEvent) => {\n e.stopPropagation();\n // If user clicked on the option, or the checkbox\n // Ignore extra click events on the checkbox wrapper\n if (\n e.target === optionRef.current ||\n (e.target as Element).tagName === 'INPUT'\n ) {\n setSelected();\n }\n },\n [optionRef, setSelected],\n );\n\n const renderedIcon = useMemo(() => {\n if (glyph) {\n if (isComponentGlyph(glyph) || isComponentType(glyph, 'Icon')) {\n return glyph;\n }\n console.error(\n '`ComboboxOption` instance did not render icon because it is not a known glyph element.',\n glyph,\n );\n }\n }, [glyph]);\n\n const renderedChildren = useMemo(() => {\n // Multiselect\n if (multiselect) {\n const checkbox = (\n <Checkbox\n // Using empty label due to this bug: https://jira.mongodb.org/browse/PD-1681\n label=\"\"\n aria-labelledby={optionTextId}\n checked={isSelected}\n tabIndex={-1}\n animate={false}\n />\n );\n\n return (\n <>\n <span className={flexSpan}>\n {withIcons ? renderedIcon : checkbox}\n <span id={optionTextId} className={displayNameStyle(isSelected)}>\n {wrapJSX(displayName, inputValue, 'strong')}\n </span>\n </span>\n {withIcons && checkbox}\n </>\n );\n }\n\n // Single select\n return (\n <>\n <span className={flexSpan}>\n {renderedIcon}\n <span className={displayNameStyle(isSelected)}>\n {wrapJSX(displayName, inputValue, 'strong')}\n </span>\n </span>\n {isSelected && (\n <Icon\n glyph=\"Checkmark\"\n color={darkMode ? uiColors.blue.light1 : uiColors.blue.base}\n />\n )}\n </>\n );\n }, [\n multiselect,\n renderedIcon,\n isSelected,\n displayName,\n inputValue,\n darkMode,\n optionTextId,\n withIcons,\n ]);\n\n return (\n <li\n ref={optionRef}\n role=\"option\"\n aria-selected={isFocused}\n aria-label={displayName}\n tabIndex={-1}\n className={cx(comboboxOptionStyle(), className)}\n onClick={handleOptionClick}\n onKeyPress={handleOptionClick}\n >\n {renderedChildren}\n </li>\n );\n },\n);\nInternalComboboxOption.displayName = 'ComboboxOption';\n\nexport { InternalComboboxOption };\nexport default function ComboboxOption(_: ComboboxOptionProps): JSX.Element {\n throw Error('`ComboboxOption` must be a child of a `Combobox` instance');\n}\nComboboxOption.displayName = 'ComboboxOption';\n","import React, { useContext, useEffect, useMemo, useRef } from 'react';\nimport { ChipProps, ComboboxSize } from './Combobox.types';\nimport Icon from '@leafygreen-ui/icon';\nimport { ComboboxContext } from './ComboboxContext';\nimport { css, cx } from '@leafygreen-ui/emotion';\nimport { uiColors } from '@leafygreen-ui/palette';\nimport InlineDefinition from '@leafygreen-ui/inline-definition';\nimport { createDataProp } from '@leafygreen-ui/lib';\nimport { keyMap } from './util';\n\nconst chipWrapperStyle = ({\n darkMode,\n size,\n}: {\n darkMode: boolean;\n size: ComboboxSize;\n}) => {\n let chipModeStyle, chipSizeStyle;\n\n if (darkMode) {\n chipModeStyle = css``;\n } else {\n chipModeStyle = css`\n --lg-combobox-chip-text-color: ${uiColors.gray.dark3};\n --lg-combobox-chip-icon-color: ${uiColors.gray.dark2};\n --lg-combobox-chip-background-color: ${uiColors.gray.light2};\n --lg-combobox-chip-hover-color: ${uiColors.gray.light1};\n --lg-combobox-chip-focus-color: ${uiColors.blue.light2};\n `;\n }\n\n switch (size) {\n case 'default':\n chipSizeStyle = css`\n --lg-combobox-chip-height: 24px;\n --lg-combobox-chip-border-radius: 4px;\n --lg-combobox-chip-font-size: 14px;\n --lg-combobox-chip-line-height: 20px;\n --lg-combobox-chip-padding-x: 6px;\n `;\n break;\n }\n\n return cx(\n chipModeStyle,\n chipSizeStyle,\n css`\n display: inline-flex;\n align-items: center;\n overflow: hidden;\n white-space: nowrap;\n height: var(--lg-combobox-chip-height);\n font-size: var(--lg-combobox-chip-font-size);\n line-height: var(--lg-combobox-chip-line-height);\n border-radius: var(--lg-combobox-chip-border-radius);\n color: var(--lg-combobox-chip-text-color);\n background-color: var(--lg-combobox-chip-background-color);\n\n // TODO - refine these styles\n /* &:focus, */\n &:focus-within {\n background-color: var(--lg-combobox-chip-focus-color);\n }\n `,\n );\n};\n\nconst chipText = css`\n padding-inline: var(--lg-combobox-chip-padding-x);\n`;\n\nconst chipButton = css`\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n width: 100%;\n outline: none;\n border: none;\n background-color: transparent;\n color: var(--lg-combobox-chip-icon-color);\n cursor: pointer;\n transition: background-color 100ms ease-in-out;\n\n &:before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 1px;\n background-color: var(--lg-combobox-chip-hover-color);\n }\n\n &:hover {\n background-color: var(--lg-combobox-chip-hover-color);\n }\n`;\n\nexport const Chip = React.forwardRef<HTMLSpanElement, ChipProps>(\n ({ displayName, isFocused, onRemove, onFocus }: ChipProps, forwardedRef) => {\n const {\n darkMode,\n size,\n disabled,\n chipTruncationLocation,\n chipCharacterLimit = 12,\n } = useContext(ComboboxContext);\n\n const isTruncated =\n !!chipCharacterLimit &&\n !!chipTruncationLocation &&\n chipTruncationLocation !== 'none' &&\n displayName.length > chipCharacterLimit;\n\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const truncatedName = useMemo(() => {\n if (isTruncated) {\n const ellipsis = '…';\n const chars = chipCharacterLimit - 3; // ellipsis dots included in the char limit\n\n switch (chipTruncationLocation) {\n case 'start': {\n const end = displayName\n .substring(displayName.length - chars)\n .trim();\n return ellipsis + end;\n }\n\n case 'middle': {\n const start = displayName.substring(0, chars / 2).trim();\n const end = displayName\n .substring(displayName.length - chars / 2)\n .trim();\n return start + ellipsis + end;\n }\n\n case 'end': {\n const start = displayName.substring(0, chars).trim();\n return start + ellipsis;\n }\n\n default: {\n return displayName;\n }\n }\n }\n\n return false;\n }, [chipCharacterLimit, chipTruncationLocation, displayName, isTruncated]);\n\n useEffect(() => {\n if (isFocused && !disabled) {\n buttonRef?.current?.focus();\n }\n }, [disabled, forwardedRef, isFocused]);\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (\n !disabled &&\n (e.keyCode === keyMap.Delete ||\n e.keyCode === keyMap.Backspace ||\n e.keyCode === keyMap.Enter ||\n e.keyCode === keyMap.Space)\n ) {\n onRemove();\n }\n };\n\n const handleChipClick = (e: React.MouseEvent) => {\n // Did not click button\n if (!buttonRef.current?.contains(e.target as Node)) {\n onFocus();\n }\n };\n\n const handleButtonClick = () => {\n if (!disabled) {\n onRemove();\n }\n };\n\n const dataProp = createDataProp('combobox-chip');\n\n return (\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events\n <span\n role=\"option\"\n aria-selected={isFocused}\n data-test-id=\"lg-combobox-chip\"\n {...dataProp.prop}\n ref={forwardedRef}\n className={chipWrapperStyle({ darkMode, size })}\n onClick={handleChipClick}\n tabIndex={-1}\n >\n <span className={chipText}>\n {truncatedName ? (\n <InlineDefinition definition={displayName} align=\"bottom\">\n {truncatedName}\n </InlineDefinition>\n ) : (\n displayName\n )}\n </span>\n <button\n aria-label={`Deselect ${displayName}`}\n aria-disabled={disabled}\n disabled={disabled}\n ref={buttonRef}\n className={chipButton}\n onClick={handleButtonClick}\n onKeyDown={handleKeyDown}\n >\n <Icon glyph=\"X\" />\n </button>\n </span>\n );\n },\n);\nChip.displayName = 'Chip';\n","import { isComponentType, keyMap as _keyMap } from '@leafygreen-ui/lib';\nimport { kebabCase } from 'lodash';\nimport React from 'react';\nimport { ComboboxOptionProps } from './Combobox.types';\n\n// TODO - remove this when lib/keyMap supports Backspace & Delete\nexport const keyMap = {\n ..._keyMap,\n Backspace: 8,\n Delete: 46,\n} as const;\n\n/**\n *\n * Wraps the first instance of `wrap` found in `str` within the provided `element`.\n *\n * E.g. `wrapJSX('Apple', 'ap', 'em') => <><em>Ap</em>ple</>`\n *\n * @param str\n * @param wrap\n * @param element\n * @returns `JSX.Element`\n */\nexport const wrapJSX = (\n str: string,\n wrap?: string,\n element?: string,\n): JSX.Element => {\n if (wrap && element) {\n const regex = new RegExp(wrap, 'gi');\n const startIndex = str.search(regex);\n const endIndex = startIndex + wrap.length;\n const nameArr = str.split('');\n const start = nameArr.slice(0, startIndex).join('');\n const end = nameArr.slice(endIndex).join('');\n const match = nameArr.slice(startIndex, endIndex).join('');\n const matchEl = React.createElement(element, null, match);\n return (\n <>\n {start}\n {matchEl}\n {end}\n </>\n );\n }\n\n return <>{str}</>;\n};\n\n/**\n *\n * Returns an object with properties `value` & `displayName`\n * based on the props provided\n *\n * @property value: string\n * @property displayName: string\n */\nexport const getNameAndValue = ({\n value: valProp,\n displayName: nameProp,\n}: ComboboxOptionProps): {\n value: string;\n displayName: string;\n} => {\n return {\n value: valProp ?? kebabCase(nameProp),\n displayName: nameProp ?? valProp ?? '', // TODO consider adding a prop to customize displayName => startCase(valProp),\n };\n};\n\nexport interface OptionObject {\n value: string;\n displayName: string;\n hasGlyph?: boolean;\n}\n\n/**\n *\n * Flattens multiple nested ComboboxOptions into a 1D array\n *\n * @param _children\n * @returns `Array<OptionObject>`\n */\nexport const flattenChildren = (\n _children: React.ReactNode,\n): Array<OptionObject> => {\n // TS doesn't like .reduce\n // @ts-expect-error\n return React.Children.toArray(_children).reduce(\n // @ts-expect-error\n (\n acc: Array<OptionObject>,\n child: React.ReactNode,\n ): Array<OptionObject> | undefined => {\n if (isComponentType(child, 'ComboboxOption')) {\n const { value, displayName } = getNameAndValue(child.props);\n const { glyph } = child.props;\n\n return [\n ...acc,\n {\n value,\n displayName,\n hasGlyph: !!glyph,\n },\n ];\n } else if (isComponentType(child, 'ComboboxGroup')) {\n const { children } = child.props;\n\n if (children) {\n return [...acc, ...flattenChildren(children)];\n }\n }\n },\n [] as Array<OptionObject>,\n );\n};\n","/**\n * Styles\n */\n\nimport { css, cx, keyframes } from '@leafygreen-ui/emotion';\nimport { uiColors } from '@leafygreen-ui/palette';\nimport { fontFamilies } from '@leafygreen-ui/tokens';\nimport { isArray } from 'lodash';\nimport { ComboboxSize, Overflow, State } from './Combobox.types';\n\nexport const comboboxParentStyle = ({\n darkMode,\n size,\n overflow,\n}: {\n darkMode: boolean;\n size: ComboboxSize;\n overflow: Overflow;\n}) => {\n const modeStyle = (darkMode: boolean) => {\n if (darkMode) {\n return css``;\n } else {\n return css`\n --lg-combobox-color-error: ${uiColors.red.base};\n --lg-combobox-text-color: ${uiColors.gray.dark3};\n --lg-combobox-text-color-disabled: ${uiColors.gray.dark1};\n\n --lg-combobox-background-color: ${uiColors.gray.light3};\n --lg-combobox-background-color-focus: ${uiColors.white};\n --lg-combobox-background-color-disabled: ${uiColors.gray.light2};\n\n --lg-combobox-border-color: ${uiColors.gray.base};\n --lg-combobox-border-color-disabled: ${uiColors.gray.light1};\n --lg-combobox-border-color-error: ${uiColors.red.base};\n\n --lg-combobox-shadow: 0px 1px 2px rgba(6, 22, 33, 0.3);\n --lg-combobox-shadow-focus: 0px 4px 4px rgba(6, 22, 33, 0.3);\n `;\n }\n };\n\n const sizeStyle = (size: ComboboxSize) => {\n switch (size) {\n case 'default':\n return css`\n --lg-combobox-padding-y: 5px;\n --lg-combobox-padding-x: 7px;\n --lg-combobox-height: 24px;\n --lg-combobox-font-size: 14px;\n --lg-combobox-line-height: 20px;\n --lg-combobox-border-radius: 3px;\n --lg-combobox-input-default-width: 12ch;\n --lg-combobox-icon-height: 16px;\n `;\n }\n };\n\n return cx(\n modeStyle(darkMode),\n sizeStyle(size),\n css`\n --lg-combobox-width: ${overflow === 'expand-x' ? 'unset' : '100%'};\n --lg-combobox-padding: var(--lg-combobox-padding-y)\n var(--lg-combobox-padding-x) var(--lg-combobox-padding-y)\n ${overflow === 'scroll-x' ? '0' : 'var(--lg-combobox-padding-x)'};\n width: var(--lg-combobox-width);\n `,\n );\n};\n\nexport const comboboxStyle = css`\n display: flex;\n flex-wrap: nowrap;\n align-items: center;\n padding: var(--lg-combobox-padding);\n color: var(--lg-combobox-text-color);\n background-color: var(--lg-combobox-background-color);\n box-shadow: var(--lg-combobox-shadow);\n border: 1px solid var(--lg-combobox-border-color);\n border-radius: var(--lg-combobox-border-radius);\n width: var(--lg-combobox-width);\n cursor: text;\n transition: 150ms ease-in-out;\n transition-property: background-color, box-shadow;\n min-width: 256px;\n\n &:focus-within {\n background-color: var(--lg-combobox-background-color-focus);\n box-shadow: var(--lg-combobox-shadow-focus);\n }\n\n &[data-disabled='true'] {\n color: var(--lg-combobox-text-color-disabled);\n background-color: var(--lg-combobox-background-color-disabled);\n border-color: var(--lg-combobox-border-color-disabled);\n box-shadow: unset;\n cursor: not-allowed;\n }\n\n &[data-state='error'] {\n border-color: var(--lg-combobox-border-color-error);\n }\n`;\n\nexport const interactionRingStyle = css`\n width: var(--lg-combobox-width);\n`;\n\nexport const interactionRingColor = ({\n state,\n darkMode,\n}: {\n state: State;\n darkMode: boolean;\n}) => {\n if (darkMode) {\n return {\n hovered: state === 'error' ? uiColors.red.dark2 : undefined,\n };\n } else {\n return {\n hovered: state === 'error' ? uiColors.red.light3 : undefined,\n };\n }\n};\n\nexport const inputWrapperStyle = ({\n overflow,\n isOpen,\n selection,\n value,\n}: {\n overflow: Overflow;\n isOpen: boolean;\n selection: string | Array<string> | null;\n value?: string;\n}) => {\n const isMultiselect = isArray(selection) && selection.length > 0;\n const inputLength = value?.length ?? 0;\n\n // The input should be hidden when there are elements selected in a multiselect\n // We don't set \\`display: none\\` since we need to be able to set .focus() on the element\n const inputWidth = isMultiselect\n ? isOpen || inputLength > 0\n ? `${inputLength + 1}ch`\n : '0'\n : 'var(--lg-combobox-input-default-width)';\n\n const baseWrapperStyle = css`\n flex-grow: 1;\n width: var(--lg-combobox-width);\n\n --lg-combobox-input-width: ${inputWidth};\n `;\n\n switch (overflow) {\n case 'scroll-x': {\n return css`\n ${baseWrapperStyle}\n display: block;\n height: var(--lg-combobox-height);\n white-space: nowrap;\n overflow-x: scroll;\n scroll-behavior: smooth;\n scrollbar-width: none;\n /*\n * Immediate transition in, slow transition out. \n * '-in' transition is handled by \\`scroll-behavior\\` \n */\n --lg-combobox-input-transition: width ease-in-out\n ${isOpen ? '0' : '100ms'};\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n & > * {\n margin-inline: 2px;\n\n &:first-child {\n margin-inline-start: var(--lg-combobox-padding-x);\n }\n\n &:last-child {\n margin-inline-end: var(--lg-combobox-padding-x);\n }\n }\n `;\n }\n\n case 'expand-x': {\n return css`\n ${baseWrapperStyle}\n display: flex;\n gap: 4px;\n flex-wrap: nowrap;\n white-space: nowrap;\n --lg-combobox-input-transition: width 150ms ease-in-out;\n `;\n }\n\n // TODO - look into animating input element height on wrap\n case 'expand-y': {\n return css`\n ${baseWrapperStyle}\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n overflow-x: visible;\n `;\n }\n }\n};\n\nexport const inputElementStyle = css`\n border: none;\n cursor: inherit;\n background-color: inherit;\n box-sizing: content-box;\n padding-block: calc(\n (var(--lg-combobox-height) - var(--lg-combobox-line-height)) / 2\n );\n padding-inline: 0;\n height: var(--lg-combobox-line-height);\n width: var(\n --lg-combobox-input-width,\n var(--lg-combobox-input-default-width, auto)\n );\n transition: var(--lg-combobox-input-transition);\n\n &:focus {\n outline: none;\n }\n`;\n\nexport const clearButton = css`\n // Add a negative margin so the button takes up the same space as the regular icons\n margin-block: calc(var(--lg-combobox-icon-height) / 2 - 100%);\n`;\n\nexport const errorMessageStyle = css`\n font-size: var(--lg-combobox-font-size);\n line-height: var(--lg-combobox-line-height);\n color: var(--lg-combobox-color-error);\n padding-top: var(--lg-combobox-padding-y);\n`;\n\nexport const endIcon = css`\n margin-inline-end: calc(var(--lg-combobox-padding-x) / 2);\n`;\n\nconst loadingIconAnimation = keyframes`\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n`;\nexport const loadingIconStyle = css`\n animation: ${loadingIconAnimation} 1.5s linear infinite;\n`;\n\n/**\n * Menu styles\n */\nexport const menuWrapperStyle = ({\n darkMode,\n size,\n width = 384,\n}: {\n darkMode: boolean;\n size: ComboboxSize;\n width?: number;\n}) => {\n let menuModeStyle, menuSizeStyle;\n\n if (darkMode) {\n menuModeStyle = css``;\n } else {\n menuModeStyle = css`\n --lg-combobox-menu-color: ${uiColors.gray.dark3};\n --lg-combobox-menu-message-color: ${uiColors.gray.dark1};\n --lg-combobox-menu-background-color: ${uiColors.white};\n --lg-combobox-menu-shadow: 0px 3px 7px rgba(0, 0, 0, 0.25);\n --lg-combobox-item-hover-color: ${uiColors.gray.light2};\n --lg-combobox-item-active-color: ${uiColors.blue.light3};\n --lg-combobox-item-wedge-color: ${uiColors.blue.base};\n `;\n }\n\n switch (size) {\n case 'default':\n menuSizeStyle = css`\n --lg-combobox-menu-border-radius: 4px;\n --lg-combobox-item-height: 36px;\n --lg-combobox-item-padding-y: 8px;\n --lg-combobox-item-padding-x: 12px;\n --lg-combobox-item-font-size: 14px;\n --lg-combobox-item-line-height: 21px;\n --lg-combobox-item-wedge-height: 22px;\n `;\n }\n\n return cx(\n menuModeStyle,\n menuSizeStyle,\n css`\n width: ${width}px;\n border-radius: var(--lg-combobox-menu-border-radius);\n\n & > * {\n border-radius: inherit;\n }\n `,\n );\n};\n\nexport const menuStyle = ({ maxHeight }: { maxHeight: number }) => css`\n position: relative;\n width: 100%;\n margin: 0;\n padding: 0;\n font-family: ${fontFamilies.default};\n color: var(--lg-combobox-menu-color);\n background-color: var(--lg-combobox-menu-background-color);\n box-shadow: var(--lg-combobox-menu-shadow);\n border-radius: inherit;\n overflow: auto;\n min-height: var(--lg-combobox-item-height);\n max-height: ${maxHeight}px;\n scroll-behavior: smooth;\n`;\n\nexport const menuList = css`\n position: relative;\n margin: 0;\n padding: 0;\n`;\n\nexport const menuMessage = css`\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: var(--lg-combobox-item-font-size);\n color: var(--lg-combobox-menu-message-color);\n padding: var(--lg-combobox-item-padding-y) var(--lg-combobox-item-padding-x);\n\n & > svg {\n width: 1em;\n height: 1em;\n }\n`;\n","import { css, cx } from '@leafygreen-ui/emotion';\nimport { useIdAllocator } from '@leafygreen-ui/hooks';\nimport { uiColors } from '@leafygreen-ui/palette';\nimport React, { useContext } from 'react';\nimport { ComboboxGroupProps } from './Combobox.types';\nimport { ComboboxContext } from './ComboboxContext';\n\nconst comboboxGroupStyle = (darkMode: boolean) => css`\n --lg-combobox-group-label-color: ${darkMode\n ? uiColors.gray.light1\n : uiColors.gray.dark1};\n --lg-combobox-group-border-color: ${darkMode\n ? uiColors.gray.dark1\n : uiColors.gray.light1};\n padding-top: 8px;\n border-bottom: 1px solid var(--lg-combobox-group-border-color);\n`;\n\nconst comboboxGroupLabel = css`\n cursor: default;\n width: 100%;\n padding: 0 12px 2px;\n outline: none;\n overflow-wrap: anywhere;\n font-size: 12px;\n line-height: 16px;\n font-weight: bold;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n color: var(--lg-combobox-group-label-color);\n`;\n\nexport function InternalComboboxGroup({\n label,\n className,\n children,\n}: ComboboxGroupProps): JSX.Element {\n const { darkMode } = useContext(ComboboxContext);\n\n const groupId = useIdAllocator({ prefix: 'combobox-group' });\n const childCount = React.Children.count(children);\n\n return childCount > 0 ? (\n <div className={cx(comboboxGroupStyle(darkMode), className)}>\n <div className={comboboxGroupLabel} id={groupId}>\n {label}\n </div>\n <div role=\"group\" aria-labelledby={groupId}>\n {children}\n </div>\n </div>\n ) : (\n <></>\n );\n}\n\nComboboxGroup.displayName = 'ComboboxGroup';\n\nexport default function ComboboxGroup(_: ComboboxGroupProps): JSX.Element {\n throw Error('`ComboboxGroup` must be a child of a `Combobox` instance');\n}\n","import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { clone, isArray, isNull, isString, isUndefined } from 'lodash';\nimport { Description, Label } from '@leafygreen-ui/typography';\nimport Popover from '@leafygreen-ui/popover';\nimport {\n useDynamicRefs,\n useEventListener,\n useIdAllocator,\n usePrevious,\n useViewportSize,\n} from '@leafygreen-ui/hooks';\nimport InteractionRing from '@leafygreen-ui/interaction-ring';\nimport Icon from '@leafygreen-ui/icon';\nimport IconButton from '@leafygreen-ui/icon-button';\nimport { cx } from '@leafygreen-ui/emotion';\nimport { uiColors } from '@leafygreen-ui/palette';\nimport { isComponentType } from '@leafygreen-ui/lib';\nimport {\n ComboboxProps,\n getNullSelection,\n onChangeType,\n SelectValueType,\n} from './Combobox.types';\nimport { ComboboxContext } from './ComboboxContext';\nimport { InternalComboboxOption } from './ComboboxOption';\nimport { Chip } from './Chip';\nimport {\n clearButton,\n comboboxParentStyle,\n comboboxStyle,\n endIcon,\n errorMessageStyle,\n inputElementStyle,\n inputWrapperStyle,\n interactionRingColor,\n interactionRingStyle,\n loadingIconStyle,\n menuList,\n menuMessage,\n menuStyle,\n menuWrapperStyle,\n} from './Combobox.styles';\nimport { InternalComboboxGroup } from './ComboboxGroup';\nimport { flattenChildren, getNameAndValue, OptionObject, keyMap } from './util';\n\n/**\n * Component\n */\nexport default function Combobox<M extends boolean>({\n children,\n label,\n description,\n placeholder = 'Select',\n 'aria-label': ariaLabel,\n disabled = false,\n size = 'default',\n darkMode = false,\n state = 'none',\n errorMessage,\n searchState = 'unset',\n searchEmptyMessage = 'No results found',\n searchErrorMessage = 'Could not get results!',\n searchLoadingMessage = 'Loading results...',\n filteredOptions,\n onFilter,\n clearable = true,\n onClear,\n overflow = 'expand-y',\n multiselect = false as M,\n initialValue,\n onChange,\n value,\n chipTruncationLocation,\n chipCharacterLimit = 12,\n className,\n ...rest\n}: ComboboxProps<M>) {\n const getOptionRef = useDynamicRefs<HTMLLIElement>({ prefix: 'option' });\n const getChipRef = useDynamicRefs<HTMLSpanElement>({ prefix: 'chip' });\n\n const inputId = useIdAllocator({ prefix: 'combobox-input' });\n const labelId = useIdAllocator({ prefix: 'combobox-label' });\n const menuId = useIdAllocator({ prefix: 'combobox-menu' });\n\n const comboboxRef = useRef<HTMLDivElement>(null);\n const clearButtonRef = useRef<HTMLButtonElement>(null);\n const inputWrapperRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n const [isOpen, setOpen] = useState(false);\n const prevOpenState = usePrevious(isOpen);\n const [focusedOption, setFocusedOption] = useState<string | null>(null);\n const [selection, setSelection] = useState<SelectValueType<M> | null>(null);\n const prevSelection = usePrevious(selection);\n const [inputValue, setInputValue] = useState<string>('');\n const prevValue = usePrevious(inputValue);\n const [focusedChip, setFocusedChip] = useState<string | null>(null);\n\n const doesSelectionExist =\n !isNull(selection) &&\n ((isArray(selection) && selection.length > 0) || isString(selection));\n\n // Tells typescript that selection is multiselect\n const isMultiselect = useCallback(\n <T extends number | string>(test: Array<T> | T | null): test is Array<T> =>\n multiselect && isArray(test),\n [multiselect],\n );\n\n // Force focus of input box\n const setInputFocus = useCallback(\n (cursorPos?: number) => {\n if (!disabled && inputRef && inputRef.current) {\n inputRef.current.focus();\n if (!isUndefined(cursorPos)) {\n inputRef.current.setSelectionRange(cursorPos, cursorPos);\n }\n }\n },\n [disabled],\n );\n\n // Update selection differently in mulit & single select\n const updateSelection = useCallback(\n (value: string | null) => {\n if (isMultiselect(selection)) {\n const newSelection: SelectValueType<M> = clone(selection);\n\n if (isNull(value)) {\n newSelection.length = 0;\n } else {\n if (selection.includes(value)) {\n // remove from array\n newSelection.splice(newSelection.indexOf(value), 1);\n } else {\n // add to array\n newSelection.push(value);\n // clear text\n setInputValue('');\n }\n }\n setSelection(newSelection);\n (onChange as onChangeType<true>)?.(\n newSelection as SelectValueType<true>,\n );\n } else {\n const newSelection: SelectValueType<M> = value as SelectValueType<M>;\n setSelection(newSelection);\n (onChange as onChangeType<false>)?.(\n newSelection as SelectValueType<false>,\n );\n }\n },\n [isMultiselect, onChange, selection],\n );\n\n // Scrolls the combobox to the far right\n // Used when `overflow == 'scroll-x'`\n const scrollToEnd = () => {\n if (inputWrapperRef && inputWrapperRef.current) {\n // TODO - consider converting to .scrollTo(). This is not yet wuppoted in IE or jsdom\n inputWrapperRef.current.scrollLeft = inputWrapperRef.current.scrollWidth;\n }\n };\n\n const placeholderValue =\n multiselect && isArray(selection) && selection.length > 0\n ? undefined\n : placeholder;\n\n const allOptions = useMemo(() => flattenChildren(children), [children]);\n\n const getDisplayNameForValue = useCallback(\n (value: string | null): string => {\n return value\n ? allOptions.find(opt => opt.value === value)?.displayName ?? value\n : '';\n },\n [allOptions],\n );\n\n // Computes whether the option is visible based on the current input\n const isOptionVisible = useCallback(\n (option: string | OptionObject): boolean => {\n const value = typeof option === 'string' ? option : option.value;\n\n // If filtered options are provided\n if (filteredOptions && filteredOptions.length > 0) {\n return filteredOptions.includes(value);\n }\n\n // otherwise, we do our own filtering\n const displayName =\n typeof option === 'string'\n ? getDisplayNameForValue(value)\n : option.displayName;\n return displayName.toLowerCase().includes(inputValue.toLowerCase());\n },\n [filteredOptions, getDisplayNameForValue, inputValue],\n );\n\n const visibleOptions = useMemo(() => allOptions.filter(isOptionVisible), [\n allOptions,\n isOptionVisible,\n ]);\n\n const isValueValid = useCallback(\n (value: string | null): boolean => {\n return value ? !!allOptions.find(opt => opt.value === value) : false;\n },\n [allOptions],\n );\n\n const getIndexOfValue = useCallback(\n (value: string | null): number => {\n return visibleOptions\n ? visibleOptions.findIndex(option => option.value === value)\n : -1;\n },\n [visibleOptions],\n );\n\n const getValueAtIndex = useCallback(\n (index: number): string | undefined => {\n if (visibleOptions && visibleOptions.length >= index) {\n const option = visibleOptions[index];\n return option ? option.value : undefined;\n }\n },\n [visibleOptions],\n );\n\n const getActiveChipIndex = useCallback(\n () =>\n isMultiselect(selection)\n ? selection.findIndex(value =>\n getChipRef(value)?.current?.contains(document.activeElement),\n )\n : -1,\n [getChipRef, isMultiselect, selection],\n );\n\n /**\n *\n * Focus Management\n *\n */\n\n const getFocusedElementName = useCallback(() => {\n const isFocusOn = {\n Input: inputRef.current?.contains(document.activeElement),\n ClearButton: clearButtonRef.current?.contains(document.activeElement),\n Chip:\n isMultiselect(selection) &&\n selection.some(value =>\n getChipRef(value)?.current?.contains(document.activeElement),\n ),\n };\n const getActiveChipIndex = () =>\n isMultiselect(selection)\n ? selection.findIndex(value =>\n getChipRef(value)?.current?.contains(document.activeElement),\n )\n : -1;\n\n if (isMultiselect(selection) && isFocusOn.Chip) {\n if (getActiveChipIndex() === 0) {\n return 'FirstChip';\n } else if (getActiveChipIndex() === selection.length - 1) {\n return 'LastChip';\n }\n\n return 'MiddleChip';\n } else if (isFocusOn.Input) {\n return 'Input';\n } else if (isFocusOn.ClearButton) {\n return 'ClearButton';\n } else if (comboboxRef.current?.contains(document.activeElement)) {\n return 'Combobox';\n }\n }, [getChipRef, isMultiselect, selection]);\n\n type Direction = 'next' | 'prev' | 'first' | 'last';\n const updateFocusedOption = useCallback(\n (direction: Direction) => {\n const optionsCount = visibleOptions?.length ?? 0;\n const lastIndex = optionsCount - 1 > 0 ? optionsCount - 1 : 0;\n const indexOfFocus = getIndexOfValue(focusedOption);\n\n // Remove focus from chip\n if (direction && isOpen) {\n setFocusedChip(null);\n setInputFocus();\n }\n\n switch (direction) {\n case 'next': {\n const newValue =\n indexOfFocus + 1 < optionsCount\n ? getValueAtIndex(indexOfFocus + 1)\n : getValueAtIndex(0);\n\n setFocusedOption(newValue ?? null);\n break;\n }\n\n case 'prev': {\n const newValue =\n indexOfFocus - 1 >= 0\n ? getValueAtIndex(indexOfFocus - 1)\n : getValueAtIndex(lastIndex);\n\n setFocusedOption(newValue ?? null);\n break;\n }\n\n case 'last': {\n const newValue = getValueAtIndex(lastIndex);\n setFocusedOption(newValue ?? null);\n break;\n }\n\n case 'first':\n default: {\n const newValue = getValueAtIndex(0);\n setFocusedOption(newValue ?? null);\n }\n }\n },\n [\n focusedOption,\n getIndexOfValue,\n getValueAtIndex,\n isOpen,\n setInputFocus,\n visibleOptions?.length,\n ],\n );\n\n const updateFocusedChip = useCallback(\n (direction: Direction | null, relativeToIndex?: number) => {\n if (isMultiselect(selection)) {\n switch (direction) {\n case 'next': {\n const referenceChipIndex = relativeToIndex ?? getActiveChipIndex();\n const nextChipIndex =\n referenceChipIndex + 1 < selection.length\n ? referenceChipIndex + 1\n : selection.length - 1;\n const nextChipValue = selection[nextChipIndex];\n setFocusedChip(nextChipValue);\n break;\n }\n\n case 'prev': {\n const referenceChipIndex = relativeToIndex ?? getActiveChipIndex();\n const prevChipIndex =\n referenceChipIndex > 0\n ? referenceChipIndex - 1\n : referenceChipIndex < 0\n ? selection.length - 1\n : 0;\n const prevChipValue = selection[prevChipIndex];\n setFocusedChip(prevChipValue);\n break;\n }\n\n case 'first': {\n const firstChipValue = selection[0];\n setFocusedChip(firstChipValue);\n break;\n }\n\n case 'last': {\n const lastChipValue = selection[selection.length - 1];\n setFocusedChip(lastChipValue);\n break;\n }\n\n default:\n setFocusedChip(null);\n break;\n }\n }\n },\n [getActiveChipIndex, isMultiselect, selection],\n );\n\n const handleArrowKey = useCallback(\n (direction: 'left' | 'right', event: React.KeyboardEvent<Element>) => {\n // Remove focus from menu\n if (direction) setFocusedOption(null);\n\n const focusedElementName = getFocusedElementName();\n\n switch (direction) {\n case 'right':\n switch (focusedElementName) {\n case 'Input': {\n // If cursor is at the end of the input\n if (\n inputRef.current?.selectionEnd ===\n inputRef.current?.value.length\n ) {\n clearButtonRef.current?.focus();\n }\n break;\n }\n\n case 'LastChip': {\n // if focus is on last chip, go to input\n event.preventDefault();\n setInputFocus(0);\n updateFocusedChip(null);\n break;\n }\n\n case 'FirstChip':\n case 'MiddleChip': {\n updateFocusedChip('next');\n break;\n }\n\n case 'ClearButton':\n default:\n break;\n }\n break;\n\n case 'left':\n switch (focusedElementName) {\n case 'ClearButton': {\n event.preventDefault();\n setInputFocus(inputRef?.current?.value.length);\n break;\n }\n\n case 'Input':\n case 'MiddleChip':\n case 'LastChip': {\n if (isMultiselect(selection)) {\n // Break if cursor is not at the start of the input\n if (\n focusedElementName === 'Input' &&\n inputRef.current?.selectionStart !== 0\n ) {\n break;\n }\n\n updateFocusedChip('prev');\n }\n break;\n }\n\n case 'FirstChip':\n default:\n break;\n }\n break;\n default:\n updateFocusedChip(null);\n break;\n }\n },\n [\n getFocusedElementName,\n isMultiselect,\n selection,\n setInputFocus,\n updateFocusedChip,\n ],\n );\n\n // Update the focused option when the inputValue changes\n useEffect(() => {\n if (inputValue !== prevValue) {\n updateFocusedOption('first');\n }\n }, [inputValue, isOpen, prevValue, updateFocusedOption]);\n\n // When the focused option chenges, update the menu scroll if necessary\n useEffect(() => {\n if (focusedOption) {\n const focusedElementRef = getOptionRef(focusedOption);\n\n if (focusedElementRef && focusedElementRef.current && menuRef.current) {\n const { offsetTop: optionTop } = focusedElementRef.current;\n const {\n scrollTop: menuScroll,\n offsetHeight: menuHeight,\n } = menuRef.current;\n\n if (optionTop > menuHeight || optionTop < menuScroll) {\n menuRef.current.scrollTop = optionTop;\n }\n }\n }\n }, [focusedOption, getOptionRef]);\n\n /**\n *\n * Rendering\n *\n */\n const renderInternalOptions = useCallback(\n (_children: React.ReactNode) => {\n return React.Children.map(_children, child => {\n if (isComponentType(child, 'ComboboxOption')) {\n const { value, displayName } = getNameAndValue(child.props);\n\n if (isOptionVisible(value)) {\n const { className, glyph } = child.props;\n const index = allOptions.findIndex(opt => opt.value === value);\n\n const isFocused = focusedOption === value;\n const isSelected = isMultiselect(selection)\n ? selection.includes(value)\n : selection === value;\n\n const setSelected = () => {\n setFocusedOption(value);\n updateSelection(value);\n setInputFocus();\n\n if (value === selection) {\n closeMenu();\n }\n };\n\n const optionRef = getOptionRef(value);\n\n return (\n <InternalComboboxOption\n value={value}\n displayName={displayName}\n isFocused={isFocused}\n isSelected={isSelected}\n setSelected={setSelected}\n glyph={glyph}\n className={className}\n index={index}\n ref={optionRef}\n />\n );\n }\n } else if (isComponentType(child, 'ComboboxGroup')) {\n const nestedChildren = renderInternalOptions(child.props.children);\n\n if (nestedChildren && nestedChildren?.length > 0) {\n return (\n <InternalComboboxGroup\n label={child.props.label}\n className={child.props.className}\n >\n {renderInternalOptions(nestedChildren)}\n </InternalComboboxGroup>\n );\n }\n }\n });\n },\n [\n allOptions,\n focusedOption,\n getOptionRef,\n isMultiselect,\n isOptionVisible,\n selection,\n setInputFocus,\n updateSelection,\n ],\n );\n\n const renderedOptions = useMemo(() => renderInternalOptions(children), [\n children,\n renderInternalOptions,\n ]);\n\n const renderedChips = useMemo(() => {\n if (isMultiselect(selection)) {\n return selection.filter(isValueValid).map((value, index) => {\n const displayName = getDisplayNameForValue(value);\n\n const onRemove = () => {\n updateFocusedChip('next', index);\n updateSelection(value);\n };\n\n const isFocused = focusedChip === value;\n const chipRef = getChipRef(value);\n\n const onFocus = () => {\n setFocusedChip(value);\n };\n\n return (\n <Chip\n key={value}\n displayName={displayName}\n isFocused={isFocused}\n onRemove={onRemove}\n onFocus={onFocus}\n ref={chipRef}\n />\n );\n });\n }\n }, [\n isMultiselect,\n selection,\n isValueValid,\n getDisplayNameForValue,\n focusedChip,\n getChipRef,\n updateFocusedChip,\n updateSelection,\n ]);\n\n const renderedInputIcons = useMemo(() => {\n const handleClearButtonClick = (\n e: React.MouseEvent<HTMLButtonElement, MouseEvent>,\n ) => {\n if (!disabled) {\n updateSelection(null);\n onClear?.(e);\n onFilter?.('');\n if (!isOpen) {\n openMenu();\n }\n }\n };\n\n return (\n <>\n {clearable && doesSelectionExist && (\n <IconButton\n aria-label=\"Clear selection\"\n aria-disabled={disabled}\n disabled={disabled}\n ref={clearButtonRef}\n onClick={handleClearButtonClick}\n onFocus={handleClearButtonFocus}\n className={clearButton}\n >\n <Icon glyph=\"XWithCircle\" />\n </IconButton>\n )}\n {state === 'error' ? (\n <Icon glyph=\"Warning\" color={uiColors.red.base} className={endIcon} />\n ) : (\n <Icon glyph=\"CaretDown\" className={endIcon} />\n )}\n </>\n );\n }, [\n clearable,\n doesSelectionExist,\n disabled,\n state,\n updateSelection,\n onClear,\n onFilter,\n isOpen,\n ]);\n\n // Do any of the options have an icon?\n const withIcons = useMemo(() => allOptions.some(opt => opt.hasGlyph), [\n allOptions,\n ]);\n\n /**\n *\n * Selection Management\n *\n */\n\n const onCloseMenu = useCallback(() => {\n if (!isMultiselect(selection) && selection === prevSelection) {\n const exactMatchedOption = visibleOptions.find(\n option =>\n option.displayName === inputValue || option.value === inputValue,\n );\n\n // check if inputValue is matches a valid option\n // Set the selection to that value if the component is not controlled\n if (exactMatchedOption && !value) {\n setSelection(exactMatchedOption.value as SelectValueType<M>);\n } else {\n // Revert the value to the previous selection\n const displayName =\n getDisplayNameForValue(selection as SelectValueType<false>) ?? '';\n setInputValue(displayName);\n }\n }\n }, [\n getDisplayNameForValue,\n inputValue,\n isMultiselect,\n prevSelection,\n selection,\n value,\n visibleOptions,\n ]);\n\n const onSelect = useCallback(() => {\n if (doesSelectionExist) {\n if (isMultiselect(selection)) {\n // Scroll the wrapper to the end. No effect if not `overflow=\"scroll-x\"`\n scrollToEnd();\n } else if (!isMultiselect(selection)) {\n // Update the text input\n const displayName =\n getDisplayNameForValue(selection as SelectValueType<false>) ?? '';\n setInputValue(displayName);\n closeMenu();\n }\n } else {\n setInputValue('');\n }\n }, [doesSelectionExist, getDisplayNameForValue, isMultiselect, selection]);\n\n // Set initialValue\n useEffect(() => {\n if (initialValue) {\n if (isArray(initialValue)) {\n // Ensure the values we set are real options\n const filteredValue =\n initialValue.filter(value => isValueValid(value)) ?? [];\n setSelection(filteredValue as SelectValueType<M>);\n } else {\n if (isValueValid(initialValue as string)) {\n setSelection(initialValue);\n }\n }\n } else {\n setSelection(getNullSelection(multiselect));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // When controlled value changes, update the selection\n useEffect(() => {\n if (!isUndefined(value) && value !== prevValue) {\n if (isNull(value)) {\n setSelection(null);\n } else if (isMultiselect(value)) {\n // Ensure the value(s) passed in are valid options\n const newSelection = value.filter(isValueValid) as SelectValueType<M>;\n setSelection(newSelection);\n } else {\n setSelection(\n isValueValid(value as SelectValueType<false>) ? value : null,\n );\n }\n }\n }, [isMultiselect, isValueValid, prevValue, value]);\n\n // onSelect\n // Side effects to run when the selection changes\n useEffect(() => {\n if (selection !== prevSelection) {\n onSelect();\n }\n }, [onSelect, prevSelection, selection]);\n\n // when the menu closes, update the value if needed\n useEffect(() => {\n if (!isOpen && prevOpenState !== isOpen) {\n onCloseMenu();\n }\n }, [isOpen, prevOpenState, onCloseMenu]);\n\n /**\n *\n * Menu management\n *\n */\n const closeMenu = () => setOpen(false);\n const openMenu = () => setOpen(true);\n\n const [menuWidth, setMenuWidth] = useState(0);\n useEffect(() => {\n setMenuWidth(comboboxRef.current?.clientWidth ?? 0);\n }, [comboboxRef, isOpen, focusedOption, selection]);\n const handleTransitionEnd = () => {\n setMenuWidth(comboboxRef.current?.clientWidth ?? 0);\n };\n\n const renderedMenuContents = useMemo((): JSX.Element => {\n switch (searchState) {\n case 'loading': {\n return (\n <span className={menuMessage}>\n <Icon\n glyph=\"Refresh\"\n color={uiColors.blue.base}\n className={loadingIconStyle}\n />\n {searchLoadingMessage}\n </span>\n );\n }\n\n case 'error': {\n return (\n <span className={menuMessage}>\n <Icon glyph=\"Warning\" color={uiColors.red.base} />\n {searchErrorMessage}\n </span>\n );\n }\n\n case 'unset':\n default: {\n if (renderedOptions && renderedOptions.length > 0) {\n return <ul className={menuList}>{renderedOptions}</ul>;\n }\n\n return <span className={menuMessage}>{searchEmptyMessage}</span>;\n }\n }\n }, [\n renderedOptions,\n searchEmptyMessage,\n searchErrorMessage,\n searchLoadingMessage,\n searchState,\n ]);\n\n const viewportSize = useViewportSize();\n\n // Set the max height of the menu\n const maxHeight = useMemo(() => {\n // TODO - consolidate this hook with Select/ListMenu\n const maxMenuHeight = 274;\n const menuMargin = 8;\n\n if (viewportSize && comboboxRef.current && menuRef.current) {\n const {\n top: triggerTop,\n bottom: triggerBottom,\n } = comboboxRef.current.getBoundingClientRect();\n\n // Find out how much space is available above or below the trigger\n const safeSpace = Math.max(\n viewportSize.height - triggerBottom,\n triggerTop,\n );\n\n // if there's more than enough space, set to maxMenuHeight\n // otherwise fill the space available\n return Math.min(maxMenuHeight, safeSpace - menuMargin);\n }\n\n return maxMenuHeight;\n }, [viewportSize, comboboxRef, menuRef]);\n\n // Scroll the menu when the focus changes\n useEffect(() => {\n // get the focused option\n }, [focusedOption]);\n\n /**\n *\n * Event Handlers\n *\n */\n\n // Prevent combobox from gaining focus by default\n const handleInputWrapperMousedown = (e: React.MouseEvent) => {\n if (e.target !== inputRef.current) {\n e.preventDefault();\n }\n };\n\n // Set focus to the input element on click\n const handleInputWrapperClick = (e: React.MouseEvent) => {\n if (e.target !== inputRef.current) {\n let cursorPos = 0;\n\n if (inputRef.current) {\n const mouseX = e.nativeEvent.offsetX;\n const inputRight =\n inputRef.current.offsetLeft + inputRef.current.clientWidth;\n cursorPos = mouseX > inputRight ? inputValue.length : 0;\n }\n\n setInputFocus(cursorPos);\n }\n };\n\n // Fired when the wrapper gains focus\n const handleInputWrapperFocus = () => {\n scrollToEnd();\n openMenu();\n };\n\n // Fired onChange\n const handleInputChange = ({\n target: { value },\n }: React.ChangeEvent<HTMLInputElement>) => {\n setInputValue(value);\n // fire any filter function passed in\n onFilter?.(value);\n };\n\n const handleClearButtonFocus = () => {\n setFocusedOption(null);\n };\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n const isFocusInMenu = menuRef.current?.contains(document.activeElement);\n const isFocusOnCombobox = comboboxRef.current?.contains(\n document.activeElement,\n );\n\n const isFocusInComponent = isFocusOnCombobox || isFocusInMenu;\n\n if (isFocusInComponent) {\n // No support for modifiers yet\n // TODO - Handle support for multiple chip selection\n if (event.ctrlKey || event.shiftKey || event.altKey) {\n return;\n }\n\n const focusedElement = getFocusedElementName();\n\n switch (event.keyCode) {\n case keyMap.Tab: {\n switch (focusedElement) {\n case 'Input': {\n if (!doesSelectionExist) {\n closeMenu();\n updateFocusedOption('first');\n updateFocusedChip(null);\n }\n // else use default behavior\n break;\n }\n\n case 'LastChip': {\n // use default behavior\n updateFocusedChip(null);\n break;\n }\n\n case 'FirstChip':\n case 'MiddleChip': {\n // use default behavior\n break;\n }\n\n case 'ClearButton':\n default:\n break;\n }\n\n break;\n }\n\n case keyMap.Escape: {\n closeMenu();\n updateFocusedOption('first');\n break;\n }\n\n case keyMap.Enter:\n case keyMap.Space: {\n if (isOpen) {\n // prevent typing the space character\n event.preventDefault();\n }\n\n if (\n // Focused on input element\n document.activeElement === inputRef.current &&\n isOpen &&\n !isNull(focusedOption)\n ) {\n updateSelection(focusedOption);\n } else if (\n // Focused on clear button\n document.activeElement === clearButtonRef.current\n ) {\n updateSelection(null);\n setInputFocus();\n }\n break;\n }\n\n case keyMap.Backspace: {\n // Backspace key focuses last chip\n // Delete key does not\n if (\n isMultiselect(selection) &&\n inputRef.current?.selectionStart === 0\n ) {\n updateFocusedChip('last');\n } else {\n openMenu();\n }\n break;\n }\n\n case keyMap.ArrowDown: {\n if (isOpen) {\n // Prevent the page from scrolling\n event.preventDefault();\n }\n openMenu();\n updateFocusedOption('next');\n break;\n }\n\n case keyMap.ArrowUp: {\n if (isOpen) {\n // Prevent the page from scrolling\n event.preventDefault();\n }\n updateFocusedOption('prev');\n break;\n }\n\n case keyMap.ArrowRight: {\n handleArrowKey('right', event);\n break;\n }\n\n case keyMap.ArrowLeft: {\n handleArrowKey('left', event);\n break;\n }\n\n default: {\n if (!isOpen) {\n openMenu();\n }\n }\n }\n }\n };\n\n /**\n *\n * Global Event Handler\n *\n */\n // Global backdrop click handler\n const handleBackdropClick = ({ target }: MouseEvent) => {\n const isChildFocused =\n menuRef.current?.contains(target as Node) ||\n comboboxRef.current?.contains(target as Node) ||\n false;\n\n if (!isChildFocused) {\n setOpen(false);\n }\n };\n useEventListener('mousedown', handleBackdropClick);\n\n return (\n <ComboboxContext.Provider\n value={{\n multiselect,\n darkMode,\n size,\n withIcons,\n disabled,\n chipTruncationLocation,\n chipCharacterLimit,\n inputValue,\n }}\n >\n <div\n className={cx(\n comboboxParentStyle({ darkMode, size, overflow }),\n className,\n )}\n {...rest}\n >\n <div>\n {label && (\n <Label id={labelId} htmlFor={inputId}>\n {label}\n </Label>\n )}\n {description && <Description>{description}</Description>}\n </div>\n\n <InteractionRing\n className={interactionRingStyle}\n disabled={disabled}\n color={interactionRingColor({ state, darkMode })}\n >\n {/* Disable eslint: onClick sets focus. Key events would already have focus */}\n {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n <div\n ref={comboboxRef}\n role=\"combobox\"\n aria-expanded={isOpen}\n aria-controls={menuId}\n aria-owns={menuId}\n tabIndex={-1}\n className={comboboxStyle}\n onMouseDown={handleInputWrapperMousedown}\n onClick={handleInputWrapperClick}\n onFocus={handleInputWrapperFocus}\n onKeyDown={handleKeyDown}\n onTransitionEnd={handleTransitionEnd}\n data-disabled={disabled}\n data-state={state}\n >\n <div\n ref={inputWrapperRef}\n className={inputWrapperStyle({\n overflow,\n isOpen,\n selection,\n value: inputValue,\n })}\n >\n {renderedChips}\n <input\n aria-label={ariaLabel ?? label}\n aria-autocomplete=\"list\"\n aria-controls={menuId}\n aria-labelledby={labelId}\n ref={inputRef}\n id={inputId}\n className={inputElementStyle}\n placeholder={placeholderValue}\n disabled={disabled ?? undefined}\n onChange={handleInputChange}\n value={inputValue}\n autoComplete=\"off\"\n />\n </div>\n {renderedInputIcons}\n </div>\n </InteractionRing>\n\n {state === 'error' && errorMessage && (\n <div className={errorMessageStyle}>{errorMessage}</div>\n )}\n\n {/******* /\n * Menu *\n / *******/}\n <Popover\n active={isOpen && !disabled}\n spacing={4}\n align=\"bottom\"\n justify=\"middle\"\n refEl={comboboxRef}\n adjustOnMutation={true}\n className={menuWrapperStyle({ darkMode, size, width: menuWidth })}\n >\n <div\n id={menuId}\n role=\"listbox\"\n aria-labelledby={labelId}\n aria-expanded={isOpen}\n ref={menuRef}\n className={menuStyle({ maxHeight })}\n onMouseDownCapture={e => e.preventDefault()}\n >\n {renderedMenuContents}\n </div>\n </Popover>\n </div>\n </ComboboxContext.Provider>\n );\n}\n","import { ReactElement, ReactNode } from 'react';\nimport { Either } from '@leafygreen-ui/lib';\n\n/**\n * Prop Enums & Types\n */\n\nexport const ComboboxSize = {\n default: 'default',\n} as const;\nexport type ComboboxSize = typeof ComboboxSize[keyof typeof ComboboxSize];\n\nexport const TrunctationLocation = {\n start: 'start',\n middle: 'middle',\n end: 'end',\n none: 'none',\n} as const;\nexport type TrunctationLocation = typeof TrunctationLocation[keyof typeof TrunctationLocation];\n\nexport const Overflow = {\n expandY: 'expand-y',\n expandX: 'expand-x',\n scrollY: 'scroll-x',\n} as const;\nexport type Overflow = typeof Overflow[keyof typeof Overflow];\n\nexport const State = {\n error: 'error',\n none: 'none',\n} as const;\nexport type State = typeof State[keyof typeof State];\n\nexport const SearchState = {\n unset: 'unset',\n error: 'error',\n loading: 'loading',\n} as const;\nexport type SearchState = typeof SearchState[keyof typeof SearchState];\n\n/**\n * Generic Typing\n */\n\nexport type SelectValueType<M extends boolean> = M extends true\n ? Array<string>\n : string | null;\n\nexport type onChangeType<M extends boolean> = M extends true\n ? (value: SelectValueType<true>) => void\n : (value: SelectValueType<false>) => void;\n\n// Returns the correct empty state for multiselcect / single select\nexport function getNullSelection<M extends boolean>(\n multiselect: M,\n): SelectValueType<M> {\n if (multiselect) {\n return ([] as Array<string>) as SelectValueType<M>;\n } else {\n return null as SelectValueType<M>;\n }\n}\n\n/**\n * Combobox Props\n */\n\nexport interface ComboboxMultiselectProps<M extends boolean> {\n /**\n * Defines whether a user can select multiple options, or only a single option.\n * When using TypeScript, `multiselect` affects the valid values of `initialValue`, `value`, and `onChange`\n */\n multiselect?: M;\n /**\n * The initial selection.\n * Must be a string for a single-select, or an array of strings for multiselect.\n * Changing the initialValue after initial render will not change the selection.\n */\n initialValue?: SelectValueType<M>;\n /**\n * A callback called when the selection changes.\n * Callback recieves a single argument that is the new selection, either string, or string array\n */\n onChange?: onChangeType<M>;\n /**\n * The controlled value of the Combobox.\n * Must be a string for a single-select, or an array of strings for multiselect.\n * Changing value after initial render will affect the selection.\n */\n value?: SelectValueType<M>;\n /**\n * Defines the overflow behavior of a multiselect combobox.\n *\n * `expand-y`: Combobox has fixed width, and additional selections will cause the element to grow in the block direction.\n *\n * `expand-x`: Combobox has fixed height, and additional selections will cause the elemenet to grow in the inline direction.\n *\n * `scroll-x`: Combobox has fixed height and width, and additional selections will cause the element to be scrollable in the x (horizontal) direction.\n */\n overflow?: M extends true ? Overflow : undefined;\n}\n\nexport interface BaseComboboxProps {\n /**\n * Defines the Combobox Options by passing children. Must be `ComboboxOption` or `ComboboxGroup`\n */\n children?: ReactNode;\n\n /**\n * An accessible label for the input, rendered in a <label> to the DOM\n */\n label?: string;\n\n /**\n * An accessible label for the input, used only for screen-readers\n */\n 'aria-label'?: string;\n\n /**\n * A description for the input\n */\n description?: string;\n\n /**\n * A placeholder for the input element. Uses the native `placeholder` attribute.\n */\n placeholder?: string;\n\n /**\n * Disables all interaction with the component\n */\n disabled?: boolean;\n\n /**\n * Defines the visual size of the component\n */\n size?: ComboboxSize;\n\n /**\n * Toggles Dark Mode\n */\n darkMode?: boolean;\n\n /**\n * The error state of the component. Defines whether the error message is displayed.\n */\n state?: State;\n\n /**\n * The message shown below the input when state is `error`\n */\n errorMessage?: string;\n\n /**\n * The state of search results. Toggles search messages within the menu.\n */\n searchState?: SearchState;\n\n /**\n * A message shown within the menu when there are no options passed in as children, or `filteredOptions` is an empty array\n */\n searchEmptyMessage?: string;\n\n /**\n * A message shown within the menu when searchState is `error`\n */\n searchErrorMessage?: string;\n\n /**\n * A message shown within the menu when searchState is `loading`\n */\n searchLoadingMessage?: string;\n\n /**\n * A callback called when the search input changes.\n * Recieves a single argument that is the current input value.\n * Use this callback to set `searchState` and/or `filteredOptions` appropriately\n */\n onFilter?: (value: string) => void;\n\n /**\n * Defines whether the Clear button appears to the right of the input.\n */\n clearable?: boolean;\n\n /**\n * A callback fired when the Clear button is pressed.\n * Fired _after_ `onChange`, and _before_ `onFilter`\n */\n onClear?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;\n\n /**\n * An array used to define which options are displayed.\n * Do not remove options from the JSX children, as this will affect the selected options\n */\n filteredOptions?: Array<string>;\n\n /**\n * Defines where the ellipses appear in a Chip when the length exceeds the `chipCharacterLimit`\n */\n chipTruncationLocation?: TrunctationLocation;\n\n /**\n * Defined the character limit of a multiselect Chip before they start truncating.\n * Note: the three ellipses dots are included in the character limit.\n */\n chipCharacterLimit?: number;\n\n /**\n * Styling prop\n */\n className?: string;\n}\n\nexport type ComboboxProps<M extends boolean> = Either<\n BaseComboboxProps & ComboboxMultiselectProps<M>,\n 'label' | 'aria-label'\n>;\n\n/**\n * Combobox Option Props\n */\ninterface BaseComboboxOptionProps {\n /**\n * The internal value of the option. Used as the identifier in Combobox `initialValue`, value and filteredOptions.\n * When undefined, this is set to `_.kebabCase(displayName)`\n */\n value?: string;\n\n /**\n * The display value of the option. Used as the rendered string within the menu and chips.\n * When undefined, this is set to `value`\n */\n displayName?: string;\n\n /**\n * The icon to display to the left of the option in the menu.\n */\n glyph?: ReactElement;\n\n /**\n * Styling Prop\n */\n className?: string;\n}\n\nexport type ComboboxOptionProps = Either<\n BaseComboboxOptionProps,\n 'value' | 'displayName'\n>;\n\nexport interface InternalComboboxOptionProps {\n value: string;\n displayName: string;\n isSelected: boolean;\n isFocused: boolean;\n setSelected: () => void;\n glyph?: ReactElement;\n className?: string;\n index: number;\n}\n\n/**\n * Combobox Group Props\n */\n\nexport interface ComboboxGroupProps {\n /**\n * Label for the group of options\n */\n label: string;\n\n /**\n * Options in the group. Must be one or more `ComboboxOption` components\n */\n children: React.ReactNode;\n\n /**\n * Styling prop\n */\n className?: string;\n}\n\n/**\n * Combobox Chip\n */\n\nexport interface ChipProps {\n displayName: string;\n isFocused: boolean;\n onRemove: () => void;\n onFocus: () => void;\n}\n"],"names":["_templateObject","_templateObject2","_templateObject3","_templateObject4","_templateObject5","_templateObject6","ComboboxContext","createContext","multiselect","darkMode","size","withIcons","disabled","keyMap","_objectSpread","_keyMap","Backspace","Delete","wrapJSX","str","wrap","element","regex","RegExp","startIndex","search","endIndex","length","nameArr","split","start","slice","join","end","match","matchEl","React","createElement","___EmotionJSX","Fragment","getNameAndValue","_ref","_ref2","valProp","value","nameProp","displayName","kebabCase","flattenChildren","_children","Children","toArray","reduce","acc","child","isComponentType","_getNameAndValue","props","glyph","concat","_toConsumableArray","hasGlyph","children","flexSpan","css","_taggedTemplateLiteral","displayNameStyle","isSelected","InternalComboboxOption","forwardRef","forwardedRef","isFocused","setSelected","className","_useContext","useContext","inputValue","optionTextId","useIdAllocator","prefix","optionRef","useForwardedRef","handleOptionClick","useCallback","e","stopPropagation","target","current","tagName","renderedIcon","useMemo","isComponentGlyph","console","error","renderedChildren","checkbox","Checkbox","label","aria-labelledby","checked","tabIndex","animate","id","Icon","color","uiColors","blue","light1","base","ref","role","aria-selected","aria-label","cx","onClick","onKeyPress","ComboboxOption","_","Error","_templateObject7","_templateObject8","_templateObject9","_templateObject10","_templateObject11","_templateObject12","_templateObject13","_templateObject14","_templateObject15","_templateObject16","_templateObject17","_templateObject18","_templateObject19","_templateObject20","_templateObject21","_templateObject22","_templateObject23","chipWrapperStyle","chipModeStyle","chipSizeStyle","gray","dark3","dark2","light2","chipText","chipButton","Chip","onRemove","onFocus","chipTruncationLocation","_useContext$chipChara","chipCharacterLimit","isTruncated","buttonRef","useRef","truncatedName","chars","substring","trim","useEffect","_buttonRef$current","focus","dataProp","createDataProp","_extends","data-test-id","prop","_buttonRef$current2","contains","InlineDefinition","definition","align","aria-disabled","onKeyDown","keyCode","Enter","Space","comboboxParentStyle","overflow","red","dark1","light3","white","modeStyle","sizeStyle","comboboxStyle","interactionRingStyle","interactionRingColor","state","hovered","undefined","inputWrapperStyle","_ref3","_value$length","isOpen","selection","isMultiselect","isArray","inputLength","inputWidth","baseWrapperStyle","inputElementStyle","clearButton","errorMessageStyle","endIcon","loadingIconAnimation","keyframes","loadingIconStyle","menuWrapperStyle","_ref4","menuModeStyle","menuSizeStyle","_ref4$width","width","menuStyle","_ref5","maxHeight","fontFamilies","default","menuList","menuMessage","comboboxGroupStyle","comboboxGroupLabel","InternalComboboxGroup","groupId","count","ComboboxGroup","_excluded","description","_ref$placeholder","placeholder","ariaLabel","_ref$disabled","_ref$size","_ref$darkMode","_ref$state","errorMessage","_ref$searchState","searchState","_ref$searchEmptyMessa","searchEmptyMessage","_ref$searchErrorMessa","searchErrorMessage","_ref$searchLoadingMes","searchLoadingMessage","filteredOptions","onFilter","_ref$clearable","clearable","onClear","_ref$overflow","_ref$multiselect","initialValue","onChange","_ref$chipCharacterLim","rest","_objectWithoutProperties","getOptionRef","useDynamicRefs","getChipRef","inputId","labelId","menuId","comboboxRef","clearButtonRef","inputWrapperRef","inputRef","menuRef","_useState2","_slicedToArray","useState","setOpen","prevOpenState","usePrevious","_useState4","focusedOption","setFocusedOption","_useState6","setSelection","prevSelection","_useState8","setInputValue","prevValue","_useState10","focusedChip","setFocusedChip","doesSelectionExist","isNull","isString","test","setInputFocus","cursorPos","isUndefined","setSelectionRange","updateSelection","newSelection","clone","includes","splice","indexOf","push","_newSelection","scrollToEnd","scrollLeft","scrollWidth","placeholderValue","allOptions","getDisplayNameForValue","_allOptions$find$disp","_allOptions$find","find","opt","isOptionVisible","option","toLowerCase","visibleOptions","filter","isValueValid","getIndexOfValue","findIndex","getValueAtIndex","index","getActiveChipIndex","_getChipRef","_getChipRef$current","document","activeElement","getFocusedElementName","_inputRef$current","_clearButtonRef$curre","_comboboxRef$current","isFocusOn","some","_getChipRef2","_getChipRef2$current","_getChipRef3","_getChipRef3$current","updateFocusedOption","direction","_visibleOptions$lengt","optionsCount","lastIndex","indexOfFocus","newValue","_newValue","_newValue2","_newValue3","updateFocusedChip","relativeToIndex","referenceChipIndex","nextChipIndex","nextChipValue","_referenceChipIndex","prevChipIndex","prevChipValue","firstChipValue","lastChipValue","handleArrowKey","event","focusedElementName","_inputRef$current2","_inputRef$current3","_clearButtonRef$curre2","selectionEnd","preventDefault","_inputRef$current4","_inputRef$current5","selectionStart","focusedElementRef","optionTop","offsetTop","_menuRef$current","menuScroll","scrollTop","offsetHeight","renderInternalOptions","map","_value","_child$props","_className","closeMenu","nestedChildren","renderedOptions","renderedChips","chipRef","key","renderedInputIcons","IconButton","openMenu","handleClearButtonFocus","onCloseMenu","exactMatchedOption","_getDisplayNameForVal","onSelect","_getDisplayNameForVal2","_initialValue$filter","filteredValue","getNullSelection","_useState12","menuWidth","setMenuWidth","_comboboxRef$current$","_comboboxRef$current2","clientWidth","renderedMenuContents","viewportSize","useViewportSize","_comboboxRef$current$3","getBoundingClientRect","triggerTop","top","triggerBottom","bottom","safeSpace","Math","max","height","min","useEventListener","_menuRef$current3","_comboboxRef$current5","Provider","Label","htmlFor","Description","InteractionRing","aria-expanded","aria-controls","aria-owns","onMouseDown","nativeEvent","offsetX","offsetLeft","_menuRef$current2","_comboboxRef$current4","isFocusInMenu","ctrlKey","shiftKey","altKey","focusedElement","Tab","Escape","_inputRef$current6","ArrowDown","ArrowUp","ArrowRight","ArrowLeft","onTransitionEnd","_comboboxRef$current$2","_comboboxRef$current3","data-disabled","data-state","aria-autocomplete","autoComplete","Popover","active","spacing","justify","refEl","adjustOnMutation","onMouseDownCapture"],"mappings":"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"}
package/dist/util.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { ComboboxOptionProps } from './Combobox.types';
3
+ export declare const keyMap: {
4
+ readonly Backspace: 8;
5
+ readonly Delete: 46;
6
+ readonly ArrowUp: 38;
7
+ readonly ArrowDown: 40;
8
+ readonly ArrowLeft: 37;
9
+ readonly ArrowRight: 39;
10
+ readonly BracketLeft: 91;
11
+ readonly Enter: 13;
12
+ readonly Escape: 27;
13
+ readonly Space: 32;
14
+ readonly Tab: 9;
15
+ };
16
+ /**
17
+ *
18
+ * Wraps the first instance of `wrap` found in `str` within the provided `element`.
19
+ *
20
+ * E.g. `wrapJSX('Apple', 'ap', 'em') => <><em>Ap</em>ple</>`
21
+ *
22
+ * @param str
23
+ * @param wrap
24
+ * @param element
25
+ * @returns `JSX.Element`
26
+ */
27
+ export declare const wrapJSX: (str: string, wrap?: string | undefined, element?: string | undefined) => JSX.Element;
28
+ /**
29
+ *
30
+ * Returns an object with properties `value` & `displayName`
31
+ * based on the props provided
32
+ *
33
+ * @property value: string
34
+ * @property displayName: string
35
+ */
36
+ export declare const getNameAndValue: ({ value: valProp, displayName: nameProp, }: ComboboxOptionProps) => {
37
+ value: string;
38
+ displayName: string;
39
+ };
40
+ export interface OptionObject {
41
+ value: string;
42
+ displayName: string;
43
+ hasGlyph?: boolean;
44
+ }
45
+ /**
46
+ *
47
+ * Flattens multiple nested ComboboxOptions into a 1D array
48
+ *
49
+ * @param _children
50
+ * @returns `Array<OptionObject>`
51
+ */
52
+ export declare const flattenChildren: (_children: React.ReactNode) => Array<OptionObject>;
53
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGvD,eAAO,MAAM,MAAM;;;;;;;;;;;;CAIT,CAAC;AAEX;;;;;;;;;;GAUG;AACH,eAAO,MAAM,OAAO,QACb,MAAM,8DAGV,WAoBF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,+CAGzB,mBAAmB,KAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CAMrB,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,cACf,MAAM,SAAS,KACzB,MAAM,YAAY,CA+BpB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+
2
+ {
3
+ "name": "@leafygreen-ui/combobox",
4
+ "version": "0.9.0",
5
+ "description": "leafyGreen UI Kit Combobox",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/esm/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "typesVersions": {
10
+ "<3.9": { "*": ["ts3.4/*"] }
11
+ },
12
+ "scripts": {
13
+ "build": "../../node_modules/.bin/rollup --config ../../rollup.config.js"
14
+ },
15
+ "license": "Apache-2.0",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "dependencies": {
20
+ "@leafygreen-ui/checkbox": "^6.0.6",
21
+ "@leafygreen-ui/emotion": "^4.0.0",
22
+ "@leafygreen-ui/icon": "^11.6.1",
23
+ "@leafygreen-ui/icon-button": "^9.1.6",
24
+ "@leafygreen-ui/inline-definition": "^2.0.6",
25
+ "@leafygreen-ui/interaction-ring": "^1.1.1",
26
+ "@leafygreen-ui/hooks": "^7.0.0",
27
+ "@leafygreen-ui/lib": "^9.1.0",
28
+ "@leafygreen-ui/palette": "^3.2.2",
29
+ "@leafygreen-ui/popover": "^7.2.2",
30
+ "@leafygreen-ui/typography": "^8.1.0"
31
+ }
32
+ }
package/src/Chip.tsx ADDED
@@ -0,0 +1,223 @@
1
+ import React, { useContext, useEffect, useMemo, useRef } from 'react';
2
+ import { ChipProps, ComboboxSize } from './Combobox.types';
3
+ import Icon from '@leafygreen-ui/icon';
4
+ import { ComboboxContext } from './ComboboxContext';
5
+ import { css, cx } from '@leafygreen-ui/emotion';
6
+ import { uiColors } from '@leafygreen-ui/palette';
7
+ import InlineDefinition from '@leafygreen-ui/inline-definition';
8
+ import { createDataProp } from '@leafygreen-ui/lib';
9
+ import { keyMap } from './util';
10
+
11
+ const chipWrapperStyle = ({
12
+ darkMode,
13
+ size,
14
+ }: {
15
+ darkMode: boolean;
16
+ size: ComboboxSize;
17
+ }) => {
18
+ let chipModeStyle, chipSizeStyle;
19
+
20
+ if (darkMode) {
21
+ chipModeStyle = css``;
22
+ } else {
23
+ chipModeStyle = css`
24
+ --lg-combobox-chip-text-color: ${uiColors.gray.dark3};
25
+ --lg-combobox-chip-icon-color: ${uiColors.gray.dark2};
26
+ --lg-combobox-chip-background-color: ${uiColors.gray.light2};
27
+ --lg-combobox-chip-hover-color: ${uiColors.gray.light1};
28
+ --lg-combobox-chip-focus-color: ${uiColors.blue.light2};
29
+ `;
30
+ }
31
+
32
+ switch (size) {
33
+ case 'default':
34
+ chipSizeStyle = css`
35
+ --lg-combobox-chip-height: 24px;
36
+ --lg-combobox-chip-border-radius: 4px;
37
+ --lg-combobox-chip-font-size: 14px;
38
+ --lg-combobox-chip-line-height: 20px;
39
+ --lg-combobox-chip-padding-x: 6px;
40
+ `;
41
+ break;
42
+ }
43
+
44
+ return cx(
45
+ chipModeStyle,
46
+ chipSizeStyle,
47
+ css`
48
+ display: inline-flex;
49
+ align-items: center;
50
+ overflow: hidden;
51
+ white-space: nowrap;
52
+ height: var(--lg-combobox-chip-height);
53
+ font-size: var(--lg-combobox-chip-font-size);
54
+ line-height: var(--lg-combobox-chip-line-height);
55
+ border-radius: var(--lg-combobox-chip-border-radius);
56
+ color: var(--lg-combobox-chip-text-color);
57
+ background-color: var(--lg-combobox-chip-background-color);
58
+
59
+ // TODO - refine these styles
60
+ /* &:focus, */
61
+ &:focus-within {
62
+ background-color: var(--lg-combobox-chip-focus-color);
63
+ }
64
+ `,
65
+ );
66
+ };
67
+
68
+ const chipText = css`
69
+ padding-inline: var(--lg-combobox-chip-padding-x);
70
+ `;
71
+
72
+ const chipButton = css`
73
+ position: relative;
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ height: 100%;
78
+ width: 100%;
79
+ outline: none;
80
+ border: none;
81
+ background-color: transparent;
82
+ color: var(--lg-combobox-chip-icon-color);
83
+ cursor: pointer;
84
+ transition: background-color 100ms ease-in-out;
85
+
86
+ &:before {
87
+ content: '';
88
+ position: absolute;
89
+ top: 0;
90
+ left: 0;
91
+ height: 100%;
92
+ width: 1px;
93
+ background-color: var(--lg-combobox-chip-hover-color);
94
+ }
95
+
96
+ &:hover {
97
+ background-color: var(--lg-combobox-chip-hover-color);
98
+ }
99
+ `;
100
+
101
+ export const Chip = React.forwardRef<HTMLSpanElement, ChipProps>(
102
+ ({ displayName, isFocused, onRemove, onFocus }: ChipProps, forwardedRef) => {
103
+ const {
104
+ darkMode,
105
+ size,
106
+ disabled,
107
+ chipTruncationLocation,
108
+ chipCharacterLimit = 12,
109
+ } = useContext(ComboboxContext);
110
+
111
+ const isTruncated =
112
+ !!chipCharacterLimit &&
113
+ !!chipTruncationLocation &&
114
+ chipTruncationLocation !== 'none' &&
115
+ displayName.length > chipCharacterLimit;
116
+
117
+ const buttonRef = useRef<HTMLButtonElement>(null);
118
+
119
+ const truncatedName = useMemo(() => {
120
+ if (isTruncated) {
121
+ const ellipsis = '…';
122
+ const chars = chipCharacterLimit - 3; // ellipsis dots included in the char limit
123
+
124
+ switch (chipTruncationLocation) {
125
+ case 'start': {
126
+ const end = displayName
127
+ .substring(displayName.length - chars)
128
+ .trim();
129
+ return ellipsis + end;
130
+ }
131
+
132
+ case 'middle': {
133
+ const start = displayName.substring(0, chars / 2).trim();
134
+ const end = displayName
135
+ .substring(displayName.length - chars / 2)
136
+ .trim();
137
+ return start + ellipsis + end;
138
+ }
139
+
140
+ case 'end': {
141
+ const start = displayName.substring(0, chars).trim();
142
+ return start + ellipsis;
143
+ }
144
+
145
+ default: {
146
+ return displayName;
147
+ }
148
+ }
149
+ }
150
+
151
+ return false;
152
+ }, [chipCharacterLimit, chipTruncationLocation, displayName, isTruncated]);
153
+
154
+ useEffect(() => {
155
+ if (isFocused && !disabled) {
156
+ buttonRef?.current?.focus();
157
+ }
158
+ }, [disabled, forwardedRef, isFocused]);
159
+
160
+ const handleKeyDown = (e: React.KeyboardEvent) => {
161
+ if (
162
+ !disabled &&
163
+ (e.keyCode === keyMap.Delete ||
164
+ e.keyCode === keyMap.Backspace ||
165
+ e.keyCode === keyMap.Enter ||
166
+ e.keyCode === keyMap.Space)
167
+ ) {
168
+ onRemove();
169
+ }
170
+ };
171
+
172
+ const handleChipClick = (e: React.MouseEvent) => {
173
+ // Did not click button
174
+ if (!buttonRef.current?.contains(e.target as Node)) {
175
+ onFocus();
176
+ }
177
+ };
178
+
179
+ const handleButtonClick = () => {
180
+ if (!disabled) {
181
+ onRemove();
182
+ }
183
+ };
184
+
185
+ const dataProp = createDataProp('combobox-chip');
186
+
187
+ return (
188
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events
189
+ <span
190
+ role="option"
191
+ aria-selected={isFocused}
192
+ data-test-id="lg-combobox-chip"
193
+ {...dataProp.prop}
194
+ ref={forwardedRef}
195
+ className={chipWrapperStyle({ darkMode, size })}
196
+ onClick={handleChipClick}
197
+ tabIndex={-1}
198
+ >
199
+ <span className={chipText}>
200
+ {truncatedName ? (
201
+ <InlineDefinition definition={displayName} align="bottom">
202
+ {truncatedName}
203
+ </InlineDefinition>
204
+ ) : (
205
+ displayName
206
+ )}
207
+ </span>
208
+ <button
209
+ aria-label={`Deselect ${displayName}`}
210
+ aria-disabled={disabled}
211
+ disabled={disabled}
212
+ ref={buttonRef}
213
+ className={chipButton}
214
+ onClick={handleButtonClick}
215
+ onKeyDown={handleKeyDown}
216
+ >
217
+ <Icon glyph="X" />
218
+ </button>
219
+ </span>
220
+ );
221
+ },
222
+ );
223
+ Chip.displayName = 'Chip';