@elliemae/ds-form-layout-autocomplete 3.80.0-next.13 → 3.80.0-next.18

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.
@@ -33,17 +33,27 @@ __export(useVirtualization_exports, {
33
33
  module.exports = __toCommonJS(useVirtualization_exports);
34
34
  var React = __toESM(require("react"));
35
35
  var import_react = __toESM(require("react"));
36
- var import_react_virtual = require("react-virtual");
36
+ var import_react_virtual = require("@tanstack/react-virtual");
37
+ const OPTION_ROW_HEIGHT_PX = 32;
37
38
  const useVirtualization = ({
38
39
  references,
39
40
  options
40
41
  }) => {
41
42
  const { listRef } = references;
42
- const virtualListHelpers = (0, import_react_virtual.useVirtual)({
43
- size: options.length,
44
- parentRef: listRef,
43
+ const estimateSize = (0, import_react.useCallback)(
44
+ (index) => {
45
+ const option = options[index];
46
+ if (option.type === "section") return OPTION_ROW_HEIGHT_PX * (1 + option.options.length);
47
+ return OPTION_ROW_HEIGHT_PX;
48
+ },
49
+ [options]
50
+ );
51
+ const virtualListHelpers = (0, import_react_virtual.useVirtualizer)({
52
+ count: options.length,
53
+ getScrollElement: () => listRef.current,
45
54
  overscan: 15,
46
- paddingStart: 0
55
+ paddingStart: 0,
56
+ estimateSize
47
57
  });
48
58
  const scrollOptionIntoView = (0, import_react.useCallback)(
49
59
  (dsId, opts = { align: "center" }) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/config/useVirtualization.ts", "../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import React, { useCallback } from 'react';\nimport { useVirtual, type ScrollToIndexOptions } from 'react-virtual';\n\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\nimport type { useRefs } from './useRefs.js';\n\nexport const useVirtualization = ({\n references,\n options,\n}: {\n references: ReturnType<typeof useRefs>;\n options: DSAutocompleteT.InternalProps['options'];\n}): {\n virtualListHelpers: ReturnType<typeof useVirtual>;\n scrollOptionIntoView: (dsId: string, opts?: ScrollToIndexOptions) => void;\n} => {\n const { listRef } = references;\n // ===========================================================================\n // Virtualization setup\n // ===========================================================================\n const virtualListHelpers: ReturnType<typeof useVirtual> = useVirtual({\n size: options.length,\n parentRef: listRef,\n overscan: 15,\n paddingStart: 0,\n });\n\n // ===========================================================================\n // Scroll into view function\n // ===========================================================================\n const scrollOptionIntoView = useCallback(\n (dsId: string, opts: ScrollToIndexOptions = { align: 'center' }) => {\n virtualListHelpers.scrollToIndex(\n options.findIndex((opt) => opt.dsId === dsId),\n opts,\n );\n },\n [options, virtualListHelpers],\n );\n\n return React.useMemo(\n () => ({\n virtualListHelpers,\n scrollOptionIntoView,\n }),\n [virtualListHelpers, scrollOptionIntoView],\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAAmC;AACnC,2BAAsD;AAK/C,MAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AACF,MAMK;AACH,QAAM,EAAE,QAAQ,IAAI;AAIpB,QAAM,yBAAoD,iCAAW;AAAA,IACnE,MAAM,QAAQ;AAAA,IACd,WAAW;AAAA,IACX,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AAKD,QAAM,2BAAuB;AAAA,IAC3B,CAAC,MAAc,OAA6B,EAAE,OAAO,SAAS,MAAM;AAClE,yBAAmB;AAAA,QACjB,QAAQ,UAAU,CAAC,QAAQ,IAAI,SAAS,IAAI;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,SAAS,kBAAkB;AAAA,EAC9B;AAEA,SAAO,aAAAA,QAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,oBAAoB,oBAAoB;AAAA,EAC3C;AACF;",
4
+ "sourcesContent": ["import React, { useCallback } from 'react';\nimport { useVirtualizer, type ScrollToOptions } from '@tanstack/react-virtual';\n\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\nimport type { useRefs } from './useRefs.js';\n\n// Mirrors ds-menu-items-commons's MENU_ITEM_ROW_HEIGHT (32px) \u2014 not importable, that constant is\n// private to that package (same duplication already tracked for Section.tsx's StyledSectionWrapper\n// min-height in KNOWN_INTENTIONAL_DEVIATIONS.md). A \"section\" virtual item renders its own header\n// plus all of its sub-options inline (see Section.tsx), so its estimate must account for that.\nconst OPTION_ROW_HEIGHT_PX = 32;\n\nexport const useVirtualization = ({\n references,\n options,\n}: {\n references: ReturnType<typeof useRefs>;\n options: DSAutocompleteT.InternalProps['options'];\n}): {\n // TItemElement is HTMLElement, not HTMLDivElement: rendered items are a mix of <li> roots\n // (StyledMenuItemAction/DSMenuItemsAction, an HTMLLIElement) and <section> roots (Section.tsx's\n // StyledSectionWrapper) \u2014 HTMLElement is their common base.\n virtualListHelpers: ReturnType<typeof useVirtualizer<HTMLDivElement, HTMLElement>>;\n scrollOptionIntoView: (dsId: string, opts?: ScrollToOptions) => void;\n} => {\n const { listRef } = references;\n // ===========================================================================\n // Virtualization setup\n // ===========================================================================\n const estimateSize = useCallback(\n (index: number) => {\n const option = options[index];\n // v3 has no internal default for estimateSize (v2 silently defaulted to 50) \u2014 omitting it\n // silently zeroes out getVirtualItems()/getTotalSize() with real data present, no thrown error.\n if (option.type === 'section') return OPTION_ROW_HEIGHT_PX * (1 + option.options.length);\n return OPTION_ROW_HEIGHT_PX;\n },\n [options],\n );\n\n const virtualListHelpers = useVirtualizer<HTMLDivElement, HTMLElement>({\n count: options.length,\n getScrollElement: () => listRef.current,\n overscan: 15,\n paddingStart: 0,\n estimateSize,\n });\n\n // ===========================================================================\n // Scroll into view function\n // ===========================================================================\n const scrollOptionIntoView = useCallback(\n (dsId: string, opts: ScrollToOptions = { align: 'center' }) => {\n virtualListHelpers.scrollToIndex(\n options.findIndex((opt) => opt.dsId === dsId),\n opts,\n );\n },\n [options, virtualListHelpers],\n );\n\n return React.useMemo(\n () => ({\n virtualListHelpers,\n scrollOptionIntoView,\n }),\n [virtualListHelpers, scrollOptionIntoView],\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAAmC;AACnC,2BAAqD;AASrD,MAAM,uBAAuB;AAEtB,MAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AACF,MASK;AACH,QAAM,EAAE,QAAQ,IAAI;AAIpB,QAAM,mBAAe;AAAA,IACnB,CAAC,UAAkB;AACjB,YAAM,SAAS,QAAQ,KAAK;AAG5B,UAAI,OAAO,SAAS,UAAW,QAAO,wBAAwB,IAAI,OAAO,QAAQ;AACjF,aAAO;AAAA,IACT;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,yBAAqB,qCAA4C;AAAA,IACrE,OAAO,QAAQ;AAAA,IACf,kBAAkB,MAAM,QAAQ;AAAA,IAChC,UAAU;AAAA,IACV,cAAc;AAAA,IACd;AAAA,EACF,CAAC;AAKD,QAAM,2BAAuB;AAAA,IAC3B,CAAC,MAAc,OAAwB,EAAE,OAAO,SAAS,MAAM;AAC7D,yBAAmB;AAAA,QACjB,QAAQ,UAAU,CAAC,QAAQ,IAAI,SAAS,IAAI;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,SAAS,kBAAkB;AAAA,EAC9B;AAEA,SAAO,aAAAA,QAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,oBAAoB,oBAAoB;AAAA,EAC3C;AACF;",
6
6
  "names": ["React"]
7
7
  }
@@ -51,24 +51,35 @@ const StyledSectionLabel = (0, import_ds_system.styled)(import_ds_typography.DST
51
51
  `;
52
52
  const Section = import_react.default.memo((props) => {
53
53
  const { label, wrapperStyles, focusOptionIdx, innerRef, options, handleClick } = props;
54
+ const dataIndex = props["data-index"];
54
55
  const id = `${label.replace(/ /g, "")}-section`;
55
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StyledSectionWrapper, { role: "group", "aria-labelledby": id, style: wrapperStyles, innerRef, children: [
56
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledSectionLabel, { variant: "b3-strong", component: "header", id, role: "presentation", children: label }),
57
- options?.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
58
- import_ds_menu_items.DSMenuItemsAction,
59
- {
60
- as: "div",
61
- dsId: option.dsId,
62
- id: option.dsId,
63
- label: option.label,
64
- "data-testid": import_AutocompleteDataTestids.AutocompleteDataTestid.OPTION,
65
- disabled: option.disabled,
66
- onClick: (e) => handleClick(option, e),
67
- isActive: option.dsId === focusOptionIdx,
68
- "aria-selected": "false"
69
- },
70
- option.dsId
71
- ))
72
- ] });
56
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
57
+ StyledSectionWrapper,
58
+ {
59
+ role: "group",
60
+ "aria-labelledby": id,
61
+ style: wrapperStyles,
62
+ innerRef,
63
+ "data-index": dataIndex,
64
+ children: [
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledSectionLabel, { variant: "b3-strong", component: "header", id, role: "presentation", children: label }),
66
+ options?.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
67
+ import_ds_menu_items.DSMenuItemsAction,
68
+ {
69
+ as: "div",
70
+ dsId: option.dsId,
71
+ id: option.dsId,
72
+ label: option.label,
73
+ "data-testid": import_AutocompleteDataTestids.AutocompleteDataTestid.OPTION,
74
+ disabled: option.disabled,
75
+ onClick: (e) => handleClick(option, e),
76
+ isActive: option.dsId === focusOptionIdx,
77
+ "aria-selected": "false"
78
+ },
79
+ option.dsId
80
+ ))
81
+ ]
82
+ }
83
+ );
73
84
  });
74
85
  //# sourceMappingURL=Section.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/parts/Section.tsx", "../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import React from 'react';\nimport { styled } from '@elliemae/ds-system';\nimport { DSTypography } from '@elliemae/ds-typography';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { AutocompleteDataTestid } from '../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\n\ninterface SectionProps {\n label: string;\n wrapperStyles: React.CSSProperties;\n innerRef: (instance: HTMLDivElement | null) => void;\n options: DSAutocompleteT.ItemOption[];\n handleClick: (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => void;\n focusOptionIdx: string;\n}\n\nconst StyledSectionWrapper = styled('section')`\n list-style: none;\n min-height: 32px;\n display: grid;\n align-items: center;\n`;\n\n/**\n * Group-label styling aligned with its siblings (PUI-18609 / PUI-15487): b3-strong typography,\n * 32px row (8px+8px padding + b3-strong line-height, mirrored by the wrapper min-height above).\n *\n * This is one of four independent reimplementations of the section/group-label row\n * (ds-menu-items-commons, ds-form-combobox, ds-menu-button being the others). The duplication \u2014\n * and the fact that these styled calls remain slot-less here \u2014 is tracked in\n * KNOWN_INTENTIONAL_DEVIATIONS.md.\n */\nconst StyledSectionLabel = styled(DSTypography)`\n display: flex;\n align-items: center;\n padding: 8px 16px;\n`;\n\nexport const Section = React.memo((props: SectionProps) => {\n const { label, wrapperStyles, focusOptionIdx, innerRef, options, handleClick } = props;\n const id = `${label.replace(/ /g, '')}-section`;\n return (\n <StyledSectionWrapper role=\"group\" aria-labelledby={id} style={wrapperStyles} innerRef={innerRef}>\n <StyledSectionLabel variant=\"b3-strong\" component=\"header\" id={id} role=\"presentation\">\n {label}\n </StyledSectionLabel>\n {options?.map((option) => (\n <DSMenuItemsAction\n as=\"div\"\n key={option.dsId}\n dsId={option.dsId}\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n />\n ))}\n </StyledSectionWrapper>\n );\n});\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD0CnB;AA1CJ,mBAAkB;AAClB,uBAAuB;AACvB,2BAA6B;AAC7B,2BAAkC;AAClC,qCAAuC;AAYvC,MAAM,2BAAuB,yBAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAgB7C,MAAM,yBAAqB,yBAAO,iCAAY;AAAA;AAAA;AAAA;AAAA;AAMvC,MAAM,UAAU,aAAAA,QAAM,KAAK,CAAC,UAAwB;AACzD,QAAM,EAAE,OAAO,eAAe,gBAAgB,UAAU,SAAS,YAAY,IAAI;AACjF,QAAM,KAAK,GAAG,MAAM,QAAQ,MAAM,EAAE,CAAC;AACrC,SACE,6CAAC,wBAAqB,MAAK,SAAQ,mBAAiB,IAAI,OAAO,eAAe,UAC5E;AAAA,gDAAC,sBAAmB,SAAQ,aAAY,WAAU,UAAS,IAAQ,MAAK,gBACrE,iBACH;AAAA,IACC,SAAS,IAAI,CAAC,WACb;AAAA,MAAC;AAAA;AAAA,QACC,IAAG;AAAA,QAEH,MAAM,OAAO;AAAA,QACb,IAAI,OAAO;AAAA,QACX,OAAO,OAAO;AAAA,QACd,eAAa,sDAAuB;AAAA,QACpC,UAAU,OAAO;AAAA,QACjB,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,QACzE,UAAU,OAAO,SAAS;AAAA,QAC1B,iBAAc;AAAA;AAAA,MART,OAAO;AAAA,IASd,CACD;AAAA,KACH;AAEJ,CAAC;",
4
+ "sourcesContent": ["import React from 'react';\nimport { styled } from '@elliemae/ds-system';\nimport { DSTypography } from '@elliemae/ds-typography';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { AutocompleteDataTestid } from '../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\n\ninterface SectionProps {\n label: string;\n wrapperStyles: React.CSSProperties;\n // Root is StyledSectionWrapper (styled('section')) \u2014 HTMLElement, not HTMLDivElement.\n innerRef: (instance: HTMLElement | null) => void;\n 'data-index': number;\n options: DSAutocompleteT.ItemOption[];\n handleClick: (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => void;\n focusOptionIdx: string;\n}\n\nconst StyledSectionWrapper = styled('section')`\n list-style: none;\n min-height: 32px;\n display: grid;\n align-items: center;\n`;\n\n/**\n * Group-label styling aligned with its siblings (PUI-18609 / PUI-15487): b3-strong typography,\n * 32px row (8px+8px padding + b3-strong line-height, mirrored by the wrapper min-height above).\n *\n * This is one of four independent reimplementations of the section/group-label row\n * (ds-menu-items-commons, ds-form-combobox, ds-menu-button being the others). The duplication \u2014\n * and the fact that these styled calls remain slot-less here \u2014 is tracked in\n * KNOWN_INTENTIONAL_DEVIATIONS.md.\n */\nconst StyledSectionLabel = styled(DSTypography)`\n display: flex;\n align-items: center;\n padding: 8px 16px;\n`;\n\nexport const Section = React.memo((props: SectionProps) => {\n const { label, wrapperStyles, focusOptionIdx, innerRef, options, handleClick } = props;\n const dataIndex = props['data-index'];\n const id = `${label.replace(/ /g, '')}-section`;\n return (\n <StyledSectionWrapper\n role=\"group\"\n aria-labelledby={id}\n style={wrapperStyles}\n innerRef={innerRef}\n data-index={dataIndex}\n >\n <StyledSectionLabel variant=\"b3-strong\" component=\"header\" id={id} role=\"presentation\">\n {label}\n </StyledSectionLabel>\n {options?.map((option) => (\n <DSMenuItemsAction\n as=\"div\"\n key={option.dsId}\n dsId={option.dsId}\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n />\n ))}\n </StyledSectionWrapper>\n );\n});\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD6CnB;AA7CJ,mBAAkB;AAClB,uBAAuB;AACvB,2BAA6B;AAC7B,2BAAkC;AAClC,qCAAuC;AAcvC,MAAM,2BAAuB,yBAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAgB7C,MAAM,yBAAqB,yBAAO,iCAAY;AAAA;AAAA;AAAA;AAAA;AAMvC,MAAM,UAAU,aAAAA,QAAM,KAAK,CAAC,UAAwB;AACzD,QAAM,EAAE,OAAO,eAAe,gBAAgB,UAAU,SAAS,YAAY,IAAI;AACjF,QAAM,YAAY,MAAM,YAAY;AACpC,QAAM,KAAK,GAAG,MAAM,QAAQ,MAAM,EAAE,CAAC;AACrC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,mBAAiB;AAAA,MACjB,OAAO;AAAA,MACP;AAAA,MACA,cAAY;AAAA,MAEZ;AAAA,oDAAC,sBAAmB,SAAQ,aAAY,WAAU,UAAS,IAAQ,MAAK,gBACrE,iBACH;AAAA,QACC,SAAS,IAAI,CAAC,WACb;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YAEH,MAAM,OAAO;AAAA,YACb,IAAI,OAAO;AAAA,YACX,OAAO,OAAO;AAAA,YACd,eAAa,sDAAuB;AAAA,YACpC,UAAU,OAAO;AAAA,YACjB,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,YACzE,UAAU,OAAO,SAAS;AAAA,YAC1B,iBAAc;AAAA;AAAA,UART,OAAO;AAAA,QASd,CACD;AAAA;AAAA;AAAA,EACH;AAEJ,CAAC;",
6
6
  "names": ["React"]
7
7
  }
@@ -49,21 +49,22 @@ const MenuList = () => {
49
49
  listRef,
50
50
  autoCompleteId,
51
51
  getOwnerProps,
52
- virtualListHelpers: { totalSize, measure }
52
+ virtualListHelpers
53
53
  } = (0, import_react.useContext)(import_AutocompleteCTX.default);
54
+ const { measure } = virtualListHelpers;
55
+ const totalSize = virtualListHelpers.getTotalSize();
54
56
  const ItemRenderer = (0, import_useItemRenderer.useItemRenderer)();
55
57
  const { width } = (0, import_useOnElementResize.useOnElementResize)(referenceElement);
58
+ (0, import_react.useEffect)(() => {
59
+ if (!measure) return;
60
+ measure();
61
+ }, [width, measure]);
56
62
  const handleOnFocus = (0, import_react.useCallback)(() => {
57
63
  inputRef.current?.focus();
58
64
  }, [inputRef]);
59
65
  const preventMouseDown = (0, import_react.useCallback)((e) => {
60
66
  e.preventDefault();
61
67
  }, []);
62
- (0, import_react.useEffect)(() => {
63
- if (!measure) return void 0;
64
- const rafId = requestAnimationFrame(() => measure());
65
- return () => cancelAnimationFrame(rafId);
66
- }, [width]);
67
68
  const renderMenuContent = () => {
68
69
  if (isSkeleton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_SkeletonContainer.SkeletonContainer, { autoCompleteId, getOwnerProps });
69
70
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_LoadingContainer.LoadingContainer, { autoCompleteId, getOwnerProps });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/parts/menu-list/MenuList.tsx", "../../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import React, { useContext, useCallback, useEffect } from 'react';\nimport { StyledListWrapper, StyledList, StyledVirtualListWrapper } from './styled.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { useItemRenderer } from './useItemRenderer.js';\nimport { useOnElementResize } from '../../utils/useOnElementResize.js';\nimport { LoadingContainer } from './LoadingContainer.js';\nimport { SkeletonContainer } from './SkeletonContainer.js';\n\nexport const MenuList = (): JSX.Element => {\n const {\n props: { options, isLoading, isSkeleton },\n referenceElement,\n inputRef,\n listRef,\n autoCompleteId,\n getOwnerProps,\n virtualListHelpers: { totalSize, measure },\n } = useContext(AutocompleteContext);\n\n const ItemRenderer = useItemRenderer();\n const { width } = useOnElementResize(referenceElement);\n\n const handleOnFocus = useCallback(() => {\n inputRef.current?.focus();\n }, [inputRef]);\n\n const preventMouseDown: React.MouseEventHandler<HTMLDivElement> = useCallback((e) => {\n e.preventDefault();\n }, []);\n\n useEffect(() => {\n if (!measure) return undefined;\n // Defer measure() to the next animation frame so it never runs inside an active\n // React sync flush (React 17 / legacy mode). Without the deferral, the call chain\n // outputsize \u2192 setWidth \u2192 [flushSyncCallbackQueue] \u2192 commit \u2192 measureRef \u2192\n // setMeasuredCache \u2192 [new syncQueue entry] \u2192 commit \u2192 measureRef \u2192 \u2026\n // keeps adding entries to the live syncQueue array that flushSyncCallbackQueue\n // is iterating over, producing 25+ nested commits and the\n // \"Maximum update depth exceeded\" crash. requestAnimationFrame breaks that chain\n // by scheduling measure() for after the current flush completes.\n const rafId = requestAnimationFrame(() => measure());\n return () => cancelAnimationFrame(rafId);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [width]);\n\n const renderMenuContent = () => {\n // Loading/skeleton own the listbox id + role + aria-busy while options are being fetched;\n // only one of skeleton / loading / list is ever mounted at a time (see the containers).\n if (isSkeleton) return <SkeletonContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (isLoading) return <LoadingContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (options.length === 0) return null;\n return (\n <StyledVirtualListWrapper tabIndex={-1} innerRef={listRef}>\n <StyledList\n // recalculate the key to force the re-render of the list and update virtualization\n key={options.length}\n role=\"listbox\"\n id={`menu-list-${autoCompleteId}`}\n // default aria label until we integrate the aria props on autocomplete\n // this is for the axe core to pass\n aria-label=\"autocomplete option lists\"\n onFocus={handleOnFocus}\n data-testid={AutocompleteDataTestid.LIST}\n style={{ height: totalSize, margin: '8px 0px' }}\n getOwnerProps={getOwnerProps}\n >\n {ItemRenderer}\n </StyledList>\n </StyledVirtualListWrapper>\n );\n };\n\n return (\n <StyledListWrapper onMouseDown={preventMouseDown} minWidth={referenceElement?.offsetWidth}>\n {renderMenuContent()}\n </StyledListWrapper>\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADiDI;AAjD3B,mBAA0D;AAC1D,oBAAwE;AACxE,qCAAuC;AACvC,6BAAgC;AAChC,6BAAgC;AAChC,gCAAmC;AACnC,8BAAiC;AACjC,+BAAkC;AAE3B,MAAM,WAAW,MAAmB;AACzC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS,WAAW,WAAW;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB,EAAE,WAAW,QAAQ;AAAA,EAC3C,QAAI,yBAAW,uBAAAA,OAAmB;AAElC,QAAM,mBAAe,wCAAgB;AACrC,QAAM,EAAE,MAAM,QAAI,8CAAmB,gBAAgB;AAErD,QAAM,oBAAgB,0BAAY,MAAM;AACtC,aAAS,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,uBAA4D,0BAAY,CAAC,MAAM;AACnF,MAAE,eAAe;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,8BAAU,MAAM;AACd,QAAI,CAAC,QAAS,QAAO;AASrB,UAAM,QAAQ,sBAAsB,MAAM,QAAQ,CAAC;AACnD,WAAO,MAAM,qBAAqB,KAAK;AAAA,EAEzC,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,oBAAoB,MAAM;AAG9B,QAAI,WAAY,QAAO,4CAAC,8CAAkB,gBAAgC,eAA8B;AACxG,QAAI,UAAW,QAAO,4CAAC,4CAAiB,gBAAgC,eAA8B;AACtG,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,WACE,4CAAC,0CAAyB,UAAU,IAAI,UAAU,SAChD;AAAA,MAAC;AAAA;AAAA,QAGC,MAAK;AAAA,QACL,IAAI,aAAa,cAAc;AAAA,QAG/B,cAAW;AAAA,QACX,SAAS;AAAA,QACT,eAAa,sDAAuB;AAAA,QACpC,OAAO,EAAE,QAAQ,WAAW,QAAQ,UAAU;AAAA,QAC9C;AAAA,QAEC;AAAA;AAAA,MAXI,QAAQ;AAAA,IAYf,GACF;AAAA,EAEJ;AAEA,SACE,4CAAC,mCAAkB,aAAa,kBAAkB,UAAU,kBAAkB,aAC3E,4BAAkB,GACrB;AAEJ;",
4
+ "sourcesContent": ["import React, { useContext, useCallback, useEffect } from 'react';\nimport { StyledListWrapper, StyledList, StyledVirtualListWrapper } from './styled.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { useItemRenderer } from './useItemRenderer.js';\nimport { useOnElementResize } from '../../utils/useOnElementResize.js';\nimport { LoadingContainer } from './LoadingContainer.js';\nimport { SkeletonContainer } from './SkeletonContainer.js';\n\nexport const MenuList = (): JSX.Element => {\n const {\n props: { options, isLoading, isSkeleton },\n referenceElement,\n inputRef,\n listRef,\n autoCompleteId,\n getOwnerProps,\n virtualListHelpers,\n } = useContext(AutocompleteContext);\n const { measure } = virtualListHelpers;\n const totalSize = virtualListHelpers.getTotalSize();\n\n const ItemRenderer = useItemRenderer();\n const { width } = useOnElementResize(referenceElement);\n\n useEffect(() => {\n if (!measure) return;\n // v2 deferred this call to the next animation frame to avoid a setMeasuredCache\n // re-entrancy bug during a React legacy-mode sync flush (measureRef \u2192 setMeasuredCache \u2192\n // new commit \u2192 measureRef \u2192 ... \u2192 \"Maximum update depth exceeded\"). @tanstack/react-virtual\n // v3 has no such state setter in its measurement path \u2014 per-item sizing is driven by\n // ResizeObserver instead \u2014 so the bug this deferral guarded against no longer applies.\n // Verified empirically (PUI-19077): removing the deferral does not reproduce the crash,\n // including under a hardened stress test (24 oscillating widths, 16ms apart, 30 repeated\n // headless Playwright CT runs).\n //\n // NOTE: a follow-up \"callback coordination\" refactor (calling measure() directly from\n // useOnElementResize's ResizeObserver callback instead of via this width-effect) was tried\n // and reverted \u2014 it broke DSAutocomplete.variableHeightLayout.test.playwright.jsx\n // deterministically (15/15 runs, same 3.6875px gap). Calling measure() from inside the native\n // ResizeObserver callback fires earlier/differently relative to @tanstack/react-virtual's own\n // per-item measureElement ResizeObserver than calling it from a React effect reacting to\n // width state does. Routing through width state is load-bearing here \u2014 do not \"simplify\" it\n // away again without re-running the variable-height layout test.\n measure();\n }, [width, measure]);\n\n const handleOnFocus = useCallback(() => {\n inputRef.current?.focus();\n }, [inputRef]);\n\n const preventMouseDown: React.MouseEventHandler<HTMLDivElement> = useCallback((e) => {\n e.preventDefault();\n }, []);\n\n const renderMenuContent = () => {\n // Loading/skeleton own the listbox id + role + aria-busy while options are being fetched;\n // only one of skeleton / loading / list is ever mounted at a time (see the containers).\n if (isSkeleton) return <SkeletonContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (isLoading) return <LoadingContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (options.length === 0) return null;\n return (\n <StyledVirtualListWrapper tabIndex={-1} innerRef={listRef}>\n <StyledList\n // recalculate the key to force the re-render of the list and update virtualization\n key={options.length}\n role=\"listbox\"\n id={`menu-list-${autoCompleteId}`}\n // default aria label until we integrate the aria props on autocomplete\n // this is for the axe core to pass\n aria-label=\"autocomplete option lists\"\n onFocus={handleOnFocus}\n data-testid={AutocompleteDataTestid.LIST}\n style={{ height: totalSize, margin: '8px 0px' }}\n getOwnerProps={getOwnerProps}\n >\n {ItemRenderer}\n </StyledList>\n </StyledVirtualListWrapper>\n );\n };\n\n return (\n <StyledListWrapper onMouseDown={preventMouseDown} minWidth={referenceElement?.offsetWidth}>\n {renderMenuContent()}\n </StyledListWrapper>\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD0DI;AA1D3B,mBAA0D;AAC1D,oBAAwE;AACxE,qCAAuC;AACvC,6BAAgC;AAChC,6BAAgC;AAChC,gCAAmC;AACnC,8BAAiC;AACjC,+BAAkC;AAE3B,MAAM,WAAW,MAAmB;AACzC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS,WAAW,WAAW;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,QAAI,yBAAW,uBAAAA,OAAmB;AAClC,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,YAAY,mBAAmB,aAAa;AAElD,QAAM,mBAAe,wCAAgB;AACrC,QAAM,EAAE,MAAM,QAAI,8CAAmB,gBAAgB;AAErD,8BAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAkBd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,OAAO,CAAC;AAEnB,QAAM,oBAAgB,0BAAY,MAAM;AACtC,aAAS,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,uBAA4D,0BAAY,CAAC,MAAM;AACnF,MAAE,eAAe;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoB,MAAM;AAG9B,QAAI,WAAY,QAAO,4CAAC,8CAAkB,gBAAgC,eAA8B;AACxG,QAAI,UAAW,QAAO,4CAAC,4CAAiB,gBAAgC,eAA8B;AACtG,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,WACE,4CAAC,0CAAyB,UAAU,IAAI,UAAU,SAChD;AAAA,MAAC;AAAA;AAAA,QAGC,MAAK;AAAA,QACL,IAAI,aAAa,cAAc;AAAA,QAG/B,cAAW;AAAA,QACX,SAAS;AAAA,QACT,eAAa,sDAAuB;AAAA,QACpC,OAAO,EAAE,QAAQ,WAAW,QAAQ,UAAU;AAAA,QAC9C;AAAA,QAEC;AAAA;AAAA,MAXI,QAAQ;AAAA,IAYf,GACF;AAAA,EAEJ;AAEA,SACE,4CAAC,mCAAkB,aAAa,kBAAkB,UAAU,kBAAkB,aAC3E,4BAAkB,GACrB;AAEJ;",
6
6
  "names": ["AutocompleteContext"]
7
7
  }
@@ -47,7 +47,6 @@ const StyledMenuItemAction = (0, import_ds_system.styled)(import_ds_menu_items.D
47
47
  const useItemRenderer = () => {
48
48
  const {
49
49
  props: { onSelect },
50
- inputRef,
51
50
  setUserTypedSomething,
52
51
  setUserJustSelectedAnOption,
53
52
  focusOptionIdx,
@@ -65,51 +64,52 @@ const useItemRenderer = () => {
65
64
  },
66
65
  [onSelect, setUserTypedSomething, setUserJustSelectedAnOption]
67
66
  );
67
+ const virtualItems = virtualListHelpers?.getVirtualItems();
68
+ const { measureElement } = virtualListHelpers ?? {};
68
69
  return (0, import_react.useMemo)(() => {
69
- if (virtualListHelpers) {
70
- return virtualListHelpers.virtualItems.map((vItem) => {
71
- const option = options[vItem.index];
72
- const generalProps = {
73
- wrapperStyles: { position: "absolute", top: vItem.start, left: 0, width: "100%" },
74
- key: `${option.dsId}`,
75
- innerRef: vItem.measureRef,
76
- dsId: option.dsId
77
- };
78
- const getOwnerPropsArguments = () => option;
79
- if (option.type === "section") {
80
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
81
- import_Section.Section,
82
- {
83
- label: option.label,
84
- ...generalProps,
85
- options: option.options,
86
- handleClick,
87
- focusOptionIdx
88
- }
89
- );
90
- }
91
- if (option.type === "option") {
92
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
93
- StyledMenuItemAction,
94
- {
95
- id: option.dsId,
96
- label: option.label,
97
- "data-testid": import_AutocompleteDataTestids.AutocompleteDataTestid.OPTION,
98
- disabled: option.disabled,
99
- tabIndex: 0,
100
- onClick: (e) => handleClick(option, e),
101
- isActive: option.dsId === focusOptionIdx,
102
- "aria-selected": "false",
103
- ...generalProps,
104
- getOwnerProps,
105
- getOwnerPropsArguments
106
- }
107
- );
108
- }
109
- return null;
110
- });
111
- }
112
- return null;
113
- }, [options, focusOptionIdx, inputRef, virtualListHelpers]);
70
+ if (!virtualItems) return null;
71
+ return virtualItems.map((vItem) => {
72
+ const option = options[vItem.index];
73
+ const generalProps = {
74
+ wrapperStyles: { position: "absolute", top: vItem.start, left: 0, width: "100%" },
75
+ key: `${option.dsId}`,
76
+ innerRef: measureElement,
77
+ "data-index": vItem.index,
78
+ dsId: option.dsId
79
+ };
80
+ const getOwnerPropsArguments = () => option;
81
+ if (option.type === "section") {
82
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
83
+ import_Section.Section,
84
+ {
85
+ label: option.label,
86
+ ...generalProps,
87
+ options: option.options,
88
+ handleClick,
89
+ focusOptionIdx
90
+ }
91
+ );
92
+ }
93
+ if (option.type === "option") {
94
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
95
+ StyledMenuItemAction,
96
+ {
97
+ id: option.dsId,
98
+ label: option.label,
99
+ "data-testid": import_AutocompleteDataTestids.AutocompleteDataTestid.OPTION,
100
+ disabled: option.disabled,
101
+ tabIndex: 0,
102
+ onClick: (e) => handleClick(option, e),
103
+ isActive: option.dsId === focusOptionIdx,
104
+ "aria-selected": "false",
105
+ ...generalProps,
106
+ getOwnerProps,
107
+ getOwnerPropsArguments
108
+ }
109
+ );
110
+ }
111
+ return null;
112
+ });
113
+ }, [virtualItems, options, focusOptionIdx, measureElement, handleClick, getOwnerProps]);
114
114
  };
115
115
  //# sourceMappingURL=useItemRenderer.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/parts/menu-list/useItemRenderer.tsx", "../../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable react-hooks/exhaustive-deps */\nimport React, { useMemo, useContext, useCallback } from 'react';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { styled } from '@elliemae/ds-system';\nimport { DSAutocompleteName, DSAutocompleteSlots } from '../../constants/index.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../../react-desc-prop-types.js';\nimport { Section } from '../Section.js';\n\nconst StyledMenuItemAction = styled(DSMenuItemsAction, {\n name: DSAutocompleteName,\n slot: DSAutocompleteSlots.OPTION,\n})``;\n\nexport const useItemRenderer = () => {\n const {\n props: { onSelect },\n inputRef,\n setUserTypedSomething,\n setUserJustSelectedAnOption,\n focusOptionIdx,\n optionsPerSection: options,\n virtualListHelpers,\n getOwnerProps,\n } = useContext(AutocompleteContext);\n\n const handleClick = useCallback(\n (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => {\n onSelect(option.label, e);\n setUserJustSelectedAnOption(true);\n setUserTypedSomething(false);\n e.stopPropagation();\n e.preventDefault();\n },\n [onSelect, setUserTypedSomething, setUserJustSelectedAnOption],\n );\n\n return useMemo(() => {\n if (virtualListHelpers) {\n return virtualListHelpers.virtualItems.map((vItem) => {\n const option = options[vItem.index];\n const generalProps = {\n wrapperStyles: { position: 'absolute', top: vItem.start, left: 0, width: '100%' } as React.CSSProperties,\n key: `${option.dsId}`,\n innerRef: vItem.measureRef,\n dsId: option.dsId,\n };\n const getOwnerPropsArguments = () => option;\n\n if (option.type === 'section') {\n return (\n <Section\n label={option.label}\n {...generalProps}\n options={option.options}\n handleClick={handleClick}\n focusOptionIdx={focusOptionIdx}\n />\n );\n }\n if (option.type === 'option') {\n return (\n <StyledMenuItemAction\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n tabIndex={0}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n {...generalProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n );\n }\n return null;\n });\n }\n return null;\n }, [options, focusOptionIdx, inputRef, virtualListHelpers]);\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADqDX;AAnDZ,mBAAwD;AACxD,2BAAkC;AAClC,uBAAuB;AACvB,uBAAwD;AACxD,6BAAgC;AAChC,qCAAuC;AAEvC,qBAAwB;AAExB,MAAM,2BAAuB,yBAAO,wCAAmB;AAAA,EACrD,MAAM;AAAA,EACN,MAAM,qCAAoB;AAC5B,CAAC;AAEM,MAAM,kBAAkB,MAAM;AACnC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,EACF,QAAI,yBAAW,uBAAAA,OAAmB;AAElC,QAAM,kBAAc;AAAA,IAClB,CAAC,QAAoC,MAA0C;AAC7E,eAAS,OAAO,OAAO,CAAC;AACxB,kCAA4B,IAAI;AAChC,4BAAsB,KAAK;AAC3B,QAAE,gBAAgB;AAClB,QAAE,eAAe;AAAA,IACnB;AAAA,IACA,CAAC,UAAU,uBAAuB,2BAA2B;AAAA,EAC/D;AAEA,aAAO,sBAAQ,MAAM;AACnB,QAAI,oBAAoB;AACtB,aAAO,mBAAmB,aAAa,IAAI,CAAC,UAAU;AACpD,cAAM,SAAS,QAAQ,MAAM,KAAK;AAClC,cAAM,eAAe;AAAA,UACnB,eAAe,EAAE,UAAU,YAAY,KAAK,MAAM,OAAO,MAAM,GAAG,OAAO,OAAO;AAAA,UAChF,KAAK,GAAG,OAAO,IAAI;AAAA,UACnB,UAAU,MAAM;AAAA,UAChB,MAAM,OAAO;AAAA,QACf;AACA,cAAM,yBAAyB,MAAM;AAErC,YAAI,OAAO,SAAS,WAAW;AAC7B,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,OAAO;AAAA,cACb,GAAG;AAAA,cACJ,SAAS,OAAO;AAAA,cAChB;AAAA,cACA;AAAA;AAAA,UACF;AAAA,QAEJ;AACA,YAAI,OAAO,SAAS,UAAU;AAC5B,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,IAAI,OAAO;AAAA,cACX,OAAO,OAAO;AAAA,cACd,eAAa,sDAAuB;AAAA,cACpC,UAAU,OAAO;AAAA,cACjB,UAAU;AAAA,cACV,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,cACzE,UAAU,OAAO,SAAS;AAAA,cAC1B,iBAAc;AAAA,cACb,GAAG;AAAA,cACJ;AAAA,cACA;AAAA;AAAA,UACF;AAAA,QAEJ;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,gBAAgB,UAAU,kBAAkB,CAAC;AAC5D;",
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable react-hooks/exhaustive-deps */\nimport React, { useMemo, useContext, useCallback } from 'react';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { styled } from '@elliemae/ds-system';\nimport { DSAutocompleteName, DSAutocompleteSlots } from '../../constants/index.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../../react-desc-prop-types.js';\nimport { Section } from '../Section.js';\n\nconst StyledMenuItemAction = styled(DSMenuItemsAction, {\n name: DSAutocompleteName,\n slot: DSAutocompleteSlots.OPTION,\n})``;\n\nexport const useItemRenderer = () => {\n const {\n props: { onSelect },\n setUserTypedSomething,\n setUserJustSelectedAnOption,\n focusOptionIdx,\n optionsPerSection: options,\n virtualListHelpers,\n getOwnerProps,\n } = useContext(AutocompleteContext);\n\n const handleClick = useCallback(\n (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => {\n onSelect(option.label, e);\n setUserJustSelectedAnOption(true);\n setUserTypedSomething(false);\n e.stopPropagation();\n e.preventDefault();\n },\n [onSelect, setUserTypedSomething, setUserJustSelectedAnOption],\n );\n\n // v3's useVirtualizer returns the SAME Virtualizer class instance across renders (mutated in\n // place), unlike v2's useVirtual which returned a fresh object with a plain `virtualItems` array\n // property every render. Memoizing on `virtualListHelpers` itself (the stable instance) instead\n // of on the actual getVirtualItems() result means this would never recompute past the first\n // render \u2014 getVirtualItems() has its own internal memoization and returns a fresh array\n // reference only when the range/measurements genuinely change, so that's the correct dependency.\n const virtualItems = virtualListHelpers?.getVirtualItems();\n const { measureElement } = virtualListHelpers ?? {};\n\n return useMemo(() => {\n if (!virtualItems) return null;\n return virtualItems.map((vItem) => {\n const option = options[vItem.index];\n const generalProps = {\n wrapperStyles: { position: 'absolute', top: vItem.start, left: 0, width: '100%' } as React.CSSProperties,\n key: `${option.dsId}`,\n innerRef: measureElement,\n 'data-index': vItem.index,\n dsId: option.dsId,\n };\n const getOwnerPropsArguments = () => option;\n\n if (option.type === 'section') {\n return (\n <Section\n label={option.label}\n {...generalProps}\n options={option.options}\n handleClick={handleClick}\n focusOptionIdx={focusOptionIdx}\n />\n );\n }\n if (option.type === 'option') {\n return (\n <StyledMenuItemAction\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n tabIndex={0}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n {...generalProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n );\n }\n return null;\n });\n }, [virtualItems, options, focusOptionIdx, measureElement, handleClick, getOwnerProps]);\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD8Db;AA5DV,mBAAwD;AACxD,2BAAkC;AAClC,uBAAuB;AACvB,uBAAwD;AACxD,6BAAgC;AAChC,qCAAuC;AAEvC,qBAAwB;AAExB,MAAM,2BAAuB,yBAAO,wCAAmB;AAAA,EACrD,MAAM;AAAA,EACN,MAAM,qCAAoB;AAC5B,CAAC;AAEM,MAAM,kBAAkB,MAAM;AACnC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,EACF,QAAI,yBAAW,uBAAAA,OAAmB;AAElC,QAAM,kBAAc;AAAA,IAClB,CAAC,QAAoC,MAA0C;AAC7E,eAAS,OAAO,OAAO,CAAC;AACxB,kCAA4B,IAAI;AAChC,4BAAsB,KAAK;AAC3B,QAAE,gBAAgB;AAClB,QAAE,eAAe;AAAA,IACnB;AAAA,IACA,CAAC,UAAU,uBAAuB,2BAA2B;AAAA,EAC/D;AAQA,QAAM,eAAe,oBAAoB,gBAAgB;AACzD,QAAM,EAAE,eAAe,IAAI,sBAAsB,CAAC;AAElD,aAAO,sBAAQ,MAAM;AACnB,QAAI,CAAC,aAAc,QAAO;AAC1B,WAAO,aAAa,IAAI,CAAC,UAAU;AACjC,YAAM,SAAS,QAAQ,MAAM,KAAK;AAClC,YAAM,eAAe;AAAA,QACnB,eAAe,EAAE,UAAU,YAAY,KAAK,MAAM,OAAO,MAAM,GAAG,OAAO,OAAO;AAAA,QAChF,KAAK,GAAG,OAAO,IAAI;AAAA,QACnB,UAAU;AAAA,QACV,cAAc,MAAM;AAAA,QACpB,MAAM,OAAO;AAAA,MACf;AACA,YAAM,yBAAyB,MAAM;AAErC,UAAI,OAAO,SAAS,WAAW;AAC7B,eACE;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,OAAO;AAAA,YACb,GAAG;AAAA,YACJ,SAAS,OAAO;AAAA,YAChB;AAAA,YACA;AAAA;AAAA,QACF;AAAA,MAEJ;AACA,UAAI,OAAO,SAAS,UAAU;AAC5B,eACE;AAAA,UAAC;AAAA;AAAA,YACC,IAAI,OAAO;AAAA,YACX,OAAO,OAAO;AAAA,YACd,eAAa,sDAAuB;AAAA,YACpC,UAAU,OAAO;AAAA,YACjB,UAAU;AAAA,YACV,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,YACzE,UAAU,OAAO,SAAS;AAAA,YAC1B,iBAAc;AAAA,YACb,GAAG;AAAA,YACJ;AAAA,YACA;AAAA;AAAA,QACF;AAAA,MAEJ;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,cAAc,SAAS,gBAAgB,gBAAgB,aAAa,aAAa,CAAC;AACxF;",
6
6
  "names": ["AutocompleteContext"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/react-desc-prop-types.ts", "../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable no-unused-vars */\nimport type React from 'react';\nimport type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport type { DSHookFloatingContextT } from '@elliemae/ds-floating-context';\n\nimport { globalAttributesPropTypes, PropTypes, xstyledPropTypes } from '@elliemae/ds-props-helpers';\nimport type { DSInputTextT } from '@elliemae/ds-form-input-text';\n\nexport declare namespace DSAutocompleteT {\n export interface CommonItemOptions {\n dsId: string;\n disabled?: boolean;\n render?: React.ComponentType<unknown>;\n }\n\n export interface ItemSeparatorOptions extends CommonItemOptions {\n type: 'separator';\n }\n // this was never implemented?\n // packages/forms/ds-form-layout-autocomplete/src/parts/menu-list/useItemRenderer.tsx\n // only have code for ItemOption and ItemSectionOptions...\n // export interface ItemSeparatorOptions extends CommonItemOptions {\n // type: 'separator';\n // }\n\n export interface ItemSectionOptions extends CommonItemOptions {\n type: 'section';\n label: string;\n options: ItemOption[];\n }\n\n export interface ItemOption extends CommonItemOptions {\n value: string;\n label: string;\n type: 'option';\n }\n\n export type OptionTypes = ItemOption | ItemSectionOptions;\n\n export type SelectedOptionsT = ItemOption[] | ItemOption | null;\n\n export interface RequiredProps {\n options: OptionTypes[];\n filter: string;\n onSelect: (\n suggestedValue: string,\n e: React.KeyboardEvent<HTMLInputElement> | React.MouseEvent<HTMLInputElement>,\n ) => void;\n }\n export interface DefaultProps {\n startPlacementPreference: string;\n placementOrderPreference: DSHookFloatingContextT.PopperPlacementsT[];\n withoutPortal: boolean;\n zIndex: number;\n forceFocusFirstOptionOnType: boolean;\n }\n\n export interface OptionalProps {\n children?: React.ReactNode;\n inputProps?: DSInputTextT.Props;\n onValueChange?: (nextValue: string | number, e: React.ChangeEvent<HTMLInputElement>) => void;\n renderInput?: (props: React.InputHTMLAttributes<HTMLInputElement>) => React.ReactNode;\n isLoading?: boolean;\n isSkeleton?: boolean;\n }\n\n export interface Props\n extends Partial<DefaultProps>,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n\n export interface InternalProps\n extends DefaultProps,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n}\n\nexport const defaultProps: DSAutocompleteT.DefaultProps = {\n zIndex: 10,\n withoutPortal: false,\n forceFocusFirstOptionOnType: false,\n startPlacementPreference: 'bottom-start',\n placementOrderPreference: ['bottom-start', 'top-start'],\n};\n\nexport const DSAutocompletePropTypes: DSPropTypesSchema<DSAutocompleteT.Props> = {\n ...globalAttributesPropTypes,\n ...xstyledPropTypes,\n options: PropTypes.oneOfType([PropTypes.array, PropTypes.object])\n .isRequired.description('List of options')\n .defaultValue([]),\n filter: PropTypes.string.isRequired.description('String value to filter the options').defaultValue(''),\n onSelect: PropTypes.func.isRequired\n .description('function triggered when an option is selected it will send the options selected')\n .defaultValue(''),\n children: PropTypes.node\n .description('React component to apply autocomplete functionality')\n .defaultValue('')\n .deprecated({ version: '3.x' }),\n inputProps: PropTypes.object.description('Props to pass to the input component').defaultValue({}),\n onValueChange: PropTypes.func.description('Function triggered when the value of the input changes').defaultValue(''),\n renderInput: PropTypes.func.description('Function to render the input').defaultValue(''),\n isLoading: PropTypes.bool\n .description('Shows a loading spinner in the menu while options are being fetched.')\n .defaultValue('false'),\n isSkeleton: PropTypes.bool.description('Displays the menu in skeleton loading mode.').defaultValue('false'),\n startPlacementPreference: PropTypes.string\n .description('Ideal preference for context placement')\n .defaultValue('bottom-start'),\n placementOrderPreference: PropTypes.arrayOf(PropTypes.string)\n .description('Placement preferences for the context menu in order of most preferred to least preferred')\n .defaultValue(\"['bottom-start', 'bottom-end']\"),\n withoutPortal: PropTypes.bool\n .description('Whether to render the context menu in a react portal or next to the input DOM element')\n .defaultValue('false (next to the input)'),\n forceFocusFirstOptionOnType: PropTypes.bool\n .description(\n 'Whether to focus the first option when typing (NOT RECOMMENDED AND BREAKING A11Y STANDARDS + ALL USERS USABILITY)',\n )\n .defaultValue('false'),\n};\n\nexport const DSAutocompletePropTypesSchema = DSAutocompletePropTypes as unknown as ValidationMap<DSAutocompleteT.Props>;\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADMvB,8BAAuE;AA4EhE,MAAM,eAA6C;AAAA,EACxD,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,6BAA6B;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,0BAA0B,CAAC,gBAAgB,WAAW;AACxD;AAEO,MAAM,0BAAoE;AAAA,EAC/E,GAAG;AAAA,EACH,GAAG;AAAA,EACH,SAAS,kCAAU,UAAU,CAAC,kCAAU,OAAO,kCAAU,MAAM,CAAC,EAC7D,WAAW,YAAY,iBAAiB,EACxC,aAAa,CAAC,CAAC;AAAA,EAClB,QAAQ,kCAAU,OAAO,WAAW,YAAY,oCAAoC,EAAE,aAAa,EAAE;AAAA,EACrG,UAAU,kCAAU,KAAK,WACtB,YAAY,iFAAiF,EAC7F,aAAa,EAAE;AAAA,EAClB,UAAU,kCAAU,KACjB,YAAY,qDAAqD,EACjE,aAAa,EAAE,EACf,WAAW,EAAE,SAAS,MAAM,CAAC;AAAA,EAChC,YAAY,kCAAU,OAAO,YAAY,sCAAsC,EAAE,aAAa,CAAC,CAAC;AAAA,EAChG,eAAe,kCAAU,KAAK,YAAY,wDAAwD,EAAE,aAAa,EAAE;AAAA,EACnH,aAAa,kCAAU,KAAK,YAAY,8BAA8B,EAAE,aAAa,EAAE;AAAA,EACvF,WAAW,kCAAU,KAClB,YAAY,sEAAsE,EAClF,aAAa,OAAO;AAAA,EACvB,YAAY,kCAAU,KAAK,YAAY,6CAA6C,EAAE,aAAa,OAAO;AAAA,EAC1G,0BAA0B,kCAAU,OACjC,YAAY,wCAAwC,EACpD,aAAa,cAAc;AAAA,EAC9B,0BAA0B,kCAAU,QAAQ,kCAAU,MAAM,EACzD,YAAY,0FAA0F,EACtG,aAAa,gCAAgC;AAAA,EAChD,eAAe,kCAAU,KACtB,YAAY,uFAAuF,EACnG,aAAa,2BAA2B;AAAA,EAC3C,6BAA6B,kCAAU,KACpC;AAAA,IACC;AAAA,EACF,EACC,aAAa,OAAO;AACzB;AAEO,MAAM,gCAAgC;",
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable no-unused-vars */\nimport type React from 'react';\nimport type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport type { DSHookFloatingContextT } from '@elliemae/ds-floating-context';\n\nimport { globalAttributesPropTypes, PropTypes, xstyledPropTypes } from '@elliemae/ds-props-helpers';\nimport type { DSInputTextT } from '@elliemae/ds-form-input-text';\n\nexport declare namespace DSAutocompleteT {\n export interface CommonItemOptions {\n dsId: string;\n disabled?: boolean;\n render?: React.ComponentType<unknown>;\n }\n\n export interface ItemSeparatorOptions extends CommonItemOptions {\n type: 'separator';\n }\n // this was never implemented?\n // packages/forms/ds-form-layout-autocomplete/src/parts/menu-list/useItemRenderer.tsx\n // only have code for ItemOption and ItemSectionOptions...\n // export interface ItemSeparatorOptions extends CommonItemOptions {\n // type: 'separator';\n // }\n\n export interface ItemSectionOptions extends CommonItemOptions {\n type: 'section';\n label: string;\n options: ItemOption[];\n }\n\n export interface ItemOption extends CommonItemOptions {\n value: string;\n label: string;\n type: 'option';\n }\n\n export type OptionTypes = ItemOption | ItemSectionOptions;\n\n export type SelectedOptionsT = ItemOption[] | ItemOption | null;\n\n export interface RequiredProps {\n options: OptionTypes[];\n filter: string;\n onSelect: (\n suggestedValue: string,\n e: React.KeyboardEvent<HTMLInputElement> | React.MouseEvent<HTMLInputElement>,\n ) => void;\n }\n export interface DefaultProps {\n startPlacementPreference: string;\n placementOrderPreference: DSHookFloatingContextT.PopperPlacementsT[];\n withoutPortal: boolean;\n zIndex: number;\n forceFocusFirstOptionOnType: boolean;\n }\n\n export interface OptionalProps {\n children?: React.ReactNode;\n inputProps?: DSInputTextT.Props;\n onValueChange?: (nextValue: string | number, e: React.ChangeEvent<HTMLInputElement>) => void;\n renderInput?: (props: React.InputHTMLAttributes<HTMLInputElement>) => React.ReactNode;\n isLoading?: boolean;\n isSkeleton?: boolean;\n }\n\n export interface Props\n extends\n Partial<DefaultProps>,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n\n export interface InternalProps\n extends\n DefaultProps,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n}\n\nexport const defaultProps: DSAutocompleteT.DefaultProps = {\n zIndex: 10,\n withoutPortal: false,\n forceFocusFirstOptionOnType: false,\n startPlacementPreference: 'bottom-start',\n placementOrderPreference: ['bottom-start', 'top-start'],\n};\n\nexport const DSAutocompletePropTypes: DSPropTypesSchema<DSAutocompleteT.Props> = {\n ...globalAttributesPropTypes,\n ...xstyledPropTypes,\n options: PropTypes.oneOfType([PropTypes.array, PropTypes.object])\n .isRequired.description('List of options')\n .defaultValue([]),\n filter: PropTypes.string.isRequired.description('String value to filter the options').defaultValue(''),\n onSelect: PropTypes.func.isRequired\n .description('function triggered when an option is selected it will send the options selected')\n .defaultValue(''),\n children: PropTypes.node\n .description('React component to apply autocomplete functionality')\n .defaultValue('')\n .deprecated({ version: '3.x' }),\n inputProps: PropTypes.object.description('Props to pass to the input component').defaultValue({}),\n onValueChange: PropTypes.func.description('Function triggered when the value of the input changes').defaultValue(''),\n renderInput: PropTypes.func.description('Function to render the input').defaultValue(''),\n isLoading: PropTypes.bool\n .description('Shows a loading spinner in the menu while options are being fetched.')\n .defaultValue('false'),\n isSkeleton: PropTypes.bool.description('Displays the menu in skeleton loading mode.').defaultValue('false'),\n startPlacementPreference: PropTypes.string\n .description('Ideal preference for context placement')\n .defaultValue('bottom-start'),\n placementOrderPreference: PropTypes.arrayOf(PropTypes.string)\n .description('Placement preferences for the context menu in order of most preferred to least preferred')\n .defaultValue(\"['bottom-start', 'bottom-end']\"),\n withoutPortal: PropTypes.bool\n .description('Whether to render the context menu in a react portal or next to the input DOM element')\n .defaultValue('false (next to the input)'),\n forceFocusFirstOptionOnType: PropTypes.bool\n .description(\n 'Whether to focus the first option when typing (NOT RECOMMENDED AND BREAKING A11Y STANDARDS + ALL USERS USABILITY)',\n )\n .defaultValue('false'),\n};\n\nexport const DSAutocompletePropTypesSchema = DSAutocompletePropTypes as unknown as ValidationMap<DSAutocompleteT.Props>;\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADMvB,8BAAuE;AA8EhE,MAAM,eAA6C;AAAA,EACxD,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,6BAA6B;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,0BAA0B,CAAC,gBAAgB,WAAW;AACxD;AAEO,MAAM,0BAAoE;AAAA,EAC/E,GAAG;AAAA,EACH,GAAG;AAAA,EACH,SAAS,kCAAU,UAAU,CAAC,kCAAU,OAAO,kCAAU,MAAM,CAAC,EAC7D,WAAW,YAAY,iBAAiB,EACxC,aAAa,CAAC,CAAC;AAAA,EAClB,QAAQ,kCAAU,OAAO,WAAW,YAAY,oCAAoC,EAAE,aAAa,EAAE;AAAA,EACrG,UAAU,kCAAU,KAAK,WACtB,YAAY,iFAAiF,EAC7F,aAAa,EAAE;AAAA,EAClB,UAAU,kCAAU,KACjB,YAAY,qDAAqD,EACjE,aAAa,EAAE,EACf,WAAW,EAAE,SAAS,MAAM,CAAC;AAAA,EAChC,YAAY,kCAAU,OAAO,YAAY,sCAAsC,EAAE,aAAa,CAAC,CAAC;AAAA,EAChG,eAAe,kCAAU,KAAK,YAAY,wDAAwD,EAAE,aAAa,EAAE;AAAA,EACnH,aAAa,kCAAU,KAAK,YAAY,8BAA8B,EAAE,aAAa,EAAE;AAAA,EACvF,WAAW,kCAAU,KAClB,YAAY,sEAAsE,EAClF,aAAa,OAAO;AAAA,EACvB,YAAY,kCAAU,KAAK,YAAY,6CAA6C,EAAE,aAAa,OAAO;AAAA,EAC1G,0BAA0B,kCAAU,OACjC,YAAY,wCAAwC,EACpD,aAAa,cAAc;AAAA,EAC9B,0BAA0B,kCAAU,QAAQ,kCAAU,MAAM,EACzD,YAAY,0FAA0F,EACtG,aAAa,gCAAgC;AAAA,EAChD,eAAe,kCAAU,KACtB,YAAY,uFAAuF,EACnG,aAAa,2BAA2B;AAAA,EAC3C,6BAA6B,kCAAU,KACpC;AAAA,IACC;AAAA,EACF,EACC,aAAa,OAAO;AACzB;AAEO,MAAM,gCAAgC;",
6
6
  "names": []
7
7
  }
@@ -1,16 +1,26 @@
1
1
  import * as React from "react";
2
2
  import React2, { useCallback } from "react";
3
- import { useVirtual } from "react-virtual";
3
+ import { useVirtualizer } from "@tanstack/react-virtual";
4
+ const OPTION_ROW_HEIGHT_PX = 32;
4
5
  const useVirtualization = ({
5
6
  references,
6
7
  options
7
8
  }) => {
8
9
  const { listRef } = references;
9
- const virtualListHelpers = useVirtual({
10
- size: options.length,
11
- parentRef: listRef,
10
+ const estimateSize = useCallback(
11
+ (index) => {
12
+ const option = options[index];
13
+ if (option.type === "section") return OPTION_ROW_HEIGHT_PX * (1 + option.options.length);
14
+ return OPTION_ROW_HEIGHT_PX;
15
+ },
16
+ [options]
17
+ );
18
+ const virtualListHelpers = useVirtualizer({
19
+ count: options.length,
20
+ getScrollElement: () => listRef.current,
12
21
  overscan: 15,
13
- paddingStart: 0
22
+ paddingStart: 0,
23
+ estimateSize
14
24
  });
15
25
  const scrollOptionIntoView = useCallback(
16
26
  (dsId, opts = { align: "center" }) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/config/useVirtualization.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useCallback } from 'react';\nimport { useVirtual, type ScrollToIndexOptions } from 'react-virtual';\n\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\nimport type { useRefs } from './useRefs.js';\n\nexport const useVirtualization = ({\n references,\n options,\n}: {\n references: ReturnType<typeof useRefs>;\n options: DSAutocompleteT.InternalProps['options'];\n}): {\n virtualListHelpers: ReturnType<typeof useVirtual>;\n scrollOptionIntoView: (dsId: string, opts?: ScrollToIndexOptions) => void;\n} => {\n const { listRef } = references;\n // ===========================================================================\n // Virtualization setup\n // ===========================================================================\n const virtualListHelpers: ReturnType<typeof useVirtual> = useVirtual({\n size: options.length,\n parentRef: listRef,\n overscan: 15,\n paddingStart: 0,\n });\n\n // ===========================================================================\n // Scroll into view function\n // ===========================================================================\n const scrollOptionIntoView = useCallback(\n (dsId: string, opts: ScrollToIndexOptions = { align: 'center' }) => {\n virtualListHelpers.scrollToIndex(\n options.findIndex((opt) => opt.dsId === dsId),\n opts,\n );\n },\n [options, virtualListHelpers],\n );\n\n return React.useMemo(\n () => ({\n virtualListHelpers,\n scrollOptionIntoView,\n }),\n [virtualListHelpers, scrollOptionIntoView],\n );\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACAvB,OAAOA,UAAS,mBAAmB;AACnC,SAAS,kBAA6C;AAK/C,MAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AACF,MAMK;AACH,QAAM,EAAE,QAAQ,IAAI;AAIpB,QAAM,qBAAoD,WAAW;AAAA,IACnE,MAAM,QAAQ;AAAA,IACd,WAAW;AAAA,IACX,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AAKD,QAAM,uBAAuB;AAAA,IAC3B,CAAC,MAAc,OAA6B,EAAE,OAAO,SAAS,MAAM;AAClE,yBAAmB;AAAA,QACjB,QAAQ,UAAU,CAAC,QAAQ,IAAI,SAAS,IAAI;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,SAAS,kBAAkB;AAAA,EAC9B;AAEA,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,oBAAoB,oBAAoB;AAAA,EAC3C;AACF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useCallback } from 'react';\nimport { useVirtualizer, type ScrollToOptions } from '@tanstack/react-virtual';\n\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\nimport type { useRefs } from './useRefs.js';\n\n// Mirrors ds-menu-items-commons's MENU_ITEM_ROW_HEIGHT (32px) \u2014 not importable, that constant is\n// private to that package (same duplication already tracked for Section.tsx's StyledSectionWrapper\n// min-height in KNOWN_INTENTIONAL_DEVIATIONS.md). A \"section\" virtual item renders its own header\n// plus all of its sub-options inline (see Section.tsx), so its estimate must account for that.\nconst OPTION_ROW_HEIGHT_PX = 32;\n\nexport const useVirtualization = ({\n references,\n options,\n}: {\n references: ReturnType<typeof useRefs>;\n options: DSAutocompleteT.InternalProps['options'];\n}): {\n // TItemElement is HTMLElement, not HTMLDivElement: rendered items are a mix of <li> roots\n // (StyledMenuItemAction/DSMenuItemsAction, an HTMLLIElement) and <section> roots (Section.tsx's\n // StyledSectionWrapper) \u2014 HTMLElement is their common base.\n virtualListHelpers: ReturnType<typeof useVirtualizer<HTMLDivElement, HTMLElement>>;\n scrollOptionIntoView: (dsId: string, opts?: ScrollToOptions) => void;\n} => {\n const { listRef } = references;\n // ===========================================================================\n // Virtualization setup\n // ===========================================================================\n const estimateSize = useCallback(\n (index: number) => {\n const option = options[index];\n // v3 has no internal default for estimateSize (v2 silently defaulted to 50) \u2014 omitting it\n // silently zeroes out getVirtualItems()/getTotalSize() with real data present, no thrown error.\n if (option.type === 'section') return OPTION_ROW_HEIGHT_PX * (1 + option.options.length);\n return OPTION_ROW_HEIGHT_PX;\n },\n [options],\n );\n\n const virtualListHelpers = useVirtualizer<HTMLDivElement, HTMLElement>({\n count: options.length,\n getScrollElement: () => listRef.current,\n overscan: 15,\n paddingStart: 0,\n estimateSize,\n });\n\n // ===========================================================================\n // Scroll into view function\n // ===========================================================================\n const scrollOptionIntoView = useCallback(\n (dsId: string, opts: ScrollToOptions = { align: 'center' }) => {\n virtualListHelpers.scrollToIndex(\n options.findIndex((opt) => opt.dsId === dsId),\n opts,\n );\n },\n [options, virtualListHelpers],\n );\n\n return React.useMemo(\n () => ({\n virtualListHelpers,\n scrollOptionIntoView,\n }),\n [virtualListHelpers, scrollOptionIntoView],\n );\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,OAAOA,UAAS,mBAAmB;AACnC,SAAS,sBAA4C;AASrD,MAAM,uBAAuB;AAEtB,MAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AACF,MASK;AACH,QAAM,EAAE,QAAQ,IAAI;AAIpB,QAAM,eAAe;AAAA,IACnB,CAAC,UAAkB;AACjB,YAAM,SAAS,QAAQ,KAAK;AAG5B,UAAI,OAAO,SAAS,UAAW,QAAO,wBAAwB,IAAI,OAAO,QAAQ;AACjF,aAAO;AAAA,IACT;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,qBAAqB,eAA4C;AAAA,IACrE,OAAO,QAAQ;AAAA,IACf,kBAAkB,MAAM,QAAQ;AAAA,IAChC,UAAU;AAAA,IACV,cAAc;AAAA,IACd;AAAA,EACF,CAAC;AAKD,QAAM,uBAAuB;AAAA,IAC3B,CAAC,MAAc,OAAwB,EAAE,OAAO,SAAS,MAAM;AAC7D,yBAAmB;AAAA,QACjB,QAAQ,UAAU,CAAC,QAAQ,IAAI,SAAS,IAAI;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,SAAS,kBAAkB;AAAA,EAC9B;AAEA,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,oBAAoB,oBAAoB;AAAA,EAC3C;AACF;",
6
6
  "names": ["React"]
7
7
  }
@@ -18,25 +18,36 @@ const StyledSectionLabel = styled(DSTypography)`
18
18
  `;
19
19
  const Section = React2.memo((props) => {
20
20
  const { label, wrapperStyles, focusOptionIdx, innerRef, options, handleClick } = props;
21
+ const dataIndex = props["data-index"];
21
22
  const id = `${label.replace(/ /g, "")}-section`;
22
- return /* @__PURE__ */ jsxs(StyledSectionWrapper, { role: "group", "aria-labelledby": id, style: wrapperStyles, innerRef, children: [
23
- /* @__PURE__ */ jsx(StyledSectionLabel, { variant: "b3-strong", component: "header", id, role: "presentation", children: label }),
24
- options?.map((option) => /* @__PURE__ */ jsx(
25
- DSMenuItemsAction,
26
- {
27
- as: "div",
28
- dsId: option.dsId,
29
- id: option.dsId,
30
- label: option.label,
31
- "data-testid": AutocompleteDataTestid.OPTION,
32
- disabled: option.disabled,
33
- onClick: (e) => handleClick(option, e),
34
- isActive: option.dsId === focusOptionIdx,
35
- "aria-selected": "false"
36
- },
37
- option.dsId
38
- ))
39
- ] });
23
+ return /* @__PURE__ */ jsxs(
24
+ StyledSectionWrapper,
25
+ {
26
+ role: "group",
27
+ "aria-labelledby": id,
28
+ style: wrapperStyles,
29
+ innerRef,
30
+ "data-index": dataIndex,
31
+ children: [
32
+ /* @__PURE__ */ jsx(StyledSectionLabel, { variant: "b3-strong", component: "header", id, role: "presentation", children: label }),
33
+ options?.map((option) => /* @__PURE__ */ jsx(
34
+ DSMenuItemsAction,
35
+ {
36
+ as: "div",
37
+ dsId: option.dsId,
38
+ id: option.dsId,
39
+ label: option.label,
40
+ "data-testid": AutocompleteDataTestid.OPTION,
41
+ disabled: option.disabled,
42
+ onClick: (e) => handleClick(option, e),
43
+ isActive: option.dsId === focusOptionIdx,
44
+ "aria-selected": "false"
45
+ },
46
+ option.dsId
47
+ ))
48
+ ]
49
+ }
50
+ );
40
51
  });
41
52
  export {
42
53
  Section
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/parts/Section.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { styled } from '@elliemae/ds-system';\nimport { DSTypography } from '@elliemae/ds-typography';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { AutocompleteDataTestid } from '../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\n\ninterface SectionProps {\n label: string;\n wrapperStyles: React.CSSProperties;\n innerRef: (instance: HTMLDivElement | null) => void;\n options: DSAutocompleteT.ItemOption[];\n handleClick: (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => void;\n focusOptionIdx: string;\n}\n\nconst StyledSectionWrapper = styled('section')`\n list-style: none;\n min-height: 32px;\n display: grid;\n align-items: center;\n`;\n\n/**\n * Group-label styling aligned with its siblings (PUI-18609 / PUI-15487): b3-strong typography,\n * 32px row (8px+8px padding + b3-strong line-height, mirrored by the wrapper min-height above).\n *\n * This is one of four independent reimplementations of the section/group-label row\n * (ds-menu-items-commons, ds-form-combobox, ds-menu-button being the others). The duplication \u2014\n * and the fact that these styled calls remain slot-less here \u2014 is tracked in\n * KNOWN_INTENTIONAL_DEVIATIONS.md.\n */\nconst StyledSectionLabel = styled(DSTypography)`\n display: flex;\n align-items: center;\n padding: 8px 16px;\n`;\n\nexport const Section = React.memo((props: SectionProps) => {\n const { label, wrapperStyles, focusOptionIdx, innerRef, options, handleClick } = props;\n const id = `${label.replace(/ /g, '')}-section`;\n return (\n <StyledSectionWrapper role=\"group\" aria-labelledby={id} style={wrapperStyles} innerRef={innerRef}>\n <StyledSectionLabel variant=\"b3-strong\" component=\"header\" id={id} role=\"presentation\">\n {label}\n </StyledSectionLabel>\n {options?.map((option) => (\n <DSMenuItemsAction\n as=\"div\"\n key={option.dsId}\n dsId={option.dsId}\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n />\n ))}\n </StyledSectionWrapper>\n );\n});\n"],
5
- "mappings": "AAAA,YAAY,WAAW;AC0CnB,SACE,KADF;AA1CJ,OAAOA,YAAW;AAClB,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAClC,SAAS,8BAA8B;AAYvC,MAAM,uBAAuB,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAgB7C,MAAM,qBAAqB,OAAO,YAAY;AAAA;AAAA;AAAA;AAAA;AAMvC,MAAM,UAAUA,OAAM,KAAK,CAAC,UAAwB;AACzD,QAAM,EAAE,OAAO,eAAe,gBAAgB,UAAU,SAAS,YAAY,IAAI;AACjF,QAAM,KAAK,GAAG,MAAM,QAAQ,MAAM,EAAE,CAAC;AACrC,SACE,qBAAC,wBAAqB,MAAK,SAAQ,mBAAiB,IAAI,OAAO,eAAe,UAC5E;AAAA,wBAAC,sBAAmB,SAAQ,aAAY,WAAU,UAAS,IAAQ,MAAK,gBACrE,iBACH;AAAA,IACC,SAAS,IAAI,CAAC,WACb;AAAA,MAAC;AAAA;AAAA,QACC,IAAG;AAAA,QAEH,MAAM,OAAO;AAAA,QACb,IAAI,OAAO;AAAA,QACX,OAAO,OAAO;AAAA,QACd,eAAa,uBAAuB;AAAA,QACpC,UAAU,OAAO;AAAA,QACjB,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,QACzE,UAAU,OAAO,SAAS;AAAA,QAC1B,iBAAc;AAAA;AAAA,MART,OAAO;AAAA,IASd,CACD;AAAA,KACH;AAEJ,CAAC;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { styled } from '@elliemae/ds-system';\nimport { DSTypography } from '@elliemae/ds-typography';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { AutocompleteDataTestid } from '../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../react-desc-prop-types.js';\n\ninterface SectionProps {\n label: string;\n wrapperStyles: React.CSSProperties;\n // Root is StyledSectionWrapper (styled('section')) \u2014 HTMLElement, not HTMLDivElement.\n innerRef: (instance: HTMLElement | null) => void;\n 'data-index': number;\n options: DSAutocompleteT.ItemOption[];\n handleClick: (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => void;\n focusOptionIdx: string;\n}\n\nconst StyledSectionWrapper = styled('section')`\n list-style: none;\n min-height: 32px;\n display: grid;\n align-items: center;\n`;\n\n/**\n * Group-label styling aligned with its siblings (PUI-18609 / PUI-15487): b3-strong typography,\n * 32px row (8px+8px padding + b3-strong line-height, mirrored by the wrapper min-height above).\n *\n * This is one of four independent reimplementations of the section/group-label row\n * (ds-menu-items-commons, ds-form-combobox, ds-menu-button being the others). The duplication \u2014\n * and the fact that these styled calls remain slot-less here \u2014 is tracked in\n * KNOWN_INTENTIONAL_DEVIATIONS.md.\n */\nconst StyledSectionLabel = styled(DSTypography)`\n display: flex;\n align-items: center;\n padding: 8px 16px;\n`;\n\nexport const Section = React.memo((props: SectionProps) => {\n const { label, wrapperStyles, focusOptionIdx, innerRef, options, handleClick } = props;\n const dataIndex = props['data-index'];\n const id = `${label.replace(/ /g, '')}-section`;\n return (\n <StyledSectionWrapper\n role=\"group\"\n aria-labelledby={id}\n style={wrapperStyles}\n innerRef={innerRef}\n data-index={dataIndex}\n >\n <StyledSectionLabel variant=\"b3-strong\" component=\"header\" id={id} role=\"presentation\">\n {label}\n </StyledSectionLabel>\n {options?.map((option) => (\n <DSMenuItemsAction\n as=\"div\"\n key={option.dsId}\n dsId={option.dsId}\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n />\n ))}\n </StyledSectionWrapper>\n );\n});\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AC6CnB,SAOE,KAPF;AA7CJ,OAAOA,YAAW;AAClB,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAClC,SAAS,8BAA8B;AAcvC,MAAM,uBAAuB,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAgB7C,MAAM,qBAAqB,OAAO,YAAY;AAAA;AAAA;AAAA;AAAA;AAMvC,MAAM,UAAUA,OAAM,KAAK,CAAC,UAAwB;AACzD,QAAM,EAAE,OAAO,eAAe,gBAAgB,UAAU,SAAS,YAAY,IAAI;AACjF,QAAM,YAAY,MAAM,YAAY;AACpC,QAAM,KAAK,GAAG,MAAM,QAAQ,MAAM,EAAE,CAAC;AACrC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,mBAAiB;AAAA,MACjB,OAAO;AAAA,MACP;AAAA,MACA,cAAY;AAAA,MAEZ;AAAA,4BAAC,sBAAmB,SAAQ,aAAY,WAAU,UAAS,IAAQ,MAAK,gBACrE,iBACH;AAAA,QACC,SAAS,IAAI,CAAC,WACb;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YAEH,MAAM,OAAO;AAAA,YACb,IAAI,OAAO;AAAA,YACX,OAAO,OAAO;AAAA,YACd,eAAa,uBAAuB;AAAA,YACpC,UAAU,OAAO;AAAA,YACjB,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,YACzE,UAAU,OAAO,SAAS;AAAA,YAC1B,iBAAc;AAAA;AAAA,UART,OAAO;AAAA,QASd,CACD;AAAA;AAAA;AAAA,EACH;AAEJ,CAAC;",
6
6
  "names": ["React"]
7
7
  }
@@ -16,21 +16,22 @@ const MenuList = () => {
16
16
  listRef,
17
17
  autoCompleteId,
18
18
  getOwnerProps,
19
- virtualListHelpers: { totalSize, measure }
19
+ virtualListHelpers
20
20
  } = useContext(AutocompleteContext);
21
+ const { measure } = virtualListHelpers;
22
+ const totalSize = virtualListHelpers.getTotalSize();
21
23
  const ItemRenderer = useItemRenderer();
22
24
  const { width } = useOnElementResize(referenceElement);
25
+ useEffect(() => {
26
+ if (!measure) return;
27
+ measure();
28
+ }, [width, measure]);
23
29
  const handleOnFocus = useCallback(() => {
24
30
  inputRef.current?.focus();
25
31
  }, [inputRef]);
26
32
  const preventMouseDown = useCallback((e) => {
27
33
  e.preventDefault();
28
34
  }, []);
29
- useEffect(() => {
30
- if (!measure) return void 0;
31
- const rafId = requestAnimationFrame(() => measure());
32
- return () => cancelAnimationFrame(rafId);
33
- }, [width]);
34
35
  const renderMenuContent = () => {
35
36
  if (isSkeleton) return /* @__PURE__ */ jsx(SkeletonContainer, { autoCompleteId, getOwnerProps });
36
37
  if (isLoading) return /* @__PURE__ */ jsx(LoadingContainer, { autoCompleteId, getOwnerProps });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/menu-list/MenuList.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useContext, useCallback, useEffect } from 'react';\nimport { StyledListWrapper, StyledList, StyledVirtualListWrapper } from './styled.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { useItemRenderer } from './useItemRenderer.js';\nimport { useOnElementResize } from '../../utils/useOnElementResize.js';\nimport { LoadingContainer } from './LoadingContainer.js';\nimport { SkeletonContainer } from './SkeletonContainer.js';\n\nexport const MenuList = (): JSX.Element => {\n const {\n props: { options, isLoading, isSkeleton },\n referenceElement,\n inputRef,\n listRef,\n autoCompleteId,\n getOwnerProps,\n virtualListHelpers: { totalSize, measure },\n } = useContext(AutocompleteContext);\n\n const ItemRenderer = useItemRenderer();\n const { width } = useOnElementResize(referenceElement);\n\n const handleOnFocus = useCallback(() => {\n inputRef.current?.focus();\n }, [inputRef]);\n\n const preventMouseDown: React.MouseEventHandler<HTMLDivElement> = useCallback((e) => {\n e.preventDefault();\n }, []);\n\n useEffect(() => {\n if (!measure) return undefined;\n // Defer measure() to the next animation frame so it never runs inside an active\n // React sync flush (React 17 / legacy mode). Without the deferral, the call chain\n // outputsize \u2192 setWidth \u2192 [flushSyncCallbackQueue] \u2192 commit \u2192 measureRef \u2192\n // setMeasuredCache \u2192 [new syncQueue entry] \u2192 commit \u2192 measureRef \u2192 \u2026\n // keeps adding entries to the live syncQueue array that flushSyncCallbackQueue\n // is iterating over, producing 25+ nested commits and the\n // \"Maximum update depth exceeded\" crash. requestAnimationFrame breaks that chain\n // by scheduling measure() for after the current flush completes.\n const rafId = requestAnimationFrame(() => measure());\n return () => cancelAnimationFrame(rafId);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [width]);\n\n const renderMenuContent = () => {\n // Loading/skeleton own the listbox id + role + aria-busy while options are being fetched;\n // only one of skeleton / loading / list is ever mounted at a time (see the containers).\n if (isSkeleton) return <SkeletonContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (isLoading) return <LoadingContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (options.length === 0) return null;\n return (\n <StyledVirtualListWrapper tabIndex={-1} innerRef={listRef}>\n <StyledList\n // recalculate the key to force the re-render of the list and update virtualization\n key={options.length}\n role=\"listbox\"\n id={`menu-list-${autoCompleteId}`}\n // default aria label until we integrate the aria props on autocomplete\n // this is for the axe core to pass\n aria-label=\"autocomplete option lists\"\n onFocus={handleOnFocus}\n data-testid={AutocompleteDataTestid.LIST}\n style={{ height: totalSize, margin: '8px 0px' }}\n getOwnerProps={getOwnerProps}\n >\n {ItemRenderer}\n </StyledList>\n </StyledVirtualListWrapper>\n );\n };\n\n return (\n <StyledListWrapper onMouseDown={preventMouseDown} minWidth={referenceElement?.offsetWidth}>\n {renderMenuContent()}\n </StyledListWrapper>\n );\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACiDI;AAjD3B,SAAgB,YAAY,aAAa,iBAAiB;AAC1D,SAAS,mBAAmB,YAAY,gCAAgC;AACxE,SAAS,8BAA8B;AACvC,OAAO,yBAAyB;AAChC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAE3B,MAAM,WAAW,MAAmB;AACzC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS,WAAW,WAAW;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB,EAAE,WAAW,QAAQ;AAAA,EAC3C,IAAI,WAAW,mBAAmB;AAElC,QAAM,eAAe,gBAAgB;AACrC,QAAM,EAAE,MAAM,IAAI,mBAAmB,gBAAgB;AAErD,QAAM,gBAAgB,YAAY,MAAM;AACtC,aAAS,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,mBAA4D,YAAY,CAAC,MAAM;AACnF,MAAE,eAAe;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,QAAI,CAAC,QAAS,QAAO;AASrB,UAAM,QAAQ,sBAAsB,MAAM,QAAQ,CAAC;AACnD,WAAO,MAAM,qBAAqB,KAAK;AAAA,EAEzC,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,oBAAoB,MAAM;AAG9B,QAAI,WAAY,QAAO,oBAAC,qBAAkB,gBAAgC,eAA8B;AACxG,QAAI,UAAW,QAAO,oBAAC,oBAAiB,gBAAgC,eAA8B;AACtG,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,WACE,oBAAC,4BAAyB,UAAU,IAAI,UAAU,SAChD;AAAA,MAAC;AAAA;AAAA,QAGC,MAAK;AAAA,QACL,IAAI,aAAa,cAAc;AAAA,QAG/B,cAAW;AAAA,QACX,SAAS;AAAA,QACT,eAAa,uBAAuB;AAAA,QACpC,OAAO,EAAE,QAAQ,WAAW,QAAQ,UAAU;AAAA,QAC9C;AAAA,QAEC;AAAA;AAAA,MAXI,QAAQ;AAAA,IAYf,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,qBAAkB,aAAa,kBAAkB,UAAU,kBAAkB,aAC3E,4BAAkB,GACrB;AAEJ;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useContext, useCallback, useEffect } from 'react';\nimport { StyledListWrapper, StyledList, StyledVirtualListWrapper } from './styled.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { useItemRenderer } from './useItemRenderer.js';\nimport { useOnElementResize } from '../../utils/useOnElementResize.js';\nimport { LoadingContainer } from './LoadingContainer.js';\nimport { SkeletonContainer } from './SkeletonContainer.js';\n\nexport const MenuList = (): JSX.Element => {\n const {\n props: { options, isLoading, isSkeleton },\n referenceElement,\n inputRef,\n listRef,\n autoCompleteId,\n getOwnerProps,\n virtualListHelpers,\n } = useContext(AutocompleteContext);\n const { measure } = virtualListHelpers;\n const totalSize = virtualListHelpers.getTotalSize();\n\n const ItemRenderer = useItemRenderer();\n const { width } = useOnElementResize(referenceElement);\n\n useEffect(() => {\n if (!measure) return;\n // v2 deferred this call to the next animation frame to avoid a setMeasuredCache\n // re-entrancy bug during a React legacy-mode sync flush (measureRef \u2192 setMeasuredCache \u2192\n // new commit \u2192 measureRef \u2192 ... \u2192 \"Maximum update depth exceeded\"). @tanstack/react-virtual\n // v3 has no such state setter in its measurement path \u2014 per-item sizing is driven by\n // ResizeObserver instead \u2014 so the bug this deferral guarded against no longer applies.\n // Verified empirically (PUI-19077): removing the deferral does not reproduce the crash,\n // including under a hardened stress test (24 oscillating widths, 16ms apart, 30 repeated\n // headless Playwright CT runs).\n //\n // NOTE: a follow-up \"callback coordination\" refactor (calling measure() directly from\n // useOnElementResize's ResizeObserver callback instead of via this width-effect) was tried\n // and reverted \u2014 it broke DSAutocomplete.variableHeightLayout.test.playwright.jsx\n // deterministically (15/15 runs, same 3.6875px gap). Calling measure() from inside the native\n // ResizeObserver callback fires earlier/differently relative to @tanstack/react-virtual's own\n // per-item measureElement ResizeObserver than calling it from a React effect reacting to\n // width state does. Routing through width state is load-bearing here \u2014 do not \"simplify\" it\n // away again without re-running the variable-height layout test.\n measure();\n }, [width, measure]);\n\n const handleOnFocus = useCallback(() => {\n inputRef.current?.focus();\n }, [inputRef]);\n\n const preventMouseDown: React.MouseEventHandler<HTMLDivElement> = useCallback((e) => {\n e.preventDefault();\n }, []);\n\n const renderMenuContent = () => {\n // Loading/skeleton own the listbox id + role + aria-busy while options are being fetched;\n // only one of skeleton / loading / list is ever mounted at a time (see the containers).\n if (isSkeleton) return <SkeletonContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (isLoading) return <LoadingContainer autoCompleteId={autoCompleteId} getOwnerProps={getOwnerProps} />;\n if (options.length === 0) return null;\n return (\n <StyledVirtualListWrapper tabIndex={-1} innerRef={listRef}>\n <StyledList\n // recalculate the key to force the re-render of the list and update virtualization\n key={options.length}\n role=\"listbox\"\n id={`menu-list-${autoCompleteId}`}\n // default aria label until we integrate the aria props on autocomplete\n // this is for the axe core to pass\n aria-label=\"autocomplete option lists\"\n onFocus={handleOnFocus}\n data-testid={AutocompleteDataTestid.LIST}\n style={{ height: totalSize, margin: '8px 0px' }}\n getOwnerProps={getOwnerProps}\n >\n {ItemRenderer}\n </StyledList>\n </StyledVirtualListWrapper>\n );\n };\n\n return (\n <StyledListWrapper onMouseDown={preventMouseDown} minWidth={referenceElement?.offsetWidth}>\n {renderMenuContent()}\n </StyledListWrapper>\n );\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AC0DI;AA1D3B,SAAgB,YAAY,aAAa,iBAAiB;AAC1D,SAAS,mBAAmB,YAAY,gCAAgC;AACxE,SAAS,8BAA8B;AACvC,OAAO,yBAAyB;AAChC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAE3B,MAAM,WAAW,MAAmB;AACzC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS,WAAW,WAAW;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,WAAW,mBAAmB;AAClC,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,YAAY,mBAAmB,aAAa;AAElD,QAAM,eAAe,gBAAgB;AACrC,QAAM,EAAE,MAAM,IAAI,mBAAmB,gBAAgB;AAErD,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAkBd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,OAAO,CAAC;AAEnB,QAAM,gBAAgB,YAAY,MAAM;AACtC,aAAS,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,mBAA4D,YAAY,CAAC,MAAM;AACnF,MAAE,eAAe;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoB,MAAM;AAG9B,QAAI,WAAY,QAAO,oBAAC,qBAAkB,gBAAgC,eAA8B;AACxG,QAAI,UAAW,QAAO,oBAAC,oBAAiB,gBAAgC,eAA8B;AACtG,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,WACE,oBAAC,4BAAyB,UAAU,IAAI,UAAU,SAChD;AAAA,MAAC;AAAA;AAAA,QAGC,MAAK;AAAA,QACL,IAAI,aAAa,cAAc;AAAA,QAG/B,cAAW;AAAA,QACX,SAAS;AAAA,QACT,eAAa,uBAAuB;AAAA,QACpC,OAAO,EAAE,QAAQ,WAAW,QAAQ,UAAU;AAAA,QAC9C;AAAA,QAEC;AAAA;AAAA,MAXI,QAAQ;AAAA,IAYf,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,qBAAkB,aAAa,kBAAkB,UAAU,kBAAkB,aAC3E,4BAAkB,GACrB;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -14,7 +14,6 @@ const StyledMenuItemAction = styled(DSMenuItemsAction, {
14
14
  const useItemRenderer = () => {
15
15
  const {
16
16
  props: { onSelect },
17
- inputRef,
18
17
  setUserTypedSomething,
19
18
  setUserJustSelectedAnOption,
20
19
  focusOptionIdx,
@@ -32,52 +31,53 @@ const useItemRenderer = () => {
32
31
  },
33
32
  [onSelect, setUserTypedSomething, setUserJustSelectedAnOption]
34
33
  );
34
+ const virtualItems = virtualListHelpers?.getVirtualItems();
35
+ const { measureElement } = virtualListHelpers ?? {};
35
36
  return useMemo(() => {
36
- if (virtualListHelpers) {
37
- return virtualListHelpers.virtualItems.map((vItem) => {
38
- const option = options[vItem.index];
39
- const generalProps = {
40
- wrapperStyles: { position: "absolute", top: vItem.start, left: 0, width: "100%" },
41
- key: `${option.dsId}`,
42
- innerRef: vItem.measureRef,
43
- dsId: option.dsId
44
- };
45
- const getOwnerPropsArguments = () => option;
46
- if (option.type === "section") {
47
- return /* @__PURE__ */ jsx(
48
- Section,
49
- {
50
- label: option.label,
51
- ...generalProps,
52
- options: option.options,
53
- handleClick,
54
- focusOptionIdx
55
- }
56
- );
57
- }
58
- if (option.type === "option") {
59
- return /* @__PURE__ */ jsx(
60
- StyledMenuItemAction,
61
- {
62
- id: option.dsId,
63
- label: option.label,
64
- "data-testid": AutocompleteDataTestid.OPTION,
65
- disabled: option.disabled,
66
- tabIndex: 0,
67
- onClick: (e) => handleClick(option, e),
68
- isActive: option.dsId === focusOptionIdx,
69
- "aria-selected": "false",
70
- ...generalProps,
71
- getOwnerProps,
72
- getOwnerPropsArguments
73
- }
74
- );
75
- }
76
- return null;
77
- });
78
- }
79
- return null;
80
- }, [options, focusOptionIdx, inputRef, virtualListHelpers]);
37
+ if (!virtualItems) return null;
38
+ return virtualItems.map((vItem) => {
39
+ const option = options[vItem.index];
40
+ const generalProps = {
41
+ wrapperStyles: { position: "absolute", top: vItem.start, left: 0, width: "100%" },
42
+ key: `${option.dsId}`,
43
+ innerRef: measureElement,
44
+ "data-index": vItem.index,
45
+ dsId: option.dsId
46
+ };
47
+ const getOwnerPropsArguments = () => option;
48
+ if (option.type === "section") {
49
+ return /* @__PURE__ */ jsx(
50
+ Section,
51
+ {
52
+ label: option.label,
53
+ ...generalProps,
54
+ options: option.options,
55
+ handleClick,
56
+ focusOptionIdx
57
+ }
58
+ );
59
+ }
60
+ if (option.type === "option") {
61
+ return /* @__PURE__ */ jsx(
62
+ StyledMenuItemAction,
63
+ {
64
+ id: option.dsId,
65
+ label: option.label,
66
+ "data-testid": AutocompleteDataTestid.OPTION,
67
+ disabled: option.disabled,
68
+ tabIndex: 0,
69
+ onClick: (e) => handleClick(option, e),
70
+ isActive: option.dsId === focusOptionIdx,
71
+ "aria-selected": "false",
72
+ ...generalProps,
73
+ getOwnerProps,
74
+ getOwnerPropsArguments
75
+ }
76
+ );
77
+ }
78
+ return null;
79
+ });
80
+ }, [virtualItems, options, focusOptionIdx, measureElement, handleClick, getOwnerProps]);
81
81
  };
82
82
  export {
83
83
  useItemRenderer
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/menu-list/useItemRenderer.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable react-hooks/exhaustive-deps */\nimport React, { useMemo, useContext, useCallback } from 'react';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { styled } from '@elliemae/ds-system';\nimport { DSAutocompleteName, DSAutocompleteSlots } from '../../constants/index.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../../react-desc-prop-types.js';\nimport { Section } from '../Section.js';\n\nconst StyledMenuItemAction = styled(DSMenuItemsAction, {\n name: DSAutocompleteName,\n slot: DSAutocompleteSlots.OPTION,\n})``;\n\nexport const useItemRenderer = () => {\n const {\n props: { onSelect },\n inputRef,\n setUserTypedSomething,\n setUserJustSelectedAnOption,\n focusOptionIdx,\n optionsPerSection: options,\n virtualListHelpers,\n getOwnerProps,\n } = useContext(AutocompleteContext);\n\n const handleClick = useCallback(\n (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => {\n onSelect(option.label, e);\n setUserJustSelectedAnOption(true);\n setUserTypedSomething(false);\n e.stopPropagation();\n e.preventDefault();\n },\n [onSelect, setUserTypedSomething, setUserJustSelectedAnOption],\n );\n\n return useMemo(() => {\n if (virtualListHelpers) {\n return virtualListHelpers.virtualItems.map((vItem) => {\n const option = options[vItem.index];\n const generalProps = {\n wrapperStyles: { position: 'absolute', top: vItem.start, left: 0, width: '100%' } as React.CSSProperties,\n key: `${option.dsId}`,\n innerRef: vItem.measureRef,\n dsId: option.dsId,\n };\n const getOwnerPropsArguments = () => option;\n\n if (option.type === 'section') {\n return (\n <Section\n label={option.label}\n {...generalProps}\n options={option.options}\n handleClick={handleClick}\n focusOptionIdx={focusOptionIdx}\n />\n );\n }\n if (option.type === 'option') {\n return (\n <StyledMenuItemAction\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n tabIndex={0}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n {...generalProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n );\n }\n return null;\n });\n }\n return null;\n }, [options, focusOptionIdx, inputRef, virtualListHelpers]);\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACqDX;AAnDZ,SAAgB,SAAS,YAAY,mBAAmB;AACxD,SAAS,yBAAyB;AAClC,SAAS,cAAc;AACvB,SAAS,oBAAoB,2BAA2B;AACxD,OAAO,yBAAyB;AAChC,SAAS,8BAA8B;AAEvC,SAAS,eAAe;AAExB,MAAM,uBAAuB,OAAO,mBAAmB;AAAA,EACrD,MAAM;AAAA,EACN,MAAM,oBAAoB;AAC5B,CAAC;AAEM,MAAM,kBAAkB,MAAM;AACnC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,EACF,IAAI,WAAW,mBAAmB;AAElC,QAAM,cAAc;AAAA,IAClB,CAAC,QAAoC,MAA0C;AAC7E,eAAS,OAAO,OAAO,CAAC;AACxB,kCAA4B,IAAI;AAChC,4BAAsB,KAAK;AAC3B,QAAE,gBAAgB;AAClB,QAAE,eAAe;AAAA,IACnB;AAAA,IACA,CAAC,UAAU,uBAAuB,2BAA2B;AAAA,EAC/D;AAEA,SAAO,QAAQ,MAAM;AACnB,QAAI,oBAAoB;AACtB,aAAO,mBAAmB,aAAa,IAAI,CAAC,UAAU;AACpD,cAAM,SAAS,QAAQ,MAAM,KAAK;AAClC,cAAM,eAAe;AAAA,UACnB,eAAe,EAAE,UAAU,YAAY,KAAK,MAAM,OAAO,MAAM,GAAG,OAAO,OAAO;AAAA,UAChF,KAAK,GAAG,OAAO,IAAI;AAAA,UACnB,UAAU,MAAM;AAAA,UAChB,MAAM,OAAO;AAAA,QACf;AACA,cAAM,yBAAyB,MAAM;AAErC,YAAI,OAAO,SAAS,WAAW;AAC7B,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,OAAO;AAAA,cACb,GAAG;AAAA,cACJ,SAAS,OAAO;AAAA,cAChB;AAAA,cACA;AAAA;AAAA,UACF;AAAA,QAEJ;AACA,YAAI,OAAO,SAAS,UAAU;AAC5B,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,IAAI,OAAO;AAAA,cACX,OAAO,OAAO;AAAA,cACd,eAAa,uBAAuB;AAAA,cACpC,UAAU,OAAO;AAAA,cACjB,UAAU;AAAA,cACV,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,cACzE,UAAU,OAAO,SAAS;AAAA,cAC1B,iBAAc;AAAA,cACb,GAAG;AAAA,cACJ;AAAA,cACA;AAAA;AAAA,UACF;AAAA,QAEJ;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,gBAAgB,UAAU,kBAAkB,CAAC;AAC5D;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable react-hooks/exhaustive-deps */\nimport React, { useMemo, useContext, useCallback } from 'react';\nimport { DSMenuItemsAction } from '@elliemae/ds-menu-items';\nimport { styled } from '@elliemae/ds-system';\nimport { DSAutocompleteName, DSAutocompleteSlots } from '../../constants/index.js';\nimport AutocompleteContext from '../../AutocompleteCTX.js';\nimport { AutocompleteDataTestid } from '../../AutocompleteDataTestids.js';\nimport type { DSAutocompleteT } from '../../react-desc-prop-types.js';\nimport { Section } from '../Section.js';\n\nconst StyledMenuItemAction = styled(DSMenuItemsAction, {\n name: DSAutocompleteName,\n slot: DSAutocompleteSlots.OPTION,\n})``;\n\nexport const useItemRenderer = () => {\n const {\n props: { onSelect },\n setUserTypedSomething,\n setUserJustSelectedAnOption,\n focusOptionIdx,\n optionsPerSection: options,\n virtualListHelpers,\n getOwnerProps,\n } = useContext(AutocompleteContext);\n\n const handleClick = useCallback(\n (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => {\n onSelect(option.label, e);\n setUserJustSelectedAnOption(true);\n setUserTypedSomething(false);\n e.stopPropagation();\n e.preventDefault();\n },\n [onSelect, setUserTypedSomething, setUserJustSelectedAnOption],\n );\n\n // v3's useVirtualizer returns the SAME Virtualizer class instance across renders (mutated in\n // place), unlike v2's useVirtual which returned a fresh object with a plain `virtualItems` array\n // property every render. Memoizing on `virtualListHelpers` itself (the stable instance) instead\n // of on the actual getVirtualItems() result means this would never recompute past the first\n // render \u2014 getVirtualItems() has its own internal memoization and returns a fresh array\n // reference only when the range/measurements genuinely change, so that's the correct dependency.\n const virtualItems = virtualListHelpers?.getVirtualItems();\n const { measureElement } = virtualListHelpers ?? {};\n\n return useMemo(() => {\n if (!virtualItems) return null;\n return virtualItems.map((vItem) => {\n const option = options[vItem.index];\n const generalProps = {\n wrapperStyles: { position: 'absolute', top: vItem.start, left: 0, width: '100%' } as React.CSSProperties,\n key: `${option.dsId}`,\n innerRef: measureElement,\n 'data-index': vItem.index,\n dsId: option.dsId,\n };\n const getOwnerPropsArguments = () => option;\n\n if (option.type === 'section') {\n return (\n <Section\n label={option.label}\n {...generalProps}\n options={option.options}\n handleClick={handleClick}\n focusOptionIdx={focusOptionIdx}\n />\n );\n }\n if (option.type === 'option') {\n return (\n <StyledMenuItemAction\n id={option.dsId}\n label={option.label}\n data-testid={AutocompleteDataTestid.OPTION}\n disabled={option.disabled}\n tabIndex={0}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => handleClick(option, e)}\n isActive={option.dsId === focusOptionIdx}\n aria-selected=\"false\"\n {...generalProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n );\n }\n return null;\n });\n }, [virtualItems, options, focusOptionIdx, measureElement, handleClick, getOwnerProps]);\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AC8Db;AA5DV,SAAgB,SAAS,YAAY,mBAAmB;AACxD,SAAS,yBAAyB;AAClC,SAAS,cAAc;AACvB,SAAS,oBAAoB,2BAA2B;AACxD,OAAO,yBAAyB;AAChC,SAAS,8BAA8B;AAEvC,SAAS,eAAe;AAExB,MAAM,uBAAuB,OAAO,mBAAmB;AAAA,EACrD,MAAM;AAAA,EACN,MAAM,oBAAoB;AAC5B,CAAC;AAEM,MAAM,kBAAkB,MAAM;AACnC,QAAM;AAAA,IACJ,OAAO,EAAE,SAAS;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,EACF,IAAI,WAAW,mBAAmB;AAElC,QAAM,cAAc;AAAA,IAClB,CAAC,QAAoC,MAA0C;AAC7E,eAAS,OAAO,OAAO,CAAC;AACxB,kCAA4B,IAAI;AAChC,4BAAsB,KAAK;AAC3B,QAAE,gBAAgB;AAClB,QAAE,eAAe;AAAA,IACnB;AAAA,IACA,CAAC,UAAU,uBAAuB,2BAA2B;AAAA,EAC/D;AAQA,QAAM,eAAe,oBAAoB,gBAAgB;AACzD,QAAM,EAAE,eAAe,IAAI,sBAAsB,CAAC;AAElD,SAAO,QAAQ,MAAM;AACnB,QAAI,CAAC,aAAc,QAAO;AAC1B,WAAO,aAAa,IAAI,CAAC,UAAU;AACjC,YAAM,SAAS,QAAQ,MAAM,KAAK;AAClC,YAAM,eAAe;AAAA,QACnB,eAAe,EAAE,UAAU,YAAY,KAAK,MAAM,OAAO,MAAM,GAAG,OAAO,OAAO;AAAA,QAChF,KAAK,GAAG,OAAO,IAAI;AAAA,QACnB,UAAU;AAAA,QACV,cAAc,MAAM;AAAA,QACpB,MAAM,OAAO;AAAA,MACf;AACA,YAAM,yBAAyB,MAAM;AAErC,UAAI,OAAO,SAAS,WAAW;AAC7B,eACE;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,OAAO;AAAA,YACb,GAAG;AAAA,YACJ,SAAS,OAAO;AAAA,YAChB;AAAA,YACA;AAAA;AAAA,QACF;AAAA,MAEJ;AACA,UAAI,OAAO,SAAS,UAAU;AAC5B,eACE;AAAA,UAAC;AAAA;AAAA,YACC,IAAI,OAAO;AAAA,YACX,OAAO,OAAO;AAAA,YACd,eAAa,uBAAuB;AAAA,YACpC,UAAU,OAAO;AAAA,YACjB,UAAU;AAAA,YACV,SAAS,CAAC,MAA0C,YAAY,QAAQ,CAAC;AAAA,YACzE,UAAU,OAAO,SAAS;AAAA,YAC1B,iBAAc;AAAA,YACb,GAAG;AAAA,YACJ;AAAA,YACA;AAAA;AAAA,QACF;AAAA,MAEJ;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,cAAc,SAAS,gBAAgB,gBAAgB,aAAa,aAAa,CAAC;AACxF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/react-desc-prop-types.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable no-unused-vars */\nimport type React from 'react';\nimport type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport type { DSHookFloatingContextT } from '@elliemae/ds-floating-context';\n\nimport { globalAttributesPropTypes, PropTypes, xstyledPropTypes } from '@elliemae/ds-props-helpers';\nimport type { DSInputTextT } from '@elliemae/ds-form-input-text';\n\nexport declare namespace DSAutocompleteT {\n export interface CommonItemOptions {\n dsId: string;\n disabled?: boolean;\n render?: React.ComponentType<unknown>;\n }\n\n export interface ItemSeparatorOptions extends CommonItemOptions {\n type: 'separator';\n }\n // this was never implemented?\n // packages/forms/ds-form-layout-autocomplete/src/parts/menu-list/useItemRenderer.tsx\n // only have code for ItemOption and ItemSectionOptions...\n // export interface ItemSeparatorOptions extends CommonItemOptions {\n // type: 'separator';\n // }\n\n export interface ItemSectionOptions extends CommonItemOptions {\n type: 'section';\n label: string;\n options: ItemOption[];\n }\n\n export interface ItemOption extends CommonItemOptions {\n value: string;\n label: string;\n type: 'option';\n }\n\n export type OptionTypes = ItemOption | ItemSectionOptions;\n\n export type SelectedOptionsT = ItemOption[] | ItemOption | null;\n\n export interface RequiredProps {\n options: OptionTypes[];\n filter: string;\n onSelect: (\n suggestedValue: string,\n e: React.KeyboardEvent<HTMLInputElement> | React.MouseEvent<HTMLInputElement>,\n ) => void;\n }\n export interface DefaultProps {\n startPlacementPreference: string;\n placementOrderPreference: DSHookFloatingContextT.PopperPlacementsT[];\n withoutPortal: boolean;\n zIndex: number;\n forceFocusFirstOptionOnType: boolean;\n }\n\n export interface OptionalProps {\n children?: React.ReactNode;\n inputProps?: DSInputTextT.Props;\n onValueChange?: (nextValue: string | number, e: React.ChangeEvent<HTMLInputElement>) => void;\n renderInput?: (props: React.InputHTMLAttributes<HTMLInputElement>) => React.ReactNode;\n isLoading?: boolean;\n isSkeleton?: boolean;\n }\n\n export interface Props\n extends Partial<DefaultProps>,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n\n export interface InternalProps\n extends DefaultProps,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n}\n\nexport const defaultProps: DSAutocompleteT.DefaultProps = {\n zIndex: 10,\n withoutPortal: false,\n forceFocusFirstOptionOnType: false,\n startPlacementPreference: 'bottom-start',\n placementOrderPreference: ['bottom-start', 'top-start'],\n};\n\nexport const DSAutocompletePropTypes: DSPropTypesSchema<DSAutocompleteT.Props> = {\n ...globalAttributesPropTypes,\n ...xstyledPropTypes,\n options: PropTypes.oneOfType([PropTypes.array, PropTypes.object])\n .isRequired.description('List of options')\n .defaultValue([]),\n filter: PropTypes.string.isRequired.description('String value to filter the options').defaultValue(''),\n onSelect: PropTypes.func.isRequired\n .description('function triggered when an option is selected it will send the options selected')\n .defaultValue(''),\n children: PropTypes.node\n .description('React component to apply autocomplete functionality')\n .defaultValue('')\n .deprecated({ version: '3.x' }),\n inputProps: PropTypes.object.description('Props to pass to the input component').defaultValue({}),\n onValueChange: PropTypes.func.description('Function triggered when the value of the input changes').defaultValue(''),\n renderInput: PropTypes.func.description('Function to render the input').defaultValue(''),\n isLoading: PropTypes.bool\n .description('Shows a loading spinner in the menu while options are being fetched.')\n .defaultValue('false'),\n isSkeleton: PropTypes.bool.description('Displays the menu in skeleton loading mode.').defaultValue('false'),\n startPlacementPreference: PropTypes.string\n .description('Ideal preference for context placement')\n .defaultValue('bottom-start'),\n placementOrderPreference: PropTypes.arrayOf(PropTypes.string)\n .description('Placement preferences for the context menu in order of most preferred to least preferred')\n .defaultValue(\"['bottom-start', 'bottom-end']\"),\n withoutPortal: PropTypes.bool\n .description('Whether to render the context menu in a react portal or next to the input DOM element')\n .defaultValue('false (next to the input)'),\n forceFocusFirstOptionOnType: PropTypes.bool\n .description(\n 'Whether to focus the first option when typing (NOT RECOMMENDED AND BREAKING A11Y STANDARDS + ALL USERS USABILITY)',\n )\n .defaultValue('false'),\n};\n\nexport const DSAutocompletePropTypesSchema = DSAutocompletePropTypes as unknown as ValidationMap<DSAutocompleteT.Props>;\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACMvB,SAAS,2BAA2B,WAAW,wBAAwB;AA4EhE,MAAM,eAA6C;AAAA,EACxD,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,6BAA6B;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,0BAA0B,CAAC,gBAAgB,WAAW;AACxD;AAEO,MAAM,0BAAoE;AAAA,EAC/E,GAAG;AAAA,EACH,GAAG;AAAA,EACH,SAAS,UAAU,UAAU,CAAC,UAAU,OAAO,UAAU,MAAM,CAAC,EAC7D,WAAW,YAAY,iBAAiB,EACxC,aAAa,CAAC,CAAC;AAAA,EAClB,QAAQ,UAAU,OAAO,WAAW,YAAY,oCAAoC,EAAE,aAAa,EAAE;AAAA,EACrG,UAAU,UAAU,KAAK,WACtB,YAAY,iFAAiF,EAC7F,aAAa,EAAE;AAAA,EAClB,UAAU,UAAU,KACjB,YAAY,qDAAqD,EACjE,aAAa,EAAE,EACf,WAAW,EAAE,SAAS,MAAM,CAAC;AAAA,EAChC,YAAY,UAAU,OAAO,YAAY,sCAAsC,EAAE,aAAa,CAAC,CAAC;AAAA,EAChG,eAAe,UAAU,KAAK,YAAY,wDAAwD,EAAE,aAAa,EAAE;AAAA,EACnH,aAAa,UAAU,KAAK,YAAY,8BAA8B,EAAE,aAAa,EAAE;AAAA,EACvF,WAAW,UAAU,KAClB,YAAY,sEAAsE,EAClF,aAAa,OAAO;AAAA,EACvB,YAAY,UAAU,KAAK,YAAY,6CAA6C,EAAE,aAAa,OAAO;AAAA,EAC1G,0BAA0B,UAAU,OACjC,YAAY,wCAAwC,EACpD,aAAa,cAAc;AAAA,EAC9B,0BAA0B,UAAU,QAAQ,UAAU,MAAM,EACzD,YAAY,0FAA0F,EACtG,aAAa,gCAAgC;AAAA,EAChD,eAAe,UAAU,KACtB,YAAY,uFAAuF,EACnG,aAAa,2BAA2B;AAAA,EAC3C,6BAA6B,UAAU,KACpC;AAAA,IACC;AAAA,EACF,EACC,aAAa,OAAO;AACzB;AAEO,MAAM,gCAAgC;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-unused-vars */\n/* eslint-disable no-unused-vars */\nimport type React from 'react';\nimport type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport type { DSHookFloatingContextT } from '@elliemae/ds-floating-context';\n\nimport { globalAttributesPropTypes, PropTypes, xstyledPropTypes } from '@elliemae/ds-props-helpers';\nimport type { DSInputTextT } from '@elliemae/ds-form-input-text';\n\nexport declare namespace DSAutocompleteT {\n export interface CommonItemOptions {\n dsId: string;\n disabled?: boolean;\n render?: React.ComponentType<unknown>;\n }\n\n export interface ItemSeparatorOptions extends CommonItemOptions {\n type: 'separator';\n }\n // this was never implemented?\n // packages/forms/ds-form-layout-autocomplete/src/parts/menu-list/useItemRenderer.tsx\n // only have code for ItemOption and ItemSectionOptions...\n // export interface ItemSeparatorOptions extends CommonItemOptions {\n // type: 'separator';\n // }\n\n export interface ItemSectionOptions extends CommonItemOptions {\n type: 'section';\n label: string;\n options: ItemOption[];\n }\n\n export interface ItemOption extends CommonItemOptions {\n value: string;\n label: string;\n type: 'option';\n }\n\n export type OptionTypes = ItemOption | ItemSectionOptions;\n\n export type SelectedOptionsT = ItemOption[] | ItemOption | null;\n\n export interface RequiredProps {\n options: OptionTypes[];\n filter: string;\n onSelect: (\n suggestedValue: string,\n e: React.KeyboardEvent<HTMLInputElement> | React.MouseEvent<HTMLInputElement>,\n ) => void;\n }\n export interface DefaultProps {\n startPlacementPreference: string;\n placementOrderPreference: DSHookFloatingContextT.PopperPlacementsT[];\n withoutPortal: boolean;\n zIndex: number;\n forceFocusFirstOptionOnType: boolean;\n }\n\n export interface OptionalProps {\n children?: React.ReactNode;\n inputProps?: DSInputTextT.Props;\n onValueChange?: (nextValue: string | number, e: React.ChangeEvent<HTMLInputElement>) => void;\n renderInput?: (props: React.InputHTMLAttributes<HTMLInputElement>) => React.ReactNode;\n isLoading?: boolean;\n isSkeleton?: boolean;\n }\n\n export interface Props\n extends\n Partial<DefaultProps>,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n\n export interface InternalProps\n extends\n DefaultProps,\n RequiredProps,\n OptionalProps,\n Omit<GlobalAttributesT, keyof DefaultProps | keyof RequiredProps | keyof OptionalProps>,\n Omit<XstyledProps, keyof DefaultProps> {}\n}\n\nexport const defaultProps: DSAutocompleteT.DefaultProps = {\n zIndex: 10,\n withoutPortal: false,\n forceFocusFirstOptionOnType: false,\n startPlacementPreference: 'bottom-start',\n placementOrderPreference: ['bottom-start', 'top-start'],\n};\n\nexport const DSAutocompletePropTypes: DSPropTypesSchema<DSAutocompleteT.Props> = {\n ...globalAttributesPropTypes,\n ...xstyledPropTypes,\n options: PropTypes.oneOfType([PropTypes.array, PropTypes.object])\n .isRequired.description('List of options')\n .defaultValue([]),\n filter: PropTypes.string.isRequired.description('String value to filter the options').defaultValue(''),\n onSelect: PropTypes.func.isRequired\n .description('function triggered when an option is selected it will send the options selected')\n .defaultValue(''),\n children: PropTypes.node\n .description('React component to apply autocomplete functionality')\n .defaultValue('')\n .deprecated({ version: '3.x' }),\n inputProps: PropTypes.object.description('Props to pass to the input component').defaultValue({}),\n onValueChange: PropTypes.func.description('Function triggered when the value of the input changes').defaultValue(''),\n renderInput: PropTypes.func.description('Function to render the input').defaultValue(''),\n isLoading: PropTypes.bool\n .description('Shows a loading spinner in the menu while options are being fetched.')\n .defaultValue('false'),\n isSkeleton: PropTypes.bool.description('Displays the menu in skeleton loading mode.').defaultValue('false'),\n startPlacementPreference: PropTypes.string\n .description('Ideal preference for context placement')\n .defaultValue('bottom-start'),\n placementOrderPreference: PropTypes.arrayOf(PropTypes.string)\n .description('Placement preferences for the context menu in order of most preferred to least preferred')\n .defaultValue(\"['bottom-start', 'bottom-end']\"),\n withoutPortal: PropTypes.bool\n .description('Whether to render the context menu in a react portal or next to the input DOM element')\n .defaultValue('false (next to the input)'),\n forceFocusFirstOptionOnType: PropTypes.bool\n .description(\n 'Whether to focus the first option when typing (NOT RECOMMENDED AND BREAKING A11Y STANDARDS + ALL USERS USABILITY)',\n )\n .defaultValue('false'),\n};\n\nexport const DSAutocompletePropTypesSchema = DSAutocompletePropTypes as unknown as ValidationMap<DSAutocompleteT.Props>;\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACMvB,SAAS,2BAA2B,WAAW,wBAAwB;AA8EhE,MAAM,eAA6C;AAAA,EACxD,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,6BAA6B;AAAA,EAC7B,0BAA0B;AAAA,EAC1B,0BAA0B,CAAC,gBAAgB,WAAW;AACxD;AAEO,MAAM,0BAAoE;AAAA,EAC/E,GAAG;AAAA,EACH,GAAG;AAAA,EACH,SAAS,UAAU,UAAU,CAAC,UAAU,OAAO,UAAU,MAAM,CAAC,EAC7D,WAAW,YAAY,iBAAiB,EACxC,aAAa,CAAC,CAAC;AAAA,EAClB,QAAQ,UAAU,OAAO,WAAW,YAAY,oCAAoC,EAAE,aAAa,EAAE;AAAA,EACrG,UAAU,UAAU,KAAK,WACtB,YAAY,iFAAiF,EAC7F,aAAa,EAAE;AAAA,EAClB,UAAU,UAAU,KACjB,YAAY,qDAAqD,EACjE,aAAa,EAAE,EACf,WAAW,EAAE,SAAS,MAAM,CAAC;AAAA,EAChC,YAAY,UAAU,OAAO,YAAY,sCAAsC,EAAE,aAAa,CAAC,CAAC;AAAA,EAChG,eAAe,UAAU,KAAK,YAAY,wDAAwD,EAAE,aAAa,EAAE;AAAA,EACnH,aAAa,UAAU,KAAK,YAAY,8BAA8B,EAAE,aAAa,EAAE;AAAA,EACvF,WAAW,UAAU,KAClB,YAAY,sEAAsE,EAClF,aAAa,OAAO;AAAA,EACvB,YAAY,UAAU,KAAK,YAAY,6CAA6C,EAAE,aAAa,OAAO;AAAA,EAC1G,0BAA0B,UAAU,OACjC,YAAY,wCAAwC,EACpD,aAAa,cAAc;AAAA,EAC9B,0BAA0B,UAAU,QAAQ,UAAU,MAAM,EACzD,YAAY,0FAA0F,EACtG,aAAa,gCAAgC;AAAA,EAChD,eAAe,UAAU,KACtB,YAAY,uFAAuF,EACnG,aAAa,2BAA2B;AAAA,EAC3C,6BAA6B,UAAU,KACpC;AAAA,IACC;AAAA,EACF,EACC,aAAa,OAAO;AACzB;AAEO,MAAM,gCAAgC;",
6
6
  "names": []
7
7
  }
@@ -1,10 +1,10 @@
1
- import { useVirtual, type ScrollToIndexOptions } from 'react-virtual';
1
+ import { useVirtualizer, type ScrollToOptions } from '@tanstack/react-virtual';
2
2
  import type { DSAutocompleteT } from '../react-desc-prop-types.js';
3
3
  import type { useRefs } from './useRefs.js';
4
4
  export declare const useVirtualization: ({ references, options, }: {
5
5
  references: ReturnType<typeof useRefs>;
6
6
  options: DSAutocompleteT.InternalProps["options"];
7
7
  }) => {
8
- virtualListHelpers: ReturnType<typeof useVirtual>;
9
- scrollOptionIntoView: (dsId: string, opts?: ScrollToIndexOptions) => void;
8
+ virtualListHelpers: ReturnType<typeof useVirtualizer<HTMLDivElement, HTMLElement>>;
9
+ scrollOptionIntoView: (dsId: string, opts?: ScrollToOptions) => void;
10
10
  };
@@ -3,7 +3,8 @@ import type { DSAutocompleteT } from '../react-desc-prop-types.js';
3
3
  interface SectionProps {
4
4
  label: string;
5
5
  wrapperStyles: React.CSSProperties;
6
- innerRef: (instance: HTMLDivElement | null) => void;
6
+ innerRef: (instance: HTMLElement | null) => void;
7
+ 'data-index': number;
7
8
  options: DSAutocompleteT.ItemOption[];
8
9
  handleClick: (option: DSAutocompleteT.ItemOption, e: React.MouseEvent<HTMLInputElement>) => void;
9
10
  focusOptionIdx: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-form-layout-autocomplete",
3
- "version": "3.80.0-next.13",
3
+ "version": "3.80.0-next.18",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Form Layout - Autocomplete",
6
6
  "files": [
@@ -36,25 +36,25 @@
36
36
  "indent": 4
37
37
  },
38
38
  "dependencies": {
39
- "react-virtual": "~2.10.4",
40
- "uid": "^2.0.2",
41
- "@elliemae/ds-floating-context": "3.80.0-next.13",
42
- "@elliemae/ds-grid": "3.80.0-next.13",
43
- "@elliemae/ds-circular-progress-indicator": "3.80.0-next.13",
44
- "@elliemae/ds-props-helpers": "3.80.0-next.13",
45
- "@elliemae/ds-system": "3.80.0-next.13",
46
- "@elliemae/ds-menu-items": "3.80.0-next.13",
47
- "@elliemae/ds-typography": "3.80.0-next.13",
48
- "@elliemae/ds-form-input-text": "3.80.0-next.13"
39
+ "@elliemae/ds-circular-progress-indicator": "3.80.0-next.18",
40
+ "@elliemae/ds-floating-context": "3.80.0-next.18",
41
+ "@elliemae/ds-form-input-text": "3.80.0-next.18",
42
+ "@elliemae/ds-grid": "3.80.0-next.18",
43
+ "@elliemae/ds-menu-items": "3.80.0-next.18",
44
+ "@elliemae/ds-props-helpers": "3.80.0-next.18",
45
+ "@elliemae/ds-system": "3.80.0-next.18",
46
+ "@elliemae/ds-typography": "3.80.0-next.18",
47
+ "@tanstack/react-virtual": "3.13.18",
48
+ "uid": "^2.0.2"
49
49
  },
50
50
  "devDependencies": {
51
+ "@elliemae/ds-form-input-text": "3.80.0-next.18",
52
+ "@elliemae/ds-monorepo-devops": "3.80.0-next.18",
53
+ "@elliemae/ds-test-utils": "3.80.0-next.18",
51
54
  "@elliemae/pui-theme": "~2.13.0",
52
55
  "jest": "^30.0.0",
53
56
  "styled-components": "~5.3.11",
54
- "styled-system": "^5.1.5",
55
- "@elliemae/ds-monorepo-devops": "3.80.0-next.13",
56
- "@elliemae/ds-form-input-text": "3.80.0-next.13",
57
- "@elliemae/ds-test-utils": "3.80.0-next.13"
57
+ "styled-system": "^5.1.5"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@elliemae/pui-theme": "~2.13.0",