@synerise/ds-context-selector 2.0.4 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [2.1.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-context-selector@2.0.5...@synerise/ds-context-selector@2.1.0) (2026-09-16)
7
+
8
+ ### Features
9
+
10
+ - **list-item:** add content-fitting size="auto" ([e269f0a](https://github.com/Synerise/synerise-design/commit/e269f0a48ca7472f4766a50a957e22d7988c5e13))
11
+
12
+ ## [2.0.5](https://github.com/Synerise/synerise-design/compare/@synerise/ds-context-selector@2.0.4...@synerise/ds-context-selector@2.0.5) (2026-09-10)
13
+
14
+ **Note:** Version bump only for package @synerise/ds-context-selector
15
+
6
16
  ## [2.0.4](https://github.com/Synerise/synerise-design/compare/@synerise/ds-context-selector@2.0.3...@synerise/ds-context-selector@2.0.4) (2026-09-08)
7
17
 
8
18
  **Note:** Version bump only for package @synerise/ds-context-selector
@@ -10,15 +10,16 @@ import { itemSizes, ListContextProvider } from "@synerise/ds-list-item";
10
10
  import Result from "@synerise/ds-result";
11
11
  import Scrollbar from "@synerise/ds-scrollbar";
12
12
  import Tabs from "@synerise/ds-tabs";
13
- import { getActiveTabGroup, getGroupName, useSearchResults, focusWithArrowKeys } from "@synerise/ds-utils";
13
+ import { getActiveTabGroup, getGroupName, useSearchResults, useMeasuredRowHeights, focusWithArrowKeys, useMeasuredRow } from "@synerise/ds-utils";
14
14
  import { ShowMoreItem, TabsWrapper, Skeleton, ItemsList, Title } from "../ContextSelector.styles.js";
15
15
  import { isContextItemsInSubGroup } from "../ContextSelector.types.js";
16
16
  import { NO_GROUP_NAME, DROPDOWN_HEIGHT } from "../constants.js";
17
17
  import ContextSelectorDropdownItem from "./ContextSelectorDropdownItem.js";
18
- import { isGroup, getDropdownContentHeight, getListWindowHeight, isListTitle } from "./utils.js";
18
+ import { isGroup, isListTitle, getDropdownContentHeight, getListWindowHeight } from "./utils.js";
19
19
  const ITEM_SIZE = {
20
20
  [itemSizes.LARGE]: 50,
21
21
  [itemSizes.DEFAULT]: 32,
22
+ [itemSizes.AUTO]: 32,
22
23
  title: 32,
23
24
  divider: 16
24
25
  };
@@ -32,10 +33,16 @@ const VirtualizedRow = ({
32
33
  data
33
34
  }) => {
34
35
  const item = data.items[index];
36
+ const rowRef = useMeasuredRow(index, data.measureRow);
37
+ const rowStyle = {
38
+ ...style,
39
+ height: "auto",
40
+ minHeight: style.height
41
+ };
35
42
  if (item && isDivider(item)) {
36
- return /* @__PURE__ */ jsx("div", { style, children: /* @__PURE__ */ jsx(Divider, { marginTop: 8, marginBottom: 8 }) });
43
+ return /* @__PURE__ */ jsx("div", { ref: rowRef, style: rowStyle, children: /* @__PURE__ */ jsx(Divider, { marginTop: 8, marginBottom: 8 }) });
37
44
  }
38
- return item && isListTitle(item) ? /* @__PURE__ */ jsx(Title, { style, children: item.title }) : /* @__PURE__ */ jsx(ContextSelectorDropdownItem, { style, ...item });
45
+ return /* @__PURE__ */ jsx("div", { ref: rowRef, style: rowStyle, children: item && isListTitle(item) ? /* @__PURE__ */ jsx(Title, { children: item.title }) : /* @__PURE__ */ jsx(ContextSelectorDropdownItem, { ...item }) });
39
46
  };
40
47
  const ContextSelectorDropdown = ({
41
48
  texts,
@@ -66,8 +73,8 @@ const ContextSelectorDropdown = ({
66
73
  const defaultIndex = groups?.findIndex((group) => group.defaultGroup);
67
74
  return defaultIndex || 0;
68
75
  }, [groups]);
69
- const listRef = useRef(null);
70
76
  const overlayRef = useRef(null);
77
+ const listRef = useRef(null);
71
78
  const scrollBarRef = useRef(null);
72
79
  const topSectionRef = useRef(null);
73
80
  const [searchInputHandle, setSearchInputHandle] = useState();
@@ -231,9 +238,39 @@ const ContextSelectorDropdown = ({
231
238
  useEffect(() => {
232
239
  listRef.current?.resetAfterIndex(0, false);
233
240
  }, [activeItems, listRef]);
234
- const itemData = useMemo(() => ({
235
- items: activeItems
241
+ const getEstimatedItemSize = useCallback((index) => {
242
+ const item = activeItems[index];
243
+ if (isListTitle(item)) {
244
+ return ITEM_SIZE.title;
245
+ }
246
+ if (isDivider(item)) {
247
+ return ITEM_SIZE.divider;
248
+ }
249
+ return menuItemHeight ? ITEM_SIZE[menuItemHeight] : ITEM_SIZE[itemSizes.DEFAULT];
250
+ }, [activeItems, menuItemHeight]);
251
+ const rowKeys = useMemo(() => activeItems.map((item, index) => {
252
+ if (isListTitle(item)) {
253
+ return `title-${item.title}`;
254
+ }
255
+ if (isDivider(item)) {
256
+ return `divider-${index}`;
257
+ }
258
+ return `${item.item.name}-${item.item.id}`;
236
259
  }), [activeItems]);
260
+ const {
261
+ getItemSize,
262
+ measureRow
263
+ } = useMeasuredRowHeights({
264
+ listRef,
265
+ keys: rowKeys,
266
+ estimate: getEstimatedItemSize,
267
+ // A changed `menuItemHeight` invalidates every measurement taken against the old one.
268
+ estimateVersion: menuItemHeight
269
+ });
270
+ const itemData = useMemo(() => ({
271
+ items: activeItems,
272
+ measureRow
273
+ }), [activeItems, measureRow]);
237
274
  const handleSearch = useCallback((val) => {
238
275
  setSearchQuery(val);
239
276
  resetList();
@@ -256,16 +293,6 @@ const ContextSelectorDropdown = ({
256
293
  listRef.current.scrollTo(Math.max(0, scrollTop - topSectionHeight));
257
294
  }
258
295
  };
259
- const getItemSize = (index) => {
260
- const item = activeItems[index];
261
- if (isListTitle(item)) {
262
- return ITEM_SIZE.title;
263
- }
264
- if (isDivider(item)) {
265
- return ITEM_SIZE.divider;
266
- }
267
- return menuItemHeight ? ITEM_SIZE[menuItemHeight] : ITEM_SIZE[itemSizes.DEFAULT];
268
- };
269
296
  const dropdownContentHeight = useMemo(() => {
270
297
  return getDropdownContentHeight({
271
298
  outerHeight,
@@ -1,7 +1,11 @@
1
+ import { ItemSize } from '@synerise/ds-list-item';
1
2
  export declare const NO_GROUP_NAME = "NO_GROUP_NAME";
2
- export declare const ITEM_SIZE: {
3
- large: number;
4
- default: number;
3
+ /**
4
+ * Row height estimates. Typed exhaustively over `ItemSize` on purpose: a new size must fail
5
+ * the build here rather than silently yield an undefined row offset. `auto` has no fixed
6
+ * height — 32 is its `min-height` floor, and the real height is measured.
7
+ */
8
+ export declare const ITEM_SIZE: Record<ItemSize, number> & {
5
9
  title: number;
6
10
  };
7
11
  export declare const DROPDOWN_HEIGHT = 420;
package/dist/constants.js CHANGED
@@ -3,6 +3,7 @@ const NO_GROUP_NAME = "NO_GROUP_NAME";
3
3
  const ITEM_SIZE = {
4
4
  [itemSizes.LARGE]: 50,
5
5
  [itemSizes.DEFAULT]: 32,
6
+ [itemSizes.AUTO]: 32,
6
7
  title: 32
7
8
  };
8
9
  const DROPDOWN_HEIGHT = 420;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-context-selector",
3
- "version": "2.0.4",
3
+ "version": "2.1.0",
4
4
  "description": "ContextSelector UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -36,26 +36,26 @@
36
36
  "check:circular-dependencies": "madge --circular --extensions ts,tsx,js,jsx --ts-config tsconfig.json src/ --exclude '/dist/'",
37
37
  "upgrade:ds": "ncu -f \"@synerise/ds-*\" -u"
38
38
  },
39
- "gitHead": "e0dced8fd3a1194eff2fafc1b7e385b29238af88",
39
+ "gitHead": "71f08fdaba8e7b80731e29a03883dc6a33f2b9db",
40
40
  "sideEffects": [
41
41
  "dist/style/*",
42
42
  "*.less"
43
43
  ],
44
44
  "types": "dist/index.d.ts",
45
45
  "dependencies": {
46
- "@synerise/ds-button": "^2.0.2",
47
- "@synerise/ds-divider": "^2.0.2",
48
- "@synerise/ds-dropdown": "^2.0.3",
49
- "@synerise/ds-factors": "^2.0.4",
50
- "@synerise/ds-icon": "^2.1.0",
51
- "@synerise/ds-information-card": "^2.0.4",
52
- "@synerise/ds-list-item": "^2.0.2",
46
+ "@synerise/ds-button": "^2.0.3",
47
+ "@synerise/ds-divider": "^2.0.3",
48
+ "@synerise/ds-dropdown": "^2.1.0",
49
+ "@synerise/ds-factors": "^2.1.0",
50
+ "@synerise/ds-icon": "^2.1.1",
51
+ "@synerise/ds-information-card": "^2.0.6",
52
+ "@synerise/ds-list-item": "^2.1.0",
53
53
  "@synerise/ds-popover": "^2.0.1",
54
- "@synerise/ds-result": "^2.0.2",
55
- "@synerise/ds-scrollbar": "^2.0.1",
56
- "@synerise/ds-skeleton": "^2.0.2",
57
- "@synerise/ds-tabs": "^2.0.3",
58
- "@synerise/ds-utils": "^2.0.0",
54
+ "@synerise/ds-result": "^2.0.3",
55
+ "@synerise/ds-scrollbar": "^2.0.2",
56
+ "@synerise/ds-skeleton": "^2.0.3",
57
+ "@synerise/ds-tabs": "^2.0.4",
58
+ "@synerise/ds-utils": "^2.1.0",
59
59
  "react-intl": "^6.8.7",
60
60
  "react-window": "1.8.11",
61
61
  "uuid": "^8.3.2"