@strapi/ui-primitives 2.0.0-rc.8 → 2.0.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.
@@ -35,6 +35,25 @@ interface RootProps {
35
35
  filterValue?: string;
36
36
  onFilterValueChange?(value: string): void;
37
37
  isPrintableCharacter?: (str: string) => boolean;
38
+ visible?: boolean;
39
+ /**
40
+ * Enable virtualization for large lists
41
+ * - true: always virtualize
42
+ * - false: never virtualize
43
+ * - 'auto': virtualize when item count > 100 (default)
44
+ * @default 'auto'
45
+ */
46
+ virtualized?: boolean | 'auto';
47
+ /**
48
+ * Estimated size of each virtualized item in pixels
49
+ * @default 40
50
+ */
51
+ estimatedItemSize?: number;
52
+ /**
53
+ * Number of items to render outside visible area for smooth scrolling
54
+ * @default 5
55
+ */
56
+ overscan?: number;
38
57
  }
39
58
  type TriggerProps = PrimitiveDivProps;
40
59
  type TextInputProps = React.InputHTMLAttributes<HTMLInputElement>;
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { Primitive } from '@radix-ui/react-primitive';
3
+ import type { ComponentPropsWithoutRef } from '@radix-ui/react-primitive';
4
+ /**
5
+ * VirtualizedViewport - Renders only visible items for performance optimization
6
+ * Used when Combobox/Select has many items (>100)
7
+ */
8
+ type VirtualizedViewportElement = React.ElementRef<typeof Primitive.div>;
9
+ type PrimitiveDivProps = ComponentPropsWithoutRef<typeof Primitive.div>;
10
+ export interface VirtualizedViewportProps extends PrimitiveDivProps {
11
+ /**
12
+ * Estimated size of each item in pixels
13
+ * @default 40
14
+ */
15
+ estimatedItemSize?: number;
16
+ /**
17
+ * Number of items to render outside the visible area
18
+ * @default 5
19
+ */
20
+ overscan?: number;
21
+ /**
22
+ * Callback to get the count of total items
23
+ */
24
+ getItemCount: () => number;
25
+ /**
26
+ * Callback when viewport ref changes
27
+ */
28
+ onViewportChange?: (node: VirtualizedViewportElement | null) => void;
29
+ }
30
+ export declare const VirtualizedViewport: React.ForwardRefExoticComponent<VirtualizedViewportProps & React.RefAttributes<HTMLDivElement>>;
31
+ export {};
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ const reactUseLayoutEffect = require("@radix-ui/react-use-layout-effect");
18
18
  const ariaHidden = require("aria-hidden");
19
19
  const ReactDOM = require("react-dom");
20
20
  const reactRemoveScroll = require("react-remove-scroll");
21
+ const reactVirtual = require("@tanstack/react-virtual");
21
22
  const number = require("@radix-ui/number");
22
23
  const reactCollection = require("@radix-ui/react-collection");
23
24
  const reactDirection = require("@radix-ui/react-direction");
@@ -173,6 +174,75 @@ const usePrev = (value) => {
173
174
  });
174
175
  return ref.current;
175
176
  };
177
+ const VirtualizedViewport = React__namespace.forwardRef(
178
+ ({ children, estimatedItemSize = 40, overscan = 5, getItemCount, onViewportChange, ...props }, forwardedRef) => {
179
+ const parentRef = React__namespace.useRef(null);
180
+ const composedRefs = reactComposeRefs.useComposedRefs(forwardedRef, parentRef, onViewportChange);
181
+ const childArray = React__namespace.useMemo(() => React__namespace.Children.toArray(children), [children]);
182
+ const virtualizer = reactVirtual.useVirtualizer({
183
+ count: getItemCount(),
184
+ getScrollElement: () => parentRef.current,
185
+ estimateSize: () => estimatedItemSize,
186
+ overscan
187
+ });
188
+ const virtualItems = virtualizer.getVirtualItems();
189
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
190
+ /* @__PURE__ */ jsxRuntime.jsx(
191
+ "style",
192
+ {
193
+ dangerouslySetInnerHTML: {
194
+ __html: `[data-radix-combobox-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-combobox-viewport]::-webkit-scrollbar{display:none}`
195
+ }
196
+ }
197
+ ),
198
+ /* @__PURE__ */ jsxRuntime.jsx(
199
+ reactPrimitive.Primitive.div,
200
+ {
201
+ "data-radix-combobox-viewport": "",
202
+ role: "presentation",
203
+ ...props,
204
+ ref: composedRefs,
205
+ style: {
206
+ position: "relative",
207
+ flex: 1,
208
+ overflow: "auto",
209
+ ...props.style
210
+ },
211
+ children: /* @__PURE__ */ jsxRuntime.jsx(
212
+ "div",
213
+ {
214
+ style: {
215
+ height: `${virtualizer.getTotalSize()}px`,
216
+ width: "100%",
217
+ position: "relative"
218
+ },
219
+ children: virtualItems.map((virtualItem) => {
220
+ const child = childArray[virtualItem.index];
221
+ return /* @__PURE__ */ jsxRuntime.jsx(
222
+ "div",
223
+ {
224
+ "data-index": virtualItem.index,
225
+ ref: virtualizer.measureElement,
226
+ style: {
227
+ position: "absolute",
228
+ top: 0,
229
+ left: 0,
230
+ width: "100%",
231
+ transform: `translateY(${virtualItem.start}px)`
232
+ },
233
+ children: child
234
+ },
235
+ virtualItem.key
236
+ );
237
+ })
238
+ }
239
+ )
240
+ }
241
+ )
242
+ ] });
243
+ }
244
+ );
245
+ VirtualizedViewport.displayName = "VirtualizedViewport";
176
246
  const OPEN_KEYS$1 = [" ", "Enter", "ArrowUp", "ArrowDown"];
177
247
  const SELECTION_KEYS$1 = ["Enter"];
178
248
  const defaultIsPrintableCharacter = (str) => {
@@ -217,7 +287,11 @@ const Combobox = (props) => {
217
287
  filterValue: filterValueProp,
218
288
  defaultFilterValue,
219
289
  onFilterValueChange,
220
- isPrintableCharacter = defaultIsPrintableCharacter
290
+ isPrintableCharacter = defaultIsPrintableCharacter,
291
+ visible = false,
292
+ virtualized = "auto",
293
+ estimatedItemSize = 40,
294
+ overscan = 5
221
295
  } = props;
222
296
  const [trigger, setTrigger] = React__namespace.useState(null);
223
297
  const [viewport, setViewport] = React__namespace.useState(null);
@@ -306,6 +380,10 @@ const Combobox = (props) => {
306
380
  onFilterValueChange: setFilterValue,
307
381
  onVisuallyFocussedItemChange: setVisuallyFocussedItem,
308
382
  isPrintableCharacter,
383
+ visible,
384
+ virtualized,
385
+ estimatedItemSize,
386
+ overscan,
309
387
  children
310
388
  }
311
389
  ) });
@@ -713,6 +791,22 @@ const VIEWPORT_NAME$1 = "ComboboxViewport";
713
791
  const ComboboxViewport = React__namespace.forwardRef((props, forwardedRef) => {
714
792
  const comboboxContext = useComboboxContext(VIEWPORT_NAME$1);
715
793
  const composedRefs = reactComposeRefs.useComposedRefs(forwardedRef, comboboxContext.onViewportChange);
794
+ const childArray = React__namespace.useMemo(() => React__namespace.Children.toArray(props.children), [props.children]);
795
+ const itemCount = childArray.length;
796
+ const shouldVirtualize = comboboxContext.virtualized === true || comboboxContext.virtualized === "auto" && itemCount > 100;
797
+ if (shouldVirtualize) {
798
+ return /* @__PURE__ */ jsxRuntime.jsx(Collection$1.Slot, { scope: void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
799
+ VirtualizedViewport,
800
+ {
801
+ ...props,
802
+ ref: composedRefs,
803
+ getItemCount: () => itemCount,
804
+ estimatedItemSize: comboboxContext.estimatedItemSize,
805
+ overscan: comboboxContext.overscan,
806
+ onViewportChange: comboboxContext.onViewportChange
807
+ }
808
+ ) });
809
+ }
716
810
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
717
811
  /* @__PURE__ */ jsxRuntime.jsx(
718
812
  "style",
@@ -873,23 +967,31 @@ const ComboboxItemIndicator = React__namespace.forwardRef((props, forwardedRef)
873
967
  ComboboxItemIndicator.displayName = ITEM_INDICATOR_NAME$1;
874
968
  const NO_VALUE_FOUND_NAME = "ComboboxNoValueFound";
875
969
  const ComboboxNoValueFound = React__namespace.forwardRef((props, ref) => {
876
- const { textValue = "", filterValue = "", locale, autocomplete } = useComboboxContext(NO_VALUE_FOUND_NAME);
970
+ const {
971
+ textValue = "",
972
+ filterValue = "",
973
+ visible = false,
974
+ locale,
975
+ autocomplete
976
+ } = useComboboxContext(NO_VALUE_FOUND_NAME);
877
977
  const [items, setItems] = React__namespace.useState([]);
878
978
  const { subscribe } = useCollection$1(void 0);
879
979
  const { startsWith, contains } = useFilter(locale, { sensitivity: "base" });
880
980
  React__namespace.useEffect(() => {
881
981
  const unsub = subscribe((state) => {
882
- setItems(state);
982
+ if (visible) {
983
+ const filteredItems = state.filter((item) => item.type !== "create");
984
+ setItems(filteredItems);
985
+ } else {
986
+ setItems(state);
987
+ }
883
988
  });
884
989
  return () => {
885
990
  unsub();
886
991
  };
887
- }, [subscribe]);
888
- if (items.length === 0)
992
+ }, [visible, subscribe]);
993
+ if (autocomplete.type === "none" && items.length > 0)
889
994
  return null;
890
- if (autocomplete.type === "none") {
891
- return null;
892
- }
893
995
  if (autocomplete.type === "list" && autocomplete.filter === "startsWith" && items.some((item) => startsWith(item.textValue, textValue))) {
894
996
  return null;
895
997
  }
@@ -936,7 +1038,7 @@ const ComboboxCreateItem = React__namespace.forwardRef((props, ref) => {
936
1038
  unsub();
937
1039
  };
938
1040
  }, [textValue, subscribe, getItems]);
939
- if (!textValue || !show) {
1041
+ if ((!textValue || !show) && !context.visible) {
940
1042
  return null;
941
1043
  }
942
1044
  return /* @__PURE__ */ jsxRuntime.jsx(