@lobehub/ui 5.43.1 → 5.44.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 (47) hide show
  1. package/es/base-ui/ContextMenu/ContextMenuHost.mjs +5 -1
  2. package/es/base-ui/ContextMenu/ContextMenuHost.mjs.map +1 -1
  3. package/es/base-ui/ContextMenu/ContextMenuTrigger.d.mts +3 -1
  4. package/es/base-ui/ContextMenu/ContextMenuTrigger.mjs +7 -3
  5. package/es/base-ui/ContextMenu/ContextMenuTrigger.mjs.map +1 -1
  6. package/es/base-ui/ContextMenu/store.d.mts +3 -1
  7. package/es/base-ui/ContextMenu/store.mjs +6 -2
  8. package/es/base-ui/ContextMenu/store.mjs.map +1 -1
  9. package/es/base-ui/DropdownMenu/DropdownMenu.mjs +6 -2
  10. package/es/base-ui/DropdownMenu/DropdownMenu.mjs.map +1 -1
  11. package/es/base-ui/DropdownMenu/atoms.d.mts +4 -2
  12. package/es/base-ui/DropdownMenu/atoms.mjs +17 -5
  13. package/es/base-ui/DropdownMenu/atoms.mjs.map +1 -1
  14. package/es/base-ui/DropdownMenu/sharedStyle.mjs +26 -3
  15. package/es/base-ui/DropdownMenu/sharedStyle.mjs.map +1 -1
  16. package/es/base-ui/DropdownMenu/type.d.mts +3 -1
  17. package/es/base-ui/Input/style.d.mts +2 -2
  18. package/es/base-ui/ScrollArea/atoms.mjs +4 -4
  19. package/es/base-ui/ScrollArea/atoms.mjs.map +1 -1
  20. package/es/base-ui/Select/Select.mjs +1 -3
  21. package/es/base-ui/Select/Select.mjs.map +1 -1
  22. package/es/base-ui/Select/hooks.mjs +1 -40
  23. package/es/base-ui/Select/hooks.mjs.map +1 -1
  24. package/es/base-ui/Select/parts.mjs +20 -18
  25. package/es/base-ui/Select/parts.mjs.map +1 -1
  26. package/es/base-ui/Select/renderOptions.mjs +1 -2
  27. package/es/base-ui/Select/renderOptions.mjs.map +1 -1
  28. package/es/base-ui/Select/style.mjs +2 -4
  29. package/es/base-ui/Select/style.mjs.map +1 -1
  30. package/es/base-ui/Select/type.d.mts +3 -3
  31. package/es/base-ui/Text/style.d.mts +1 -1
  32. package/es/base-ui/index.d.mts +4 -2
  33. package/es/base-ui/index.mjs +1 -1
  34. package/es/base-ui/virtual/MenuVirtualList.d.mts +3 -0
  35. package/es/base-ui/virtual/MenuVirtualList.mjs +28 -0
  36. package/es/base-ui/virtual/MenuVirtualList.mjs.map +1 -0
  37. package/es/base-ui/virtual/VirtualScrollArea.d.mts +3 -0
  38. package/es/base-ui/virtual/VirtualScrollArea.mjs +82 -0
  39. package/es/base-ui/virtual/VirtualScrollArea.mjs.map +1 -0
  40. package/es/base-ui/virtual/index.d.mts +5 -0
  41. package/es/base-ui/virtual/type.d.mts +26 -0
  42. package/es/base-ui/virtual/useMenuVirtualList.d.mts +3 -0
  43. package/es/base-ui/virtual/useMenuVirtualList.mjs +256 -0
  44. package/es/base-ui/virtual/useMenuVirtualList.mjs.map +1 -0
  45. package/es/index.d.mts +1 -1
  46. package/es/index.mjs +1 -1
  47. package/package.json +1 -1
@@ -7,6 +7,7 @@ import Button from "./Button/Button.mjs";
7
7
  import { styles as styles$9 } from "./Switch/style.mjs";
8
8
  import { SwitchIcon, SwitchRoot, SwitchThumb, useSwitchContext } from "./Switch/atoms.mjs";
9
9
  import Switch from "./Switch/Switch.mjs";
10
+ import { ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "./ScrollArea/atoms.mjs";
10
11
  import { DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuFooter, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuHeader, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemDesc, DropdownMenuItemExtra, DropdownMenuItemIcon, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuPopup, DropdownMenuPortal, DropdownMenuPositioner, DropdownMenuRoot, DropdownMenuScrollViewport, DropdownMenuSeparator, DropdownMenuSubmenuArrow, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSwitchItem, DropdownMenuTrigger } from "./DropdownMenu/atoms.mjs";
11
12
  import { parseTrigger } from "../utils/parseTrigger.mjs";
12
13
  import { renderDropdownMenuItems } from "./DropdownMenu/renderItems.mjs";
@@ -28,7 +29,6 @@ import SkeletonAvatar from "./Skeleton/SkeletonAvatar.mjs";
28
29
  import SkeletonText from "./Skeleton/SkeletonText.mjs";
29
30
  import Skeleton from "./Skeleton/index.mjs";
30
31
  import { ToastHost, toast, useToast } from "./Toast/imperative.mjs";
31
- import { ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "./ScrollArea/atoms.mjs";
32
32
  import { ScrollArea } from "./ScrollArea/ScrollArea.mjs";
33
33
  import { styles } from "./Accordion/style.mjs";
34
34
  import { AccordionHeader, AccordionItem, AccordionPanel, AccordionRoot, AccordionTrigger, useAccordionContext } from "./Accordion/atoms.mjs";
@@ -0,0 +1,3 @@
1
+ import "./type.mjs";
2
+ import "./VirtualScrollArea.mjs";
3
+ import { ReactNode } from "react";
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { VirtualScrollArea } from "./VirtualScrollArea.mjs";
3
+ import { useMenuVirtualList } from "./useMenuVirtualList.mjs";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/base-ui/virtual/MenuVirtualList.tsx
6
+ const MenuVirtualList = ({ children, getItemLabel, keepMounted, ...rest }) => {
7
+ const { keepMountedIndices, viewportProps, viewportRef, virtualChildren, virtualizerRef } = useMenuVirtualList({
8
+ children,
9
+ enabled: true,
10
+ getItemLabel,
11
+ keepMounted
12
+ });
13
+ return /* @__PURE__ */ jsx(VirtualScrollArea, {
14
+ ...rest,
15
+ keepMounted: keepMountedIndices,
16
+ viewportProps: {
17
+ ref: viewportRef,
18
+ ...viewportProps
19
+ },
20
+ virtualizerRef,
21
+ children: virtualChildren
22
+ });
23
+ };
24
+ MenuVirtualList.displayName = "MenuVirtualList";
25
+ //#endregion
26
+ export { MenuVirtualList };
27
+
28
+ //# sourceMappingURL=MenuVirtualList.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MenuVirtualList.mjs","names":[],"sources":["../../../src/base-ui/virtual/MenuVirtualList.tsx"],"sourcesContent":["'use client';\n\nimport { type ReactNode } from 'react';\n\nimport type { VirtualListProps } from './type';\nimport { useMenuVirtualList } from './useMenuVirtualList';\nimport { VirtualScrollArea, type VirtualScrollAreaProps } from './VirtualScrollArea';\n\nexport interface MenuVirtualListProps\n extends\n Omit<VirtualScrollAreaProps, 'viewport' | 'viewportProps' | 'virtualizerRef'>,\n Pick<VirtualListProps, 'getItemLabel' | 'keepMounted'> {\n children: ReactNode;\n}\n\nexport const MenuVirtualList = ({\n children,\n getItemLabel,\n keepMounted,\n ...rest\n}: MenuVirtualListProps) => {\n const { keepMountedIndices, viewportProps, viewportRef, virtualChildren, virtualizerRef } =\n useMenuVirtualList({ children, enabled: true, getItemLabel, keepMounted });\n\n return (\n <VirtualScrollArea\n {...rest}\n keepMounted={keepMountedIndices}\n viewportProps={{ ref: viewportRef, ...viewportProps }}\n virtualizerRef={virtualizerRef}\n >\n {virtualChildren}\n </VirtualScrollArea>\n );\n};\n\nMenuVirtualList.displayName = 'MenuVirtualList';\n"],"mappings":";;;;;AAeA,MAAa,mBAAmB,EAC9B,UACA,cACA,aACA,GAAG,WACuB;CAC1B,MAAM,EAAE,oBAAoB,eAAe,aAAa,iBAAiB,mBACvE,mBAAmB;EAAE;EAAU,SAAS;EAAM;EAAc;CAAY,CAAC;CAE3E,OACE,oBAAC,mBAAD;EACE,GAAI;EACJ,aAAa;EACb,eAAe;GAAE,KAAK;GAAa,GAAG;EAAc;EACpC;EAEf,UAAA;CACgB,CAAA;AAEvB;AAEA,gBAAgB,cAAc"}
@@ -0,0 +1,3 @@
1
+ import "../ScrollArea/atoms.mjs";
2
+ import { ReactElement, ReactNode, Ref } from "react";
3
+ import "virtua";
@@ -0,0 +1,82 @@
1
+ "use client";
2
+ import { ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "../ScrollArea/atoms.mjs";
3
+ import { cloneElement, useCallback, useEffect, useRef } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { createStaticStyles, cx } from "antd-style";
6
+ import { useMergeRefs } from "react-merge-refs";
7
+ import { Virtualizer } from "virtua";
8
+ //#region src/base-ui/virtual/VirtualScrollArea.tsx
9
+ const styles = createStaticStyles(({ css }) => ({
10
+ root: css`
11
+ display: flex;
12
+ flex: 1;
13
+ flex-direction: column;
14
+
15
+ min-height: 0;
16
+ margin-inline: calc(-1 * var(--lobe-virtual-scroll-inset, 0px));
17
+ `,
18
+ viewport: css`
19
+ flex: 1;
20
+ height: auto;
21
+ min-height: 0;
22
+ padding-inline: var(--lobe-virtual-scroll-inset, 0);
23
+ `
24
+ }));
25
+ const INITIAL_ROW_COUNT = 20;
26
+ const VirtualScrollArea = ({ children, className, itemSize, keepMounted, viewport, viewportProps, virtualizerRef, ...rest }) => {
27
+ const viewportRef = useRef(null);
28
+ const mergedViewportRef = useMergeRefs([viewportRef, viewportProps?.ref]);
29
+ const list = /* @__PURE__ */ jsx(Virtualizer, {
30
+ itemSize,
31
+ keepMounted,
32
+ ref: virtualizerRef,
33
+ scrollRef: viewportRef,
34
+ ssrCount: INITIAL_ROW_COUNT,
35
+ children
36
+ });
37
+ const viewportElement = /* @__PURE__ */ jsx(ScrollAreaViewport, {
38
+ tabIndex: -1,
39
+ ...viewportProps,
40
+ className: cx(styles.viewport, viewportProps?.className),
41
+ ref: mergedViewportRef
42
+ });
43
+ return /* @__PURE__ */ jsxs(ScrollAreaRoot, {
44
+ "data-virtual": "",
45
+ ...rest,
46
+ className: cx(styles.root, className),
47
+ children: [cloneElement(viewport ?? viewportElement, {
48
+ children: list,
49
+ ...viewport && { render: viewportElement }
50
+ }), /* @__PURE__ */ jsx(ScrollAreaScrollbar, { children: /* @__PURE__ */ jsx(ScrollAreaThumb, {}) })]
51
+ });
52
+ };
53
+ VirtualScrollArea.displayName = "VirtualScrollArea";
54
+ function usePointerScrollGuard() {
55
+ const pointerScrollRef = useRef(false);
56
+ const timeoutRef = useRef(null);
57
+ const markPointerScroll = useCallback(() => {
58
+ pointerScrollRef.current = true;
59
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
60
+ timeoutRef.current = setTimeout(() => {
61
+ pointerScrollRef.current = false;
62
+ }, 120);
63
+ }, []);
64
+ useEffect(() => {
65
+ return () => {
66
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
67
+ };
68
+ }, []);
69
+ return {
70
+ markPointerScroll,
71
+ pointerScrollRef,
72
+ scrollGuardProps: {
73
+ onPointerDown: markPointerScroll,
74
+ onTouchMove: markPointerScroll,
75
+ onWheel: markPointerScroll
76
+ }
77
+ };
78
+ }
79
+ //#endregion
80
+ export { VirtualScrollArea, usePointerScrollGuard };
81
+
82
+ //# sourceMappingURL=VirtualScrollArea.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VirtualScrollArea.mjs","names":[],"sources":["../../../src/base-ui/virtual/VirtualScrollArea.tsx"],"sourcesContent":["'use client';\n\nimport { createStaticStyles, cx } from 'antd-style';\nimport {\n cloneElement,\n type ReactElement,\n type ReactNode,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n} from 'react';\nimport { useMergeRefs } from 'react-merge-refs';\nimport { Virtualizer, type VirtualizerHandle } from 'virtua';\n\nimport {\n ScrollAreaRoot,\n type ScrollAreaRootProps,\n ScrollAreaScrollbar,\n ScrollAreaThumb,\n ScrollAreaViewport,\n type ScrollAreaViewportProps,\n} from '@/base-ui/ScrollArea/atoms';\n\n// `--lobe-virtual-scroll-inset` is the inline padding of the box the scroll area sits in: the\n// root bleeds out by it so the scrollbar hugs that box's edge, and the viewport pads the rows back.\nconst styles = createStaticStyles(({ css }) => ({\n root: css`\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-height: 0;\n margin-inline: calc(-1 * var(--lobe-virtual-scroll-inset, 0px));\n `,\n viewport: css`\n flex: 1;\n height: auto;\n min-height: 0;\n padding-inline: var(--lobe-virtual-scroll-inset, 0);\n `,\n}));\n\n// Rows mounted on the very first render, before the viewport is measured, so Base UI's\n// list navigation sees the leading rows (not a pinned footer item) when it focuses on open.\nconst INITIAL_ROW_COUNT = 20;\n\nexport interface VirtualScrollAreaProps extends Omit<ScrollAreaRootProps, 'children'> {\n children: ReactNode;\n itemSize?: number;\n keepMounted?: readonly number[];\n /**\n * Element that owns the scrolling box (e.g. a Base UI `Select.List`); it is rendered\n * through the ScrollArea viewport so its own props and role win.\n */\n viewport?: ReactElement<{ children?: ReactNode; render?: ReactElement }>;\n viewportProps?: ScrollAreaViewportProps;\n virtualizerRef?: Ref<VirtualizerHandle>;\n}\n\nexport const VirtualScrollArea = ({\n children,\n className,\n itemSize,\n keepMounted,\n viewport,\n viewportProps,\n virtualizerRef,\n ...rest\n}: VirtualScrollAreaProps) => {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const mergedViewportRef = useMergeRefs([viewportRef, viewportProps?.ref]);\n\n const list = (\n <Virtualizer\n itemSize={itemSize}\n keepMounted={keepMounted}\n ref={virtualizerRef}\n scrollRef={viewportRef}\n ssrCount={INITIAL_ROW_COUNT}\n >\n {children}\n </Virtualizer>\n );\n const viewportElement = (\n <ScrollAreaViewport\n tabIndex={-1}\n {...viewportProps}\n className={cx(styles.viewport, viewportProps?.className as string)}\n ref={mergedViewportRef}\n />\n );\n\n return (\n <ScrollAreaRoot data-virtual=\"\" {...rest} className={cx(styles.root, className as string)}>\n {cloneElement(viewport ?? viewportElement, {\n children: list,\n ...(viewport && { render: viewportElement }),\n })}\n <ScrollAreaScrollbar>\n <ScrollAreaThumb />\n </ScrollAreaScrollbar>\n </ScrollAreaRoot>\n );\n};\n\nVirtualScrollArea.displayName = 'VirtualScrollArea';\n\n// Base UI scrolls the highlighted item into view after focusing it; while the pointer drives\n// the scroll that would yank the list back, so callers consult `pointerScrollRef` first.\nexport function usePointerScrollGuard() {\n const pointerScrollRef = useRef(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const markPointerScroll = useCallback(() => {\n pointerScrollRef.current = true;\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n pointerScrollRef.current = false;\n }, 120);\n }, []);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n };\n }, []);\n\n return {\n markPointerScroll,\n pointerScrollRef,\n scrollGuardProps: {\n onPointerDown: markPointerScroll,\n onTouchMove: markPointerScroll,\n onWheel: markPointerScroll,\n },\n };\n}\n"],"mappings":";;;;;;;;AA0BA,MAAM,SAAS,oBAAoB,EAAE,WAAW;CAC9C,MAAM,GAAG;;;;;;;;CAQT,UAAU,GAAG;;;;;;AAMf,EAAE;AAIF,MAAM,oBAAoB;AAe1B,MAAa,qBAAqB,EAChC,UACA,WACA,UACA,aACA,UACA,eACA,gBACA,GAAG,WACyB;CAC5B,MAAM,cAAc,OAA8B,IAAI;CACtD,MAAM,oBAAoB,aAAa,CAAC,aAAa,eAAe,GAAG,CAAC;CAExE,MAAM,OACJ,oBAAC,aAAD;EACY;EACG;EACb,KAAK;EACL,WAAW;EACX,UAAU;EAET;CACU,CAAA;CAEf,MAAM,kBACJ,oBAAC,oBAAD;EACE,UAAU;EACV,GAAI;EACJ,WAAW,GAAG,OAAO,UAAU,eAAe,SAAmB;EACjE,KAAK;CACN,CAAA;CAGH,OACE,qBAAC,gBAAD;EAAgB,gBAAa;EAAG,GAAI;EAAM,WAAW,GAAG,OAAO,MAAM,SAAmB;EAAxF,UAAA,CACG,aAAa,YAAY,iBAAiB;GACzC,UAAU;GACV,GAAI,YAAY,EAAE,QAAQ,gBAAgB;EAC5C,CAAC,GACD,oBAAC,qBAAD,EAAA,UACE,oBAAC,iBAAD,CAAkB,CAAA,EACC,CAAA,CACP;;AAEpB;AAEA,kBAAkB,cAAc;AAIhC,SAAgB,wBAAwB;CACtC,MAAM,mBAAmB,OAAO,KAAK;CACrC,MAAM,aAAa,OAA6C,IAAI;CAEpE,MAAM,oBAAoB,kBAAkB;EAC1C,iBAAiB,UAAU;EAC3B,IAAI,WAAW,SAAS,aAAa,WAAW,OAAO;EACvD,WAAW,UAAU,iBAAiB;GACpC,iBAAiB,UAAU;EAC7B,GAAG,GAAG;CACR,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,aAAa;GACX,IAAI,WAAW,SAAS,aAAa,WAAW,OAAO;EACzD;CACF,GAAG,CAAC,CAAC;CAEL,OAAO;EACL;EACA;EACA,kBAAkB;GAChB,eAAe;GACf,aAAa;GACb,SAAS;EACX;CACF;AACF"}
@@ -0,0 +1,5 @@
1
+ import { VirtualListProps } from "./type.mjs";
2
+ import "./VirtualScrollArea.mjs";
3
+ import "./MenuVirtualList.mjs";
4
+ import "./useMenuVirtualList.mjs";
5
+ export type { VirtualListProps };
@@ -0,0 +1,26 @@
1
+ import { ReactElement } from "react";
2
+ //#region src/base-ui/virtual/type.d.ts
3
+ interface VirtualListProps {
4
+ /**
5
+ * Typeahead label for a child row. Defaults to the row's `label` prop, then `aria-label`,
6
+ * then the flattened text of its children.
7
+ */
8
+ getItemLabel?: (child: ReactElement, index: number) => string | undefined;
9
+ /**
10
+ * Extra child indices to keep mounted while virtualized, on top of the highlighted row.
11
+ */
12
+ keepMounted?: readonly number[];
13
+ /**
14
+ * Row height hint for the virtualizer; rows are still measured after mount.
15
+ */
16
+ listItemHeight?: number;
17
+ /**
18
+ * Only mount the rows near the scroll position. Use for long flat lists (hundreds of rows);
19
+ * groups and submenus keep working but are kept mounted as one row.
20
+ * @default false
21
+ */
22
+ virtual?: boolean;
23
+ }
24
+ //#endregion
25
+ export { VirtualListProps };
26
+ //# sourceMappingURL=type.d.mts.map
@@ -0,0 +1,3 @@
1
+ import "./type.mjs";
2
+ import React from "react";
3
+ import "virtua";
@@ -0,0 +1,256 @@
1
+ "use client";
2
+ import { usePointerScrollGuard } from "./VirtualScrollArea.mjs";
3
+ import { Children, cloneElement, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ //#region src/base-ui/virtual/useMenuVirtualList.tsx
5
+ const LIST_ITEM_SELECTOR = "[role^=\"menuitem\"], [role=\"option\"]";
6
+ const INDEX_ATTRIBUTE = "data-virtual-index";
7
+ const MAX_JUMP_FRAMES = 60;
8
+ const flattenText = (node) => {
9
+ if (node == null || typeof node === "boolean") return "";
10
+ if (typeof node === "string" || typeof node === "number") return String(node);
11
+ if (Array.isArray(node)) return node.map(flattenText).join("");
12
+ if (isValidElement(node)) return flattenText(node.props.children);
13
+ return "";
14
+ };
15
+ const readEntry = (child, index, getItemLabel) => {
16
+ if (!isValidElement(child)) return {
17
+ disabled: true,
18
+ label: ""
19
+ };
20
+ const props = child.props;
21
+ const label = getItemLabel?.(child, index) ?? props.label ?? props["aria-label"] ?? flattenText(props.children);
22
+ return {
23
+ disabled: Boolean(props.disabled) || props["aria-disabled"] === true || props["aria-disabled"] === "true",
24
+ label: label.trim()
25
+ };
26
+ };
27
+ const findMatch = (entries, query, startIndex) => {
28
+ if (entries.length === 0) return -1;
29
+ const lowerQuery = query.toLowerCase();
30
+ const start = (startIndex % entries.length + entries.length) % entries.length;
31
+ for (let offset = 0; offset < entries.length; offset += 1) {
32
+ const index = (start + offset) % entries.length;
33
+ const entry = entries[index];
34
+ if (!entry.disabled && entry.label.toLowerCase().startsWith(lowerQuery)) return index;
35
+ }
36
+ return -1;
37
+ };
38
+ const findEdge = (entries, fromEnd) => {
39
+ if (fromEnd) {
40
+ for (let index = entries.length - 1; index >= 0; index -= 1) if (!entries[index].disabled) return index;
41
+ return -1;
42
+ }
43
+ return entries.findIndex((entry) => !entry.disabled);
44
+ };
45
+ function useMenuVirtualList({ children, enabled, getItemLabel, keepMounted, typeaheadResetMs = 500 }) {
46
+ const viewportRef = useRef(null);
47
+ const virtualizerRef = useRef(null);
48
+ const focusedItemRef = useRef(null);
49
+ const activeIndexRef = useRef(null);
50
+ const [activeIndex, setActiveIndex] = useState(null);
51
+ const jumpTargetRef = useRef(null);
52
+ const [jumpTarget, setJumpTarget] = useState(null);
53
+ const { pointerScrollRef, scrollGuardProps } = usePointerScrollGuard();
54
+ const entries = useMemo(() => enabled ? Children.map(children, (child, index) => readEntry(child, index, getItemLabel)) ?? [] : [], [
55
+ children,
56
+ enabled,
57
+ getItemLabel
58
+ ]);
59
+ const guardScrollIntoView = useCallback((item) => {
60
+ if (Object.hasOwn(item, "scrollIntoView")) return;
61
+ item.scrollIntoView = (...args) => {
62
+ if (!pointerScrollRef.current) HTMLElement.prototype.scrollIntoView.call(item, ...args);
63
+ };
64
+ }, [pointerScrollRef]);
65
+ const parkFocus = useCallback(() => {
66
+ const viewport = viewportRef.current;
67
+ if (viewport && !viewport.contains(document.activeElement)) viewport.focus({ preventScroll: true });
68
+ }, []);
69
+ const settleScrollRef = useRef(-1);
70
+ const focusJumpTarget = useCallback(() => {
71
+ const index = jumpTargetRef.current;
72
+ const viewport = viewportRef.current;
73
+ if (index === null || !viewport) return false;
74
+ parkFocus();
75
+ const scrollTop = viewport.scrollTop;
76
+ const settled = scrollTop === settleScrollRef.current;
77
+ settleScrollRef.current = scrollTop;
78
+ if (!settled) return false;
79
+ const child = viewport.querySelector(`[${INDEX_ATTRIBUTE}="${index}"]`);
80
+ const target = child && (child.matches(LIST_ITEM_SELECTOR) ? child : child.querySelector(LIST_ITEM_SELECTOR));
81
+ if (!target) return false;
82
+ guardScrollIntoView(target);
83
+ focusedItemRef.current = target;
84
+ target.focus({ preventScroll: true });
85
+ return true;
86
+ }, [guardScrollIntoView, parkFocus]);
87
+ useEffect(() => {
88
+ const viewport = viewportRef.current;
89
+ if (!enabled || !viewport) return;
90
+ const observer = new MutationObserver(() => {
91
+ if (jumpTargetRef.current !== null) {
92
+ focusJumpTarget();
93
+ return;
94
+ }
95
+ const focusedItem = focusedItemRef.current;
96
+ const active = document.activeElement;
97
+ if (focusedItem && !focusedItem.isConnected && active === document.body) {
98
+ focusedItemRef.current = null;
99
+ viewport.focus({ preventScroll: true });
100
+ return;
101
+ }
102
+ if (focusedItem && active === focusedItem) focusedItem.dispatchEvent(new FocusEvent("focusin", { bubbles: true }));
103
+ });
104
+ observer.observe(viewport, {
105
+ childList: true,
106
+ subtree: true
107
+ });
108
+ return () => observer.disconnect();
109
+ }, [enabled, focusJumpTarget]);
110
+ const jumpFrameRef = useRef(null);
111
+ useEffect(() => {
112
+ return () => {
113
+ if (jumpFrameRef.current !== null) cancelAnimationFrame(jumpFrameRef.current);
114
+ };
115
+ }, []);
116
+ const jumpTo = useCallback((index) => {
117
+ activeIndexRef.current = index;
118
+ jumpTargetRef.current = index;
119
+ setActiveIndex(index);
120
+ setJumpTarget(index);
121
+ viewportRef.current?.focus({ preventScroll: true });
122
+ settleScrollRef.current = -1;
123
+ virtualizerRef.current?.scrollToIndex(index, { align: "nearest" });
124
+ if (jumpFrameRef.current !== null) cancelAnimationFrame(jumpFrameRef.current);
125
+ let frames = 0;
126
+ const kick = () => {
127
+ jumpFrameRef.current = null;
128
+ focusJumpTarget();
129
+ frames += 1;
130
+ if (jumpTargetRef.current !== null && frames < MAX_JUMP_FRAMES) jumpFrameRef.current = requestAnimationFrame(kick);
131
+ else if (jumpTargetRef.current !== null) {
132
+ jumpTargetRef.current = null;
133
+ setJumpTarget(null);
134
+ }
135
+ };
136
+ jumpFrameRef.current = requestAnimationFrame(kick);
137
+ }, [focusJumpTarget]);
138
+ const queryRef = useRef("");
139
+ const lastMatchRef = useRef(null);
140
+ const resetTimeoutRef = useRef(null);
141
+ const resetQuery = useCallback(() => {
142
+ queryRef.current = "";
143
+ if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);
144
+ resetTimeoutRef.current = null;
145
+ }, []);
146
+ useEffect(() => {
147
+ return () => {
148
+ if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);
149
+ };
150
+ }, []);
151
+ const handleKeyDownCapture = useCallback((event) => {
152
+ if (!enabled || entries.length === 0) return;
153
+ if (event.target instanceof HTMLElement && event.target.matches("input, textarea")) return;
154
+ if (event.key === "Home" || event.key === "End") {
155
+ const fromEnd = event.key === "End";
156
+ const index = findEdge(entries, fromEnd);
157
+ if (index === -1) return;
158
+ event.preventDefault();
159
+ event.stopPropagation();
160
+ resetQuery();
161
+ jumpTo(index);
162
+ return;
163
+ }
164
+ if (event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey) return;
165
+ if (event.key === " " && queryRef.current === "") return;
166
+ event.preventDefault();
167
+ event.stopPropagation();
168
+ const isNewSession = queryRef.current === "";
169
+ if (entries.every((entry) => entry.disabled || entry.label[0]?.toLowerCase() !== entry.label[1]?.toLowerCase()) && queryRef.current === event.key) queryRef.current = "";
170
+ queryRef.current += event.key;
171
+ if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);
172
+ resetTimeoutRef.current = setTimeout(resetQuery, typeaheadResetMs);
173
+ const previous = isNewSession || queryRef.current.length === 1 ? activeIndexRef.current ?? lastMatchRef.current ?? -1 : lastMatchRef.current ?? activeIndexRef.current ?? -1;
174
+ const startIndex = queryRef.current.length === 1 ? previous + 1 : previous;
175
+ const index = findMatch(entries, queryRef.current, startIndex);
176
+ if (index !== -1) {
177
+ lastMatchRef.current = index;
178
+ jumpTo(index);
179
+ } else if (event.key !== " ") resetQuery();
180
+ }, [
181
+ enabled,
182
+ entries,
183
+ jumpTo,
184
+ resetQuery,
185
+ typeaheadResetMs
186
+ ]);
187
+ const virtualChildren = useMemo(() => {
188
+ if (!enabled) return children;
189
+ return Children.map(children, (child, index) => {
190
+ if (!isValidElement(child)) return child;
191
+ const { onFocus } = child.props;
192
+ return cloneElement(child, {
193
+ [INDEX_ATTRIBUTE]: index,
194
+ onFocus: (event) => {
195
+ onFocus?.(event);
196
+ const item = event.target.closest(LIST_ITEM_SELECTOR);
197
+ if (item && event.currentTarget.contains(item)) {
198
+ focusedItemRef.current = item;
199
+ guardScrollIntoView(item);
200
+ }
201
+ activeIndexRef.current = index;
202
+ setActiveIndex(index);
203
+ if (jumpTargetRef.current === index) {
204
+ jumpTargetRef.current = null;
205
+ setJumpTarget(null);
206
+ }
207
+ }
208
+ });
209
+ });
210
+ }, [
211
+ children,
212
+ enabled,
213
+ guardScrollIntoView
214
+ ]);
215
+ const keepMountedIndices = useMemo(() => {
216
+ if (!enabled) return void 0;
217
+ const count = Children.count(children);
218
+ const indices = new Set(keepMounted);
219
+ if (activeIndex !== null) indices.add(activeIndex);
220
+ if (jumpTarget !== null) indices.add(jumpTarget);
221
+ const inRange = [...indices].filter((index) => index >= 0 && index < count);
222
+ return inRange.length ? inRange : void 0;
223
+ }, [
224
+ activeIndex,
225
+ children,
226
+ enabled,
227
+ jumpTarget,
228
+ keepMounted
229
+ ]);
230
+ const handleBlur = useCallback((event) => {
231
+ if (jumpTargetRef.current === null) return;
232
+ const viewport = viewportRef.current;
233
+ if (!viewport) return;
234
+ if (event.relatedTarget && viewport.contains(event.relatedTarget)) return;
235
+ viewport.focus({ preventScroll: true });
236
+ }, []);
237
+ return {
238
+ keepMountedIndices,
239
+ viewportProps: useMemo(() => ({
240
+ ...scrollGuardProps,
241
+ onBlur: handleBlur,
242
+ onKeyDownCapture: handleKeyDownCapture
243
+ }), [
244
+ handleBlur,
245
+ handleKeyDownCapture,
246
+ scrollGuardProps
247
+ ]),
248
+ viewportRef,
249
+ virtualChildren,
250
+ virtualizerRef
251
+ };
252
+ }
253
+ //#endregion
254
+ export { findEdge, findMatch, flattenText, useMenuVirtualList };
255
+
256
+ //# sourceMappingURL=useMenuVirtualList.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMenuVirtualList.mjs","names":[],"sources":["../../../src/base-ui/virtual/useMenuVirtualList.tsx"],"sourcesContent":["'use client';\n\nimport type React from 'react';\nimport {\n Children,\n cloneElement,\n isValidElement,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { VirtualizerHandle } from 'virtua';\n\nimport type { VirtualListProps } from './type';\nimport { usePointerScrollGuard } from './VirtualScrollArea';\n\nconst LIST_ITEM_SELECTOR = '[role^=\"menuitem\"], [role=\"option\"]';\nconst INDEX_ATTRIBUTE = 'data-virtual-index';\nconst MAX_JUMP_FRAMES = 60;\n\ninterface VirtualChildProps {\n 'aria-disabled'?: boolean | 'true' | 'false';\n 'aria-label'?: string;\n 'children'?: React.ReactNode;\n 'disabled'?: boolean;\n 'label'?: string;\n 'onFocus'?: (event: React.FocusEvent<HTMLElement>) => void;\n}\n\nexport interface ListEntry {\n disabled: boolean;\n label: string;\n}\n\nexport interface UseMenuVirtualListParams extends Pick<\n VirtualListProps,\n 'getItemLabel' | 'keepMounted'\n> {\n children: React.ReactNode;\n enabled: boolean | undefined;\n /**\n * Idle time after which the typeahead query resets; matches Base UI's per-component value.\n * @default 500\n */\n typeaheadResetMs?: number;\n}\n\nexport const flattenText = (node: React.ReactNode): string => {\n if (node == null || typeof node === 'boolean') return '';\n if (typeof node === 'string' || typeof node === 'number') return String(node);\n if (Array.isArray(node)) return node.map(flattenText).join('');\n if (isValidElement<{ children?: React.ReactNode }>(node)) return flattenText(node.props.children);\n return '';\n};\n\nconst readEntry = (\n child: React.ReactNode,\n index: number,\n getItemLabel: UseMenuVirtualListParams['getItemLabel'],\n): ListEntry => {\n if (!isValidElement<VirtualChildProps>(child)) return { disabled: true, label: '' };\n const props = child.props;\n const label =\n getItemLabel?.(child, index) ??\n props.label ??\n props['aria-label'] ??\n flattenText(props.children);\n const disabled =\n Boolean(props.disabled) || props['aria-disabled'] === true || props['aria-disabled'] === 'true';\n return { disabled, label: label.trim() };\n};\n\nexport const findMatch = (entries: ListEntry[], query: string, startIndex: number) => {\n if (entries.length === 0) return -1;\n const lowerQuery = query.toLowerCase();\n const start = ((startIndex % entries.length) + entries.length) % entries.length;\n for (let offset = 0; offset < entries.length; offset += 1) {\n const index = (start + offset) % entries.length;\n const entry = entries[index];\n if (!entry.disabled && entry.label.toLowerCase().startsWith(lowerQuery)) return index;\n }\n return -1;\n};\n\nexport const findEdge = (entries: ListEntry[], fromEnd: boolean) => {\n if (fromEnd) {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n if (!entries[index].disabled) return index;\n }\n return -1;\n }\n return entries.findIndex((entry) => !entry.disabled);\n};\n\nexport function useMenuVirtualList({\n children,\n enabled,\n getItemLabel,\n keepMounted,\n typeaheadResetMs = 500,\n}: UseMenuVirtualListParams) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const virtualizerRef = useRef<VirtualizerHandle | null>(null);\n const focusedItemRef = useRef<HTMLElement | null>(null);\n const activeIndexRef = useRef<number | null>(null);\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n // A Home/End/typeahead target stays mounted until focus actually lands on it, so the\n // focusin resync (which re-sets the active index to the still-focused old row) can't unmount\n // it mid-scroll on a large jump.\n const jumpTargetRef = useRef<number | null>(null);\n const [jumpTarget, setJumpTarget] = useState<number | null>(null);\n const { pointerScrollRef, scrollGuardProps } = usePointerScrollGuard();\n\n const entries = useMemo(\n () =>\n enabled\n ? (Children.map(children, (child, index) => readEntry(child, index, getItemLabel)) ?? [])\n : [],\n [children, enabled, getItemLabel],\n );\n\n // Base UI scrolls the focused item into view right after focusing it; while the pointer is\n // scrolling that would yank the list back, so the item's scrollIntoView checks the guard.\n const guardScrollIntoView = useCallback(\n (item: HTMLElement) => {\n if (Object.hasOwn(item, 'scrollIntoView')) return;\n item.scrollIntoView = (...args) => {\n if (!pointerScrollRef.current) HTMLElement.prototype.scrollIntoView.call(item, ...args);\n };\n },\n [pointerScrollRef],\n );\n\n // Keeps focus inside the popup on the viewport, so a row unmounting under the old focus never\n // drops focus to the body mid-jump — Base UI Menu closes on focus-out.\n const parkFocus = useCallback(() => {\n const viewport = viewportRef.current;\n if (viewport && !viewport.contains(document.activeElement)) {\n viewport.focus({ preventScroll: true });\n }\n }, []);\n\n const settleScrollRef = useRef(-1);\n // Focus the jump target only once the scroll has settled on it (its row is mounted and the\n // offset stopped changing), so we never focus a row that's about to unmount and orphan focus.\n const focusJumpTarget = useCallback(() => {\n const index = jumpTargetRef.current;\n const viewport = viewportRef.current;\n if (index === null || !viewport) return false;\n parkFocus();\n const scrollTop = viewport.scrollTop;\n const settled = scrollTop === settleScrollRef.current;\n settleScrollRef.current = scrollTop;\n if (!settled) return false;\n const child = viewport.querySelector<HTMLElement>(`[${INDEX_ATTRIBUTE}=\"${index}\"]`);\n const target =\n child &&\n (child.matches(LIST_ITEM_SELECTOR)\n ? child\n : child.querySelector<HTMLElement>(LIST_ITEM_SELECTOR));\n if (!target) return false;\n guardScrollIntoView(target);\n focusedItemRef.current = target;\n target.focus({ preventScroll: true });\n return true;\n }, [guardScrollIntoView, parkFocus]);\n\n // The virtualizer shifts its window in response to keyboard nav and jumps, so react to every\n // DOM change: finish a pending jump the moment its target mounts, otherwise keep Base UI's\n // highlight index in step by re-firing focusin on the still-focused row (or, if it was\n // unmounted, park focus on the viewport so the popup keydown handlers stay reachable).\n useEffect(() => {\n const viewport = viewportRef.current;\n if (!enabled || !viewport) return;\n const observer = new MutationObserver(() => {\n if (jumpTargetRef.current !== null) {\n focusJumpTarget();\n return;\n }\n const focusedItem = focusedItemRef.current;\n const active = document.activeElement;\n if (focusedItem && !focusedItem.isConnected && active === document.body) {\n focusedItemRef.current = null;\n viewport.focus({ preventScroll: true });\n return;\n }\n if (focusedItem && active === focusedItem) {\n focusedItem.dispatchEvent(new FocusEvent('focusin', { bubbles: true }));\n }\n });\n observer.observe(viewport, { childList: true, subtree: true });\n return () => observer.disconnect();\n }, [enabled, focusJumpTarget]);\n\n const jumpFrameRef = useRef<number | null>(null);\n useEffect(() => {\n return () => {\n if (jumpFrameRef.current !== null) cancelAnimationFrame(jumpFrameRef.current);\n };\n }, []);\n\n const jumpTo = useCallback(\n (index: number) => {\n activeIndexRef.current = index;\n jumpTargetRef.current = index;\n setActiveIndex(index);\n setJumpTarget(index);\n // Park focus on the viewport (inside the popup) before scrolling, so the row being\n // unmounted underneath the old focus never drops focus to the body — which would trip\n // Base UI Menu's focus-out close mid-jump.\n viewportRef.current?.focus({ preventScroll: true });\n settleScrollRef.current = -1;\n virtualizerRef.current?.scrollToIndex(index, { align: 'nearest' });\n // The MutationObserver finishes the jump on mount; kick it in case the target is already\n // mounted (a short jump) and produces no mutation.\n if (jumpFrameRef.current !== null) cancelAnimationFrame(jumpFrameRef.current);\n let frames = 0;\n const kick = () => {\n jumpFrameRef.current = null;\n focusJumpTarget();\n frames += 1;\n // Loop until the real focus event lands (which clears `jumpTargetRef` in onFocus), so a\n // mid-scroll remount that orphans focus is recovered on the next frame; cap so a target\n // that never mounts can't spin forever.\n if (jumpTargetRef.current !== null && frames < MAX_JUMP_FRAMES) {\n jumpFrameRef.current = requestAnimationFrame(kick);\n } else if (jumpTargetRef.current !== null) {\n jumpTargetRef.current = null;\n setJumpTarget(null);\n }\n };\n jumpFrameRef.current = requestAnimationFrame(kick);\n },\n [focusJumpTarget],\n );\n\n const queryRef = useRef('');\n const lastMatchRef = useRef<number | null>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const resetQuery = useCallback(() => {\n queryRef.current = '';\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = null;\n }, []);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n // Runs in the capture phase so Base UI's own Home/End and typeahead, which only see the\n // mounted rows, never get the event. Mirrors `useTypeahead`: printable keys accumulate for\n // `typeaheadResetMs`, a lone space activates while a space mid-query keeps typing, rapid\n // repeats of one letter cycle through matches, and the search starts after the focused row\n // and wraps.\n const handleKeyDownCapture = useCallback(\n (event: React.KeyboardEvent<HTMLElement>) => {\n if (!enabled || entries.length === 0) return;\n if (event.target instanceof HTMLElement && event.target.matches('input, textarea')) return;\n\n if (event.key === 'Home' || event.key === 'End') {\n const fromEnd = event.key === 'End';\n const index = findEdge(entries, fromEnd);\n if (index === -1) return;\n event.preventDefault();\n event.stopPropagation();\n resetQuery();\n jumpTo(index);\n return;\n }\n\n if (event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey) return;\n if (event.key === ' ' && queryRef.current === '') return;\n event.preventDefault();\n event.stopPropagation();\n\n const isNewSession = queryRef.current === '';\n const allowRapidRepeat = entries.every(\n (entry) =>\n entry.disabled || entry.label[0]?.toLowerCase() !== entry.label[1]?.toLowerCase(),\n );\n if (allowRapidRepeat && queryRef.current === event.key) {\n queryRef.current = '';\n }\n queryRef.current += event.key;\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(resetQuery, typeaheadResetMs);\n\n const previous =\n isNewSession || queryRef.current.length === 1\n ? (activeIndexRef.current ?? lastMatchRef.current ?? -1)\n : (lastMatchRef.current ?? activeIndexRef.current ?? -1);\n const startIndex = queryRef.current.length === 1 ? previous + 1 : previous;\n const index = findMatch(entries, queryRef.current, startIndex);\n if (index !== -1) {\n lastMatchRef.current = index;\n jumpTo(index);\n } else if (event.key !== ' ') {\n resetQuery();\n }\n },\n [enabled, entries, jumpTo, resetQuery, typeaheadResetMs],\n );\n\n const virtualChildren = useMemo(() => {\n if (!enabled) return children;\n return Children.map(children, (child, index) => {\n if (!isValidElement<VirtualChildProps>(child)) return child;\n const { onFocus } = child.props;\n const extraProps: Record<string, unknown> = {\n [INDEX_ATTRIBUTE]: index,\n onFocus: (event: React.FocusEvent<HTMLElement>) => {\n onFocus?.(event);\n const item = (event.target as HTMLElement).closest<HTMLElement>(LIST_ITEM_SELECTOR);\n if (item && event.currentTarget.contains(item)) {\n focusedItemRef.current = item;\n guardScrollIntoView(item);\n }\n activeIndexRef.current = index;\n setActiveIndex(index);\n if (jumpTargetRef.current === index) {\n jumpTargetRef.current = null;\n setJumpTarget(null);\n }\n },\n };\n return cloneElement(child, extraProps);\n });\n }, [children, enabled, guardScrollIntoView]);\n\n const keepMountedIndices = useMemo(() => {\n if (!enabled) return undefined;\n const count = Children.count(children);\n const indices = new Set(keepMounted);\n if (activeIndex !== null) indices.add(activeIndex);\n if (jumpTarget !== null) indices.add(jumpTarget);\n const inRange = [...indices].filter((index) => index >= 0 && index < count);\n return inRange.length ? inRange : undefined;\n }, [activeIndex, children, enabled, jumpTarget, keepMounted]);\n\n // While a jump is in flight, a row unmounting under focus fires blur → focus to the body,\n // which trips Base UI Menu's focus-out close. Catch it and pull focus back to the viewport\n // (still inside the popup) synchronously before the close can run.\n const handleBlur = useCallback((event: React.FocusEvent<HTMLElement>) => {\n if (jumpTargetRef.current === null) return;\n const viewport = viewportRef.current;\n if (!viewport) return;\n if (event.relatedTarget && viewport.contains(event.relatedTarget)) return;\n viewport.focus({ preventScroll: true });\n }, []);\n\n const viewportProps = useMemo(\n () => ({ ...scrollGuardProps, onBlur: handleBlur, onKeyDownCapture: handleKeyDownCapture }),\n [handleBlur, handleKeyDownCapture, scrollGuardProps],\n );\n\n return { keepMountedIndices, viewportProps, viewportRef, virtualChildren, virtualizerRef };\n}\n"],"mappings":"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"}
package/es/index.d.mts CHANGED
@@ -30,6 +30,7 @@ import "./Tooltip/index.mjs";
30
30
  import { ActionIconProps, ActionIconSize } from "./ActionIcon/type.mjs";
31
31
  import ActionIcon from "./ActionIcon/ActionIcon.mjs";
32
32
  import "./ActionIcon/index.mjs";
33
+ import { ScrollAreaContent, ScrollAreaContentProps, ScrollAreaCorner, ScrollAreaCornerProps, ScrollAreaRoot, ScrollAreaRootProps, ScrollAreaScrollbar, ScrollAreaScrollbarProps, ScrollAreaThumb, ScrollAreaThumbProps, ScrollAreaViewport, ScrollAreaViewportProps } from "./base-ui/ScrollArea/atoms.mjs";
33
34
  import { MenuCheckboxItemType } from "./Menu/checkboxItem.mjs";
34
35
  import { GenericItemType, ItemType, MenuInfo, MenuItemType, MenuProps } from "./Menu/type.mjs";
35
36
  import { BaseMenuItemType } from "./Menu/baseItem.mjs";
@@ -243,7 +244,6 @@ import "./NeuralNetworkLoading/index.mjs";
243
244
  import { I18nProvider, I18nProviderProps } from "./i18n/context.mjs";
244
245
  import { useTranslation } from "./i18n/useTranslation.mjs";
245
246
  import "./Provider.mjs";
246
- import { ScrollAreaContent, ScrollAreaContentProps, ScrollAreaCorner, ScrollAreaCornerProps, ScrollAreaRoot, ScrollAreaRootProps, ScrollAreaScrollbar, ScrollAreaScrollbarProps, ScrollAreaThumb, ScrollAreaThumbProps, ScrollAreaViewport, ScrollAreaViewportProps } from "./base-ui/ScrollArea/atoms.mjs";
247
247
  import { ScrollAreaProps } from "./base-ui/ScrollArea/type.mjs";
248
248
  import { ScrollArea } from "./base-ui/ScrollArea/ScrollArea.mjs";
249
249
  import "./ScrollArea/index.mjs";
package/es/index.mjs CHANGED
@@ -30,6 +30,7 @@ import Text from "./Text/Text.mjs";
30
30
  import { preventDefault, preventDefaultAndStopPropagation, stopPropagation } from "./utils/dom.mjs";
31
31
  import AccordionItem from "./Accordion/AccordionItem.mjs";
32
32
  import ActionIcon from "./ActionIcon/ActionIcon.mjs";
33
+ import { ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "./base-ui/ScrollArea/atoms.mjs";
33
34
  import { styles } from "./base-ui/DropdownMenu/sharedStyle.mjs";
34
35
  import { DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuFooter, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuHeader, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemExtra, DropdownMenuItemIcon, DropdownMenuItemLabel, DropdownMenuPopup, DropdownMenuPortal, DropdownMenuPositioner, DropdownMenuRoot, DropdownMenuScrollViewport, DropdownMenuSeparator, DropdownMenuSubmenuArrow, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuTrigger } from "./base-ui/DropdownMenu/atoms.mjs";
35
36
  import Menu from "./Menu/Menu.mjs";
@@ -144,7 +145,6 @@ import Typography from "./Markdown/Typography.mjs";
144
145
  import Markdown from "./Markdown/Markdown.mjs";
145
146
  import { remarkColor } from "./Markdown/plugins/remarkColor.mjs";
146
147
  import MaskShadow from "./MaskShadow/MaskShadow.mjs";
147
- import { ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "./base-ui/ScrollArea/atoms.mjs";
148
148
  import { ScrollArea } from "./base-ui/ScrollArea/ScrollArea.mjs";
149
149
  import SearchBar from "./SearchBar/SearchBar.mjs";
150
150
  import Select from "./Select/Select.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/ui",
3
- "version": "5.43.1",
3
+ "version": "5.44.0",
4
4
  "description": "Lobe UI is an open-source UI component library for building AIGC web apps",
5
5
  "keywords": [
6
6
  "lobehub",