@sproutsocial/seeds-react-menu 1.17.0 → 1.18.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.
Files changed (59) hide show
  1. package/.turbo/turbo-build.log +14 -22
  2. package/CHANGELOG.md +30 -0
  3. package/dist/esm/index.js +542 -229
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/esm/v3/index.js +793 -1038
  6. package/dist/esm/v3/index.js.map +1 -1
  7. package/dist/v3/index.d.mts +64 -38
  8. package/dist/v3/index.d.ts +64 -38
  9. package/dist/v3/index.js +813 -1058
  10. package/dist/v3/index.js.map +1 -1
  11. package/dist/v3/menu.css +947 -0
  12. package/package.json +20 -21
  13. package/src/v3/ActionMenuStyles.tsx +169 -149
  14. package/src/v3/Common/ComboboxStyles.tsx +396 -511
  15. package/src/v3/Common/SelectIcons.tsx +17 -8
  16. package/src/v3/Common/SelectItem.tsx +25 -38
  17. package/src/v3/Common/SelectStyles.tsx +146 -144
  18. package/src/v3/Common/cn.ts +38 -0
  19. package/src/v3/MenuButtonStyles.tsx +55 -34
  20. package/src/v3/MultiSearchableSelect.tsx +8 -8
  21. package/src/v3/MultiSelect.tsx +11 -16
  22. package/src/v3/SingleSelect.tsx +13 -13
  23. package/src/v3/menu.css +947 -0
  24. package/tsup.config.ts +0 -1
  25. package/dist/esm/chunk-ROQATIB7.js +0 -357
  26. package/dist/esm/chunk-ROQATIB7.js.map +0 -1
  27. package/dist/esm/v2/index.js +0 -2089
  28. package/dist/esm/v2/index.js.map +0 -1
  29. package/dist/v2/index.d.mts +0 -108
  30. package/dist/v2/index.d.ts +0 -108
  31. package/dist/v2/index.js +0 -2280
  32. package/dist/v2/index.js.map +0 -1
  33. package/src/v2/Autocomplete/Autocomplete.stories.tsx +0 -645
  34. package/src/v2/Autocomplete/MultiAutocomplete.tsx +0 -487
  35. package/src/v2/Autocomplete/SingleAutocomplete.tsx +0 -366
  36. package/src/v2/Autocomplete/index.ts +0 -2
  37. package/src/v2/Common-V2/ComboboxContent.tsx +0 -457
  38. package/src/v2/Common-V2/MenuGroup.tsx +0 -60
  39. package/src/v2/Common-V2/MenuGroupTypes.ts +0 -30
  40. package/src/v2/Common-V2/MenuHeading.tsx +0 -83
  41. package/src/v2/Common-V2/MenuItem.tsx +0 -138
  42. package/src/v2/Common-V2/Popout.tsx +0 -124
  43. package/src/v2/Common-V2/PopoutTypes.tsx +0 -109
  44. package/src/v2/Common-V2/SelectToggleButton.tsx +0 -99
  45. package/src/v2/Common-V2/index.ts +0 -11
  46. package/src/v2/Common-V2/props.ts +0 -84
  47. package/src/v2/Common-V2/styles.tsx +0 -41
  48. package/src/v2/Common-V2/types.ts +0 -16
  49. package/src/v2/Common-V2/useHighlightedIndex.ts +0 -62
  50. package/src/v2/Common-V2/utils.ts +0 -238
  51. package/src/v2/SearchableSelect/AutocompleteContent.tsx +0 -325
  52. package/src/v2/SearchableSelect/SearchableMultiSelect.tsx +0 -527
  53. package/src/v2/SearchableSelect/SearchableSelect.tsx +0 -395
  54. package/src/v2/SearchableSelect/index.ts +0 -12
  55. package/src/v2/Select/MultiSelect.tsx +0 -417
  56. package/src/v2/Select/Select.stories.tsx +0 -593
  57. package/src/v2/Select/SingleSelect.tsx +0 -285
  58. package/src/v2/Select/index.ts +0 -2
  59. package/src/v2/index.ts +0 -2
@@ -1,138 +0,0 @@
1
- import React, { useMemo } from "react";
2
- import type { ReactNode, CSSProperties } from "react";
3
- import { Icon } from "@sproutsocial/seeds-react-icon";
4
- import { Checkbox } from "@sproutsocial/seeds-react-checkbox";
5
- import { Switch } from "@sproutsocial/seeds-react-switch";
6
- import { Radio } from "@sproutsocial/seeds-react-radio";
7
- import { useTheme } from "styled-components";
8
- import { Box } from "@sproutsocial/seeds-react-box";
9
- import {
10
- StyledMenuItem,
11
- MenuItemActionLeft,
12
- MenuItemRow,
13
- MenuItemText,
14
- } from "../../MenuItem/styles";
15
- import type { InputType } from "../../MenuItem/MenuItemTypes";
16
-
17
- /**
18
- * SelectionIndicator - handles logic for appropriate rendering of inputs
19
- * @returns Checkbox | Icon | Radio | Switch
20
- */
21
- const SelectionIndicator = ({
22
- id,
23
- inputType = "icon",
24
- "aria-labelledby": labeledBy,
25
- selected,
26
- }: {
27
- id: string;
28
- "aria-labelledby": string;
29
- selected: boolean;
30
- inputType: InputType;
31
- }) => {
32
- const { space } = useTheme();
33
-
34
- const inputStub = (e: React.SyntheticEvent) => {
35
- e.stopPropagation();
36
- };
37
-
38
- switch (inputType) {
39
- case "checkbox":
40
- return (
41
- <Checkbox
42
- tabIndex={-1}
43
- onChange={() => {}}
44
- checked={selected}
45
- aria-labelledby={labeledBy}
46
- id={`MenuItem-${inputType}-${id}`}
47
- />
48
- );
49
- case "switch":
50
- return (
51
- <Switch
52
- tabIndex={-1}
53
- onClick={inputStub}
54
- checked={!!selected}
55
- aria-labelledby={labeledBy}
56
- id={`MenuItem-${inputType}-${id}`}
57
- />
58
- );
59
- case "radio":
60
- return (
61
- <Radio
62
- tabIndex={-1}
63
- checked={selected}
64
- id={`MenuItem-${inputType}-${id}`}
65
- name={`MenuItem-${inputType}`}
66
- aria-labelledby={labeledBy}
67
- />
68
- );
69
- case "icon":
70
- if (selected) {
71
- return <Icon name="check-solid" aria-hidden size="small" />;
72
- } else {
73
- return <Box width={space["400"]}></Box>;
74
- }
75
- default:
76
- return null;
77
- }
78
- };
79
-
80
- export interface MenuItemProps {
81
- children: ReactNode;
82
- $highlighted?: boolean;
83
- $active?: boolean;
84
- inputType?: InputType;
85
- selected?: boolean;
86
- id?: string | number;
87
- style?: CSSProperties;
88
- [key: string]: any; // For spreading getItemProps and other props
89
- }
90
-
91
- export const MenuItem = ({
92
- children,
93
- $highlighted = false,
94
- $active = false,
95
- inputType,
96
- selected = false,
97
- id,
98
- style,
99
- ...props
100
- }: MenuItemProps) => {
101
- // Generate a label id for the menu item text
102
- const labelId = useMemo(
103
- () =>
104
- id
105
- ? `menu-item-child-${id}`
106
- : `menu-item-child-${Math.random().toString(36).substr(2, 9)}`,
107
- [id]
108
- );
109
-
110
- // If inputType is provided, wrap children with SelectionIndicator
111
- const content = inputType ? (
112
- <MenuItemRow>
113
- <MenuItemActionLeft>
114
- <SelectionIndicator
115
- id={String(id || "")}
116
- aria-labelledby={labelId}
117
- inputType={inputType}
118
- selected={selected}
119
- />
120
- </MenuItemActionLeft>
121
- <MenuItemText id={labelId}>{children}</MenuItemText>
122
- </MenuItemRow>
123
- ) : (
124
- children
125
- );
126
-
127
- return (
128
- <StyledMenuItem
129
- $highlighted={$highlighted}
130
- $active={$active}
131
- $selected={selected}
132
- style={style}
133
- {...props}
134
- >
135
- {content}
136
- </StyledMenuItem>
137
- );
138
- };
@@ -1,124 +0,0 @@
1
- import * as React from "react";
2
- import { AnimatePresence, motion } from "motion/react";
3
- import * as Popover from "@radix-ui/react-popover";
4
- import { MOTION_DURATION_FAST } from "@sproutsocial/seeds-motion/unitless";
5
- import type { TypePopoutProps } from "./PopoutTypes";
6
- import styled from "styled-components";
7
-
8
- const defaultAnimationConfig = {
9
- initial: { opacity: 0 },
10
- animate: { opacity: 1 },
11
- exit: { opacity: 0 },
12
- transition: {
13
- ease: "circOut",
14
- type: "tween",
15
- duration: MOTION_DURATION_FAST,
16
- },
17
- };
18
-
19
- const StyledContent = styled(Popover.Content)`
20
- width: var(--radix-popover-trigger-width);
21
- max-height: var(--radix-popover-content-available-height);
22
- // This should be a prop on the Popout component
23
- z-index: 7;
24
- `;
25
-
26
- const StyledMotionDiv = styled(motion.div)`
27
- background-color: ${({ theme }) => theme.colors.container.background.base};
28
- border: ${({ theme }) => theme.borders[500]};
29
- border-color: ${({ theme }) => theme.colors.container.border.base};
30
- border-radius: ${({ theme }) => theme.radii[500]};
31
- box-shadow: ${({ theme }) => theme.shadows.medium};
32
- padding: ${({ theme }) => theme.space[400]};
33
- `;
34
-
35
- export function Popout({
36
- children,
37
- content,
38
- open,
39
- defaultOpen,
40
- onOpenChange,
41
- animationConfig = defaultAnimationConfig,
42
- side = "bottom",
43
- sideOffset = 8,
44
- align = "center",
45
- alignOffset = 0,
46
- onOpenAutoFocus,
47
- onEscapeKeyDown,
48
- onCloseAutoFocus,
49
- onPointerDownOutside,
50
- onInteractOutside,
51
- ...rest
52
- }: TypePopoutProps) {
53
- const [isOpen, setIsOpen] = React.useState(defaultOpen ?? false);
54
-
55
- const handleOpenChange = React.useCallback(
56
- (newOpen: boolean) => {
57
- setIsOpen(newOpen);
58
- onOpenChange?.(newOpen);
59
- },
60
- [onOpenChange]
61
- );
62
-
63
- // Use controlled state if open prop is provided
64
- const isControlled = open !== undefined;
65
- const popoverOpen = isControlled ? open : isOpen;
66
- const popoverOnOpenChange = isControlled ? onOpenChange : handleOpenChange;
67
-
68
- // Handle ref forwarding for components that use innerRef instead of ref
69
- // (e.g., seeds-react-button)
70
- // Radix UI's asChild passes ref, but Button uses innerRef
71
- const radixRef = React.useRef<HTMLButtonElement | null>(null);
72
-
73
- // The popout needs its role and aria controls props to be overridden.
74
- // This should not be treated like a dialog when used in a menu.
75
- // The combobox itself needs aria-expanded and aria-controls
76
- // We could also likely try to collapse the popout and downshift props by merging them and their onclick
77
- // At the moment, if the popout content is the combobox the onclick gets overridden by the popout
78
- return (
79
- <Popover.Root open={popoverOpen} onOpenChange={popoverOnOpenChange}>
80
- {/*
81
- The Anchor provides the positioning reference for the popout content.
82
- We can't rely on Popover.Trigger's ref for positioning because the children
83
- (e.g. Input) are class components that don't support forwardRef — Radix's
84
- asChild would connect the ref to the class instance rather than the DOM node.
85
- The Anchor is absolutely positioned at the top or bottom edge of the wrapper
86
- so the popout content appears correctly above or below the trigger.
87
- */}
88
- <div style={{ position: "relative" }}>
89
- <Popover.Trigger ref={radixRef} asChild>
90
- {children}
91
- </Popover.Trigger>
92
- <Popover.Anchor
93
- style={{
94
- position: "absolute",
95
- top: side === "top" ? 0 : "100%",
96
- left: 0,
97
- right: 0,
98
- height: "1px",
99
- }}
100
- />
101
- </div>
102
- <Popover.Portal>
103
- <StyledContent
104
- side={side}
105
- sideOffset={sideOffset}
106
- align={align}
107
- alignOffset={alignOffset}
108
- onOpenAutoFocus={onOpenAutoFocus}
109
- onEscapeKeyDown={onEscapeKeyDown}
110
- onCloseAutoFocus={onCloseAutoFocus}
111
- onPointerDownOutside={onPointerDownOutside}
112
- onInteractOutside={onInteractOutside}
113
- // This only needs added to Popouts used in Menus
114
- // those do not need the dialog role which radix applies automatically
115
- role="presentation"
116
- >
117
- <StyledMotionDiv {...animationConfig} {...rest}>
118
- {content}
119
- </StyledMotionDiv>
120
- </StyledContent>
121
- </Popover.Portal>
122
- </Popover.Root>
123
- );
124
- }
@@ -1,109 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import * as React from "react";
3
- import type {
4
- TypeSystemCommonProps,
5
- TypeSystemLayoutProps,
6
- TypeStyledComponentsCommonProps,
7
- } from "@sproutsocial/seeds-react-system-props";
8
- import type { HTMLMotionProps } from "motion/react";
9
-
10
- export interface TypePopoutProps
11
- extends TypeStyledComponentsCommonProps,
12
- TypeSystemCommonProps,
13
- TypeSystemLayoutProps,
14
- Omit<
15
- React.ComponentPropsWithoutRef<"div">,
16
- "color" | "children" | "content"
17
- > {
18
- /**
19
- * The content that the popout should be attached to (renders in Popover.Trigger)
20
- */
21
- children: React.ReactElement<any>;
22
-
23
- /**
24
- * The content to be shown in the popout (renders in Popover.Content)
25
- */
26
- content: React.ReactNode;
27
-
28
- /**
29
- * Whether the popout is open (controlled)
30
- */
31
- open?: boolean;
32
-
33
- /**
34
- * Default open state (uncontrolled)
35
- */
36
- defaultOpen?: boolean;
37
-
38
- /**
39
- * Callback fired when the open state changes
40
- */
41
- onOpenChange?: (open: boolean) => void;
42
-
43
- /**
44
- * Used to override the default Popout animations.
45
- * Any props that are valid for use on a Motion div can be passed here as an object.
46
- * See https://motion.dev/docs/react-motion-component#props
47
- */
48
- animationConfig?: Omit<HTMLMotionProps<"div">, "children">;
49
-
50
- /**
51
- * The preferred side of the trigger to render against when open.
52
- * @default "bottom"
53
- */
54
- side?: "top" | "right" | "bottom" | "left";
55
-
56
- /**
57
- * The distance in pixels from the trigger.
58
- * @default 8
59
- */
60
- sideOffset?: number;
61
-
62
- /**
63
- * The preferred alignment against the trigger.
64
- * @default "center"
65
- */
66
- align?: "start" | "center" | "end";
67
-
68
- /**
69
- * An offset in pixels from the "start" or "end" alignment options.
70
- * @default 0
71
- */
72
- alignOffset?: number;
73
-
74
- /**
75
- * Event handler called when the popout content tries to auto-focus on open.
76
- * Can be used to prevent the default auto-focus behavior or customize it.
77
- * If not provided, defaults to allowing auto-focus on the content.
78
- */
79
- onOpenAutoFocus?: (event: Event) => void;
80
-
81
- /**
82
- * Event handler called when the Escape key is pressed while the popout is open.
83
- * Can be used to prevent the default close behavior or customize it.
84
- */
85
- onEscapeKeyDown?: (event: KeyboardEvent) => void;
86
-
87
- /**
88
- * Event handler called when the popout tries to return focus to the trigger on close.
89
- * Can be used to prevent the default focus return behavior or customize it.
90
- */
91
- onCloseAutoFocus?: (event: Event) => void;
92
-
93
- /**
94
- * Event handler called when a pointer down event occurs outside the popout content.
95
- * Call `event.preventDefault()` to prevent the default dismiss behavior.
96
- * Useful when the Popout is nested inside a Radix Dialog (e.g. ModalV2) to
97
- * avoid conflicts between nested DismissableLayer instances.
98
- */
99
- onPointerDownOutside?: (
100
- event: CustomEvent<{ originalEvent: PointerEvent }>
101
- ) => void;
102
-
103
- /**
104
- * Event handler called when an interaction (pointer or focus) occurs outside
105
- * the popout content. Call `event.preventDefault()` to prevent the popout
106
- * from closing.
107
- */
108
- onInteractOutside?: (event: CustomEvent<{ originalEvent: Event }>) => void;
109
- }
@@ -1,99 +0,0 @@
1
- import styled, { css } from "styled-components";
2
- import { Icon } from "@sproutsocial/seeds-react-icon";
3
- import {
4
- MenuToggleButton,
5
- type TypeMenuToggleButtonProps,
6
- } from "../../MenuToggleButton";
7
- import Button from "@sproutsocial/seeds-react-button";
8
-
9
- export interface TypeSelectToggleButtonProps extends TypeMenuToggleButtonProps {
10
- invalid?: boolean;
11
- isOpen?: boolean;
12
- shouldOverflow?: boolean;
13
- }
14
-
15
- // I don't think this needs to be a button.
16
- // Looking at https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
17
- // It appears that a div is acceptable.
18
- export const StyledSelectToggleButton = styled(
19
- Button
20
- )<TypeSelectToggleButtonProps>`
21
- border-color: ${(props) => props.theme.colors.form.border.base};
22
- width: 100%;
23
-
24
- ${(props) =>
25
- props.invalid &&
26
- css`
27
- border-color: ${(props) => props.theme.colors.form.border.error};
28
- `}
29
- `;
30
-
31
- export interface TypeStyledChevronProps {
32
- $isOpen?: boolean;
33
- invalid?: boolean;
34
- }
35
-
36
- const StyledChevron = styled.div<TypeStyledChevronProps>`
37
- margin-left: ${({ theme }) => theme.space[300]};
38
-
39
- ${({ invalid }) =>
40
- invalid &&
41
- css`
42
- color: ${(props) => props.theme.colors.icon.error};
43
- `}
44
- `;
45
-
46
- const RotatingIcon = styled(Icon)<{ $isOpen?: boolean }>`
47
- transition: transform 0.15s;
48
-
49
- ${({ $isOpen }) =>
50
- $isOpen &&
51
- `
52
- transform: rotate(-180deg);
53
- `}
54
- `;
55
-
56
- const StyledInnerButton = styled.div`
57
- display: flex;
58
- justify-content: space-between;
59
- `;
60
-
61
- const EllipsisContent = styled.div`
62
- overflow: hidden;
63
- text-overflow: ellipsis;
64
- white-space: nowrap;
65
- `;
66
-
67
- const WrappedContent = styled.div`
68
- flex: 1;
69
- width: 100%;
70
- text-align: left;
71
- text-wrap: auto;
72
- `;
73
-
74
- export const SelectToggleButton = ({
75
- children,
76
- isOpen,
77
- shouldOverflow,
78
- ...buttonProps
79
- }: TypeSelectToggleButtonProps) => {
80
- return (
81
- <StyledSelectToggleButton {...buttonProps}>
82
- <StyledInnerButton>
83
- {shouldOverflow ? (
84
- <WrappedContent>{children}</WrappedContent>
85
- ) : (
86
- <EllipsisContent>{children}</EllipsisContent>
87
- )}
88
- <StyledChevron $isOpen={isOpen} invalid={buttonProps.invalid}>
89
- <RotatingIcon
90
- name="chevron-down-outline"
91
- fixedWidth
92
- aria-hidden
93
- $isOpen={isOpen}
94
- />
95
- </StyledChevron>
96
- </StyledInnerButton>
97
- </StyledSelectToggleButton>
98
- );
99
- };
@@ -1,11 +0,0 @@
1
- export * from "./types";
2
- export * from "./utils";
3
- export * from "./Popout";
4
- export * from "./PopoutTypes";
5
- export * from "./SelectToggleButton";
6
- export * from "./MenuItem";
7
- export * from "./MenuHeading";
8
- export * from "./MenuGroup";
9
- export * from "./props";
10
- export * from "./useHighlightedIndex";
11
- export * from "./ComboboxContent";
@@ -1,84 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import type { DataWithId, ToggleItem, GroupHeading } from "./types";
3
-
4
- /**
5
- * Base props common to all select/autocomplete components
6
- */
7
- export interface BaseSelectProps<T extends DataWithId> {
8
- id: string;
9
- data: T[];
10
- isOpen?: boolean;
11
- itemToKey?: (item: T) => string | number;
12
- itemToString: (item: T | null) => string;
13
- renderItem: (item: T) => ReactNode;
14
- groupBy?: (item: T) => string;
15
- renderGroupHeading?: (label: string) => string;
16
- isVirtualized?: boolean;
17
- estimatedItemHeight?: number;
18
- placeholder: string;
19
- onIsOpenChange?: (isOpen: boolean) => void;
20
- stateReducer?: any;
21
- menuProps?: React.HTMLAttributes<HTMLDivElement>;
22
- onPointerDownOutside?: (
23
- event: CustomEvent<{ originalEvent: PointerEvent }>
24
- ) => void;
25
- onInteractOutside?: (event: CustomEvent<{ originalEvent: Event }>) => void;
26
- /**
27
- * Controls which side the popout appears relative to the trigger.
28
- * @default "bottom"
29
- */
30
- popoutPlacement?: "top" | "bottom";
31
- }
32
- /**
33
- * Props common to all single-select components
34
- */
35
- export interface BaseSingleSelectProps<T extends DataWithId>
36
- extends BaseSelectProps<T> {
37
- selectedItemId?: T["id"] | null;
38
- onSelectedItemIdChange?: (itemId: T["id"] | null) => void;
39
- placeholderItem?: ToggleItem;
40
- inputType?: "radio" | "icon";
41
- removePlaceholderItem?: boolean;
42
- }
43
-
44
- /**
45
- * Props common to all multi-select components
46
- */
47
- export interface BaseMultiSelectProps<T extends DataWithId>
48
- extends BaseSelectProps<T> {
49
- selectedItemIds?: T["id"][];
50
- onSelectedItemIdsChange?: (itemIds: T["id"][]) => void;
51
- showSelectedItems?: boolean;
52
- selectHeadings?: boolean;
53
- selectAllItem?: ToggleItem;
54
- inputType?: "checkbox" | "icon";
55
- }
56
-
57
- /**
58
- * Props common to all autocomplete/searchable components
59
- */
60
- export interface BaseAutocompleteProps<T extends DataWithId>
61
- extends BaseSelectProps<T> {
62
- EmptyState: ReactNode;
63
- inputValue?: string;
64
- itemToSearchString?: (item: T) => string;
65
- onInputValueChange?: (value: string) => void;
66
- }
67
-
68
- /**
69
- * Props for single-select autocomplete/searchable components
70
- */
71
- export interface BaseSingleAutocompleteProps<T extends DataWithId>
72
- extends BaseSingleSelectProps<T>,
73
- BaseAutocompleteProps<T> {
74
- inputType?: "radio" | "icon";
75
- }
76
-
77
- /**
78
- * Props for multi-select autocomplete/searchable components
79
- */
80
- export interface BaseMultiAutocompleteProps<T extends DataWithId>
81
- extends BaseMultiSelectProps<T>,
82
- BaseAutocompleteProps<T> {
83
- inputType?: "checkbox" | "icon";
84
- }
@@ -1,41 +0,0 @@
1
- import styled from "styled-components";
2
- import Text from "@sproutsocial/seeds-react-text";
3
- import {
4
- border,
5
- layout,
6
- position,
7
- flexbox,
8
- grid,
9
- color,
10
- space,
11
- } from "styled-system";
12
- import type { TypeStyledMenuGroupProps } from "./MenuGroupTypes";
13
-
14
- export const StyledMenuGroup = styled.li<TypeStyledMenuGroupProps>`
15
- font-family: ${({ theme }) => theme.fontFamily};
16
- &:not(:last-child) {
17
- border-bottom: 1px solid
18
- ${({ theme }) => theme.colors.container.border.base};
19
- }
20
- list-style: none;
21
- margin: 0;
22
-
23
- ${border}
24
- ${position}
25
- ${color}
26
- ${flexbox}
27
- ${grid}
28
- ${layout}
29
- ${space}
30
- `;
31
-
32
- export const StyledTitle = styled(Text.SmallSubHeadline)`
33
- font-size: ${({ theme }) => theme.typography[100].fontSize};
34
- margin: ${({ theme }) => theme.space[400]} ${({ theme }) => theme.space[400]}
35
- 0px ${({ theme }) => theme.space[400]};
36
- `;
37
-
38
- export const StyledMenuGroupUl = styled.ul`
39
- margin: 0;
40
- padding: 0;
41
- `;
@@ -1,16 +0,0 @@
1
- export interface DataWithId {
2
- id: string | number;
3
- }
4
-
5
- export interface ToggleItem {
6
- id: "select-all" | "clear-selection";
7
- label: string;
8
- }
9
-
10
- export interface GroupHeading {
11
- type: "heading";
12
- id: string;
13
- label: string;
14
- }
15
-
16
- export type GroupedItem<T> = T | GroupHeading | ToggleItem;
@@ -1,62 +0,0 @@
1
- import { useMemo } from "react";
2
- import { isGroupHeading } from "./utils";
3
- import type { GroupedItem, DataWithId } from "./types";
4
-
5
- /**
6
- * Hook to map the highlighted index from Downshift (which only knows about filtered items)
7
- * to the full items array (which includes headings).
8
- *
9
- * @param items - The full items array (includes headings, toggle items, and data items)
10
- * @param itemsForDownshift - The filtered items array passed to Downshift (may exclude headings)
11
- * @param downshiftHighlightedIndex - The highlighted index from Downshift
12
- * @param hasGroups - Whether groupBy is provided (determines if we need to map indices)
13
- * @param selectHeadings - Whether headings are selectable (affects how we map indices)
14
- * @returns The highlighted index in the full items array, or null if invalid
15
- */
16
- export function useHighlightedIndex<T extends DataWithId>(
17
- items: GroupedItem<T>[],
18
- itemsForDownshift: GroupedItem<T>[],
19
- downshiftHighlightedIndex: number | null | undefined,
20
- hasGroups: boolean,
21
- selectHeadings: boolean
22
- ): number | null {
23
- return useMemo<number | null>(() => {
24
- if (
25
- downshiftHighlightedIndex === null ||
26
- downshiftHighlightedIndex === undefined ||
27
- downshiftHighlightedIndex < 0
28
- ) {
29
- return null;
30
- }
31
-
32
- // If no groups, the indices are the same
33
- if (!hasGroups) {
34
- return downshiftHighlightedIndex;
35
- }
36
-
37
- // Get the highlighted item from the downshift items array
38
- const highlightedItem = itemsForDownshift[downshiftHighlightedIndex];
39
- if (!highlightedItem) {
40
- return null;
41
- }
42
-
43
- // Find the index of this item in the full items array
44
- if (selectHeadings) {
45
- // When headings are selectable, itemsForDownshift === items,
46
- // so we can find the index directly in the items array
47
- return items.findIndex((item) => item === highlightedItem);
48
- } else {
49
- // When headings are not selectable, itemsForDownshift excludes headings,
50
- // so we need to find the item in the full items array, skipping headings
51
- return items.findIndex(
52
- (item) => !isGroupHeading(item) && item === highlightedItem
53
- );
54
- }
55
- }, [
56
- items,
57
- itemsForDownshift,
58
- downshiftHighlightedIndex,
59
- hasGroups,
60
- selectHeadings,
61
- ]);
62
- }