+ "content": "'use client'\n\nimport { ScrollArea } from '@base-ui/react/scroll-area'\nimport { SuggestionMenu as Primitive } from '@bazza-ui/react/suggestion-menu'\nimport { cva } from 'class-variance-authority'\nimport type * as React from 'react'\nimport { forwardRef, useCallback, useRef } from 'react'\nimport { cn } from '@/lib/utils'\n\nconst itemVariants = cva([\n 'group/row flex items-center gap-2 text-sm select-none cursor-default',\n 'data-[highlighted]:text-accent-foreground',\n 'h-8 px-4 w-full overflow-hidden relative z-[1]',\n // Highlight background, inset from the popup edges.\n 'before:absolute before:top-0 before:left-1 before:right-1 before:h-full before:rounded-md before:z-[-1]',\n 'data-[highlighted]:before:bg-accent',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50 aria-disabled:opacity-50',\n])\n\nconst scrollAreaViewportVariants = cva('scroll-py-1 overscroll-none', {\n variants: {\n withScrollFade: {\n true: [\n // Gradient fade at the scroll edges, from Base UI ScrollArea's overflow variables.\n 'before:[--scroll-area-overflow-y-start:inherit] after:[--scroll-area-overflow-y-end:inherit]',\n 'before:block after:block',\n 'before:absolute after:absolute before:left-0 after:left-0 before:top-0 after:bottom-0',\n 'before:w-full after:w-full before:z-10 after:z-10',\n 'before:pointer-events-none after:pointer-events-none',\n 'before:bg-gradient-to-b before:from-popover before:to-transparent',\n 'after:bg-gradient-to-t after:from-popover after:to-transparent',\n 'before:h-[min(24px,var(--scroll-area-overflow-y-start,0px))] after:h-[min(24px,var(--scroll-area-overflow-y-end,24px))]',\n ],\n false: '',\n },\n },\n defaultVariants: {\n withScrollFade: true,\n },\n})\n\nconst Root = Primitive.Root\n\nconst Portal = Primitive.Portal\n\nconst Positioner = forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof Primitive.Positioner>\n>(({ className, sideOffset = 8, ...props }, ref) => (\n <Primitive.Positioner\n ref={ref}\n sideOffset={sideOffset}\n className={cn('z-50 outline-none', className)}\n {...props}\n />\n))\nPositioner.displayName = 'SuggestionMenu.Positioner'\n\nconst Popup = forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof Primitive.Popup>\n>(({ className, ...props }, ref) => (\n <Primitive.Popup\n ref={ref}\n className={(state) =>\n cn(\n 'w-64 rounded-lg border bg-popover text-popover-foreground text-sm',\n 'drop-shadow-xl overflow-hidden outline-none',\n 'origin-(--transform-origin)',\n 'opacity-100 scale-100 transition-[opacity,scale] duration-150 ease-out',\n 'motion-reduce:transition-none',\n 'data-[starting-style]:opacity-0 data-[starting-style]:scale-95',\n 'data-[ending-style]:opacity-0 data-[ending-style]:scale-95',\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n />\n))\nPopup.displayName = 'SuggestionMenu.Popup'\n\nconst Surface = Primitive.Surface\n\nexport interface ListProps\n extends Omit<React.ComponentProps<typeof Primitive.List>, 'render'> {\n viewportRef?: React.Ref<HTMLDivElement>\n /** Maximum height of the scrollable area; never taller than the room left on screen. */\n maxHeight?: string | number\n /** Whether to fade the list out at its scroll edges. */\n withScrollFade?: boolean\n}\n\nconst List = forwardRef<HTMLDivElement, ListProps>(\n (\n {\n className,\n viewportRef,\n maxHeight = 320,\n withScrollFade = true,\n children,\n ...props\n },\n ref,\n ) => {\n const scrollContainerRef = useRef<HTMLDivElement | null>(null)\n const mergedViewportRef = useCallback(\n (node: HTMLDivElement | null) => {\n scrollContainerRef.current = node\n if (typeof viewportRef === 'function') viewportRef(node)\n else if (viewportRef) viewportRef.current = node\n },\n [viewportRef],\n )\n const maxHeightValue =\n typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight\n\n return (\n <ScrollArea.Root>\n <ScrollArea.Viewport\n ref={mergedViewportRef}\n className={scrollAreaViewportVariants({ withScrollFade })}\n style={{\n maxHeight: `min(${maxHeightValue}, var(--available-height, ${maxHeightValue}))`,\n }}\n >\n <Primitive.List\n ref={ref}\n className={(state) =>\n cn(\n // ScrollArea.Content sets `min-width: fit-content`, which lets the\n // widest row push past the popup's width; rows truncate instead.\n 'py-1 outline-none !min-w-0',\n typeof className === 'function' ? className(state) : className,\n )\n }\n render={<ScrollArea.Content />}\n scrollContainerRef={scrollContainerRef}\n {...props}\n >\n {children}\n </Primitive.List>\n </ScrollArea.Viewport>\n <ScrollArea.Scrollbar\n orientation=\"vertical\"\n className={cn(\n 'z-10 mx-0.5 my-2 flex w-1 touch-none select-none rounded-full bg-border/50',\n 'opacity-0 data-[hovering]:opacity-100 data-[scrolling]:opacity-100 hover:w-1.5',\n 'transition-[width,opacity] duration-150 ease-out',\n )}\n >\n <ScrollArea.Thumb className=\"relative flex-1 rounded-full bg-muted-foreground/50\" />\n </ScrollArea.Scrollbar>\n </ScrollArea.Root>\n )\n },\n)\nList.displayName = 'SuggestionMenu.List'\n\nconst Item = forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof Primitive.Item>\n>(({ className, ...props }, ref) => (\n <Primitive.Item\n ref={ref}\n className={(state) =>\n cn(\n itemVariants(),\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n />\n))\nItem.displayName = 'SuggestionMenu.Item'\n\nconst LinkItem = forwardRef<\n HTMLAnchorElement,\n React.ComponentProps<typeof Primitive.LinkItem>\n>(({ className, ...props }, ref) => (\n <Primitive.LinkItem\n ref={ref}\n className={(state) =>\n cn(\n itemVariants(),\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n />\n))\nLinkItem.displayName = 'SuggestionMenu.LinkItem'\n\nconst Group = Primitive.Group\n\nconst GroupLabel = forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof Primitive.GroupLabel>\n>(({ className, ...props }, ref) => (\n <Primitive.GroupLabel\n ref={ref}\n className={(state) =>\n cn(\n 'mt-3 mb-1 px-4 text-xs font-medium text-muted-foreground',\n // A label that opens the list, or follows a separator, needs less room above it.\n 'data-[first]:mt-1 [[bazzaui-suggestion-menu-separator]+*>&]:mt-2',\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n />\n))\nGroupLabel.displayName = 'SuggestionMenu.GroupLabel'\n\nconst Separator = forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof Primitive.Separator>\n>(({ className, ...props }, ref) => (\n <Primitive.Separator\n ref={ref}\n className={(state) =>\n cn(\n 'my-1 h-px w-full bg-border',\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n />\n))\nSeparator.displayName = 'SuggestionMenu.Separator'\n\nconst Icon = forwardRef<\n HTMLSpanElement,\n React.ComponentProps<typeof Primitive.Icon>\n>(({ className, ...props }, ref) => (\n <Primitive.Icon\n ref={ref}\n className={(state) =>\n cn(\n 'flex size-4 min-h-4 min-w-4 shrink-0 items-center justify-center',\n 'text-muted-foreground group-data-[highlighted]/row:text-primary',\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n />\n))\nIcon.displayName = 'SuggestionMenu.Icon'\n\nexport interface ShortcutProps extends React.ComponentProps<'span'> {\n /** The keys, shown one `<kbd>` each, e.g. `['⌘', '⌥', '1']`. */\n keys: string[]\n}\n\n/**\n * An editor shortcut shown at the end of a row, as a hint for next time.\n * It's only a label: the menu doesn't bind the keys, so typing stays with the host.\n */\nconst Shortcut = forwardRef<HTMLSpanElement, ShortcutProps>(\n ({ className, keys, ...props }, ref) => (\n <span\n ref={ref}\n className={cn(\n 'ml-auto flex shrink-0 items-center gap-0.5 pl-4',\n 'text-xs font-medium text-muted-foreground',\n className,\n )}\n {...props}\n >\n {keys.map((key, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: keys can repeat and never reorder\n <kbd key={i} className=\"min-w-3 text-center font-sans\">\n {key}\n </kbd>\n ))}\n </span>\n ),\n)\nShortcut.displayName = 'SuggestionMenu.Shortcut'\n\nconst Empty = forwardRef<\n HTMLDivElement,\n Omit<React.ComponentProps<typeof Primitive.Empty>, 'children'> & {\n children?: React.ReactNode\n }\n>(({ className, children, ...props }, ref) => (\n <Primitive.Empty\n ref={ref}\n className={(state) =>\n cn(\n 'flex h-8 items-center justify-center text-sm text-muted-foreground',\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n >\n {children ?? 'No results.'}\n </Primitive.Empty>\n))\nEmpty.displayName = 'SuggestionMenu.Empty'\n\nconst Loading = forwardRef<\n HTMLDivElement,\n Omit<React.ComponentProps<typeof Primitive.Loading>, 'children'> & {\n children?: React.ReactNode\n }\n>(({ className, children, ...props }, ref) => (\n <Primitive.Loading\n ref={ref}\n className={(state) =>\n cn(\n 'flex h-8 items-center justify-center text-sm text-muted-foreground',\n typeof className === 'function' ? className(state) : className,\n )\n }\n {...props}\n >\n {children ?? 'Loading…'}\n </Primitive.Loading>\n))\nLoading.displayName = 'SuggestionMenu.Loading'\n\nexport const SuggestionMenu = {\n Root,\n Portal,\n Positioner,\n Popup,\n Surface,\n List,\n Item,\n LinkItem,\n Group,\n GroupLabel,\n Separator,\n Icon,\n Shortcut,\n Empty,\n Loading,\n createHandle: Primitive.createHandle,\n attachTextTrigger: Primitive.attachTextTrigger,\n useTextTrigger: Primitive.useTextTrigger,\n useDataList: Primitive.useDataList,\n useAsyncMenuCoordinator: Primitive.useAsyncMenuCoordinator,\n}\n",
0 commit comments