@keboola/design 8.0.0 → 9.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/dist/index.cjs CHANGED
@@ -28,6 +28,7 @@ var reactSlider = require('@radix-ui/react-slider');
28
28
  var SelectPrimitive6 = require('@radix-ui/react-select');
29
29
  var reactCheckbox = require('@radix-ui/react-checkbox');
30
30
  var combobox = require('@base-ui/react/combobox');
31
+ var reactVirtual = require('@tanstack/react-virtual');
31
32
  var inputOtp = require('input-otp');
32
33
  var ReactMarkdown = require('react-markdown');
33
34
  var remarkGfm = require('remark-gfm');
@@ -12433,9 +12434,9 @@ var ComboboxItemIndicator = ({ ref, className, variant = "check", ...props }) =>
12433
12434
  "tw:absolute",
12434
12435
  "tw:right-2",
12435
12436
  "tw:group-data-[variant=default]/combobox-content:right-4",
12436
- // Lucide SVGs default to 24×24 — constrain the rendered child so the
12437
- // 16px indicator slot owns the actual size.
12438
- 'tw:[&_svg:not([class*="tw:size-"])]:size-4',
12437
+ // `render={<Check />}` makes the icon the indicator element itself (not a descendant), so a
12438
+ // `[&_svg]` rule never matches it — size it directly or Lucide renders at its 24px default.
12439
+ "tw:size-4",
12439
12440
  "tw:group-data-[highlighted]/combobox-item:text-accent-foreground",
12440
12441
  "tw:group-data-[variant=default]/combobox-content:group-data-[highlighted]/combobox-item:text-foreground",
12441
12442
  className
@@ -12560,6 +12561,9 @@ var ComboboxGroupLabel = ({ ref, className, ...props }) => /* @__PURE__ */ jsxRu
12560
12561
  }
12561
12562
  );
12562
12563
  var ComboboxCollection = ({ ...props }) => /* @__PURE__ */ jsxRuntime.jsx(combobox.Combobox.Collection, { "data-slot": "combobox-collection", ...props });
12564
+ var VIRTUALIZE_THRESHOLD = 30;
12565
+ var ESTIMATED_ROW_HEIGHT = 40;
12566
+ var ESTIMATED_LIST_HEIGHT = 320;
12563
12567
  var Combobox = ({
12564
12568
  // root props
12565
12569
  items: _itemsProp,
@@ -12586,6 +12590,7 @@ var Combobox = ({
12586
12590
  empty = "No items",
12587
12591
  indicator,
12588
12592
  prefix,
12593
+ virtualized = false,
12589
12594
  // creatable
12590
12595
  allowCreate,
12591
12596
  queryToCreateValue,
@@ -12609,7 +12614,9 @@ var Combobox = ({
12609
12614
  const label = value === null ? "" : itemToStringLabelProp?.(value) ?? toLabel(value);
12610
12615
  setInputValue((prev) => prev === label ? prev : label);
12611
12616
  }, [value]);
12612
- const query = inputValue.trim();
12617
+ const selectedLabel = value == null ? void 0 : itemToStringLabelProp?.(value) ?? toLabel(value);
12618
+ const isShowingSelection = selectedLabel !== void 0 && inputValue === selectedLabel;
12619
+ const query = (isShowingSelection ? "" : inputValue).trim();
12613
12620
  const queryLower = query.toLowerCase();
12614
12621
  const itemsProp = _itemsProp ?? [];
12615
12622
  const sampleItem = itemsProp.flatMap((item) => isGroupItem(item) ? item.items : [item])[0];
@@ -12634,7 +12641,7 @@ var Combobox = ({
12634
12641
  if (isCreatableItem(value2)) return value2.value;
12635
12642
  return itemToStringLabelProp?.(value2) ?? toLabel(value2);
12636
12643
  };
12637
- const wrappedFilter = !allowCreate || !filter ? filter : (item, ...args) => isCreatableItem(item) || filter(item, ...args);
12644
+ const wrappedFilter = isShowingSelection ? () => true : !allowCreate || !filter ? filter : (item, ...args) => isCreatableItem(item) || filter(item, ...args);
12638
12645
  const wrappedItemToStringValue = !allowCreate || !itemToStringValue ? itemToStringValue : (item) => isCreatableItem(item) ? item.value : itemToStringValue(item);
12639
12646
  const isItemEqualToValue = (a, b) => {
12640
12647
  if (isCreatableItem(a) || isCreatableItem(b)) return toValueKey(a) === toValueKey(b);
@@ -12669,10 +12676,34 @@ var Combobox = ({
12669
12676
  closeReasonRef.current = "item-press";
12670
12677
  onValueChange?.(next, event);
12671
12678
  };
12672
- const renderItem = (item) => {
12679
+ const [listElement, setListElement] = React26.useState(null);
12680
+ const isGrouped = isGroupItems(allItems);
12681
+ const shouldVirtualize = !isGrouped && (virtualized === true || virtualized === "auto" && allItems.length > VIRTUALIZE_THRESHOLD);
12682
+ const matchesQuery2 = (item) => {
12683
+ if (isCreatableItem(item)) return true;
12684
+ if (!query) return true;
12685
+ if (filter) return !!filter(item, query, (value2) => itemToStringLabel(value2) ?? toLabel(value2));
12686
+ return (itemToStringLabel(item) ?? toLabel(item)).toLowerCase().includes(queryLower);
12687
+ };
12688
+ const virtualItems = shouldVirtualize ? items.filter(matchesQuery2) : [];
12689
+ const virtualizer = reactVirtual.useVirtualizer({
12690
+ count: virtualItems.length,
12691
+ getScrollElement: () => listElement,
12692
+ estimateSize: () => ESTIMATED_ROW_HEIGHT,
12693
+ overscan: 12,
12694
+ enabled: shouldVirtualize,
12695
+ // Seed a viewport height so rows paint on the first frame. Without it the list measures 0 until
12696
+ // the ResizeObserver fires (a frame later), which renders an empty popup on open.
12697
+ initialRect: { width: 0, height: ESTIMATED_LIST_HEIGHT }
12698
+ });
12699
+ const renderItem = (item, index) => {
12700
+ if (isCreatableItem(item)) {
12701
+ return /* @__PURE__ */ jsxRuntime.jsx(Combobox.Item, { index, value: item, icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}), children: queryToCreateLabel(item.value) });
12702
+ }
12673
12703
  return /* @__PURE__ */ jsxRuntime.jsx(
12674
12704
  Combobox.Item,
12675
12705
  {
12706
+ index,
12676
12707
  value: item,
12677
12708
  indicator,
12678
12709
  icon: itemToIcon(item),
@@ -12681,13 +12712,20 @@ var Combobox = ({
12681
12712
  }
12682
12713
  );
12683
12714
  };
12715
+ const rowStyle = (offset) => ({
12716
+ position: "absolute",
12717
+ top: 0,
12718
+ left: 0,
12719
+ width: "100%",
12720
+ transform: `translateY(${offset}px)`
12721
+ });
12684
12722
  return /* @__PURE__ */ jsxRuntime.jsxs(
12685
12723
  combobox.Combobox.Root,
12686
12724
  {
12687
- items,
12725
+ items: shouldVirtualize ? virtualItems : items,
12688
12726
  value,
12689
12727
  defaultValue,
12690
- filter: wrappedFilter,
12728
+ filter: shouldVirtualize ? null : wrappedFilter,
12691
12729
  onValueChange: handleOnValueChange,
12692
12730
  itemToStringLabel,
12693
12731
  isItemEqualToValue,
@@ -12697,6 +12735,12 @@ var Combobox = ({
12697
12735
  inputRef,
12698
12736
  inputValue,
12699
12737
  onInputValueChange: handleInputValueChange,
12738
+ virtualized: shouldVirtualize,
12739
+ onItemHighlighted: (_, details) => {
12740
+ if (shouldVirtualize && details.reason === "keyboard") {
12741
+ virtualizer.scrollToIndex(details.index);
12742
+ }
12743
+ },
12700
12744
  children: [
12701
12745
  /* @__PURE__ */ jsxRuntime.jsxs(
12702
12746
  ComboboxInputGroup,
@@ -12718,7 +12762,30 @@ var Combobox = ({
12718
12762
  ),
12719
12763
  /* @__PURE__ */ jsxRuntime.jsxs(ComboboxContent, { children: [
12720
12764
  /* @__PURE__ */ jsxRuntime.jsx(ComboboxEmpty, { children: empty }),
12721
- /* @__PURE__ */ jsxRuntime.jsx(ComboboxList, { children: (itemOrGroup) => {
12765
+ shouldVirtualize ? /* @__PURE__ */ jsxRuntime.jsx(ComboboxList, { ref: setListElement, children: /* @__PURE__ */ jsxRuntime.jsx(
12766
+ "div",
12767
+ {
12768
+ style: {
12769
+ position: "relative",
12770
+ width: "100%",
12771
+ height: `${virtualizer.getTotalSize()}px`
12772
+ },
12773
+ children: virtualizer.getVirtualItems().map((row) => {
12774
+ const item = virtualItems[row.index];
12775
+ if (item === void 0) return null;
12776
+ return /* @__PURE__ */ jsxRuntime.jsx(
12777
+ "div",
12778
+ {
12779
+ "data-index": row.index,
12780
+ ref: virtualizer.measureElement,
12781
+ style: rowStyle(row.start),
12782
+ children: renderItem(item, row.index)
12783
+ },
12784
+ row.key
12785
+ );
12786
+ })
12787
+ }
12788
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(ComboboxList, { children: (itemOrGroup) => {
12722
12789
  if (isCreatableItem(itemOrGroup))
12723
12790
  return /* @__PURE__ */ jsxRuntime.jsx(Combobox.Item, { value: itemOrGroup, icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}), children: queryToCreateLabel(itemOrGroup.value) });
12724
12791
  if (isGroupItem(itemOrGroup)) {
@@ -12729,7 +12796,7 @@ var Combobox = ({
12729
12796
  const groupLabel = groupToLabel(itemOrGroup);
12730
12797
  return /* @__PURE__ */ jsxRuntime.jsxs(Combobox.Group, { items: itemOrGroup.items, children: [
12731
12798
  groupLabel && /* @__PURE__ */ jsxRuntime.jsx(Combobox.GroupLabel, { children: groupLabel }),
12732
- /* @__PURE__ */ jsxRuntime.jsx(Combobox.GroupList, { children: renderItem })
12799
+ /* @__PURE__ */ jsxRuntime.jsx(Combobox.GroupList, { children: (item) => renderItem(item) })
12733
12800
  ] });
12734
12801
  }
12735
12802
  return renderItem(itemOrGroup);
@@ -13767,6 +13834,8 @@ var formatDateRange = (range, inputValue) => {
13767
13834
  const toStr = to?.toLocaleDateString();
13768
13835
  return `${fromStr} - ${toStr}`;
13769
13836
  };
13837
+ var ISO_DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
13838
+ var parseLocalDate = (value) => ISO_DATE_ONLY.test(value) ? /* @__PURE__ */ new Date(`${value}T00:00:00`) : new Date(value);
13770
13839
  var parseDateRange = (value) => {
13771
13840
  const parts = value.split(" - ");
13772
13841
  if (parts.length !== 2) {
@@ -13776,8 +13845,8 @@ var parseDateRange = (value) => {
13776
13845
  if (!fromPart || !toPart) {
13777
13846
  return null;
13778
13847
  }
13779
- const from = new Date(fromPart.trim());
13780
- const to = new Date(toPart.trim());
13848
+ const from = parseLocalDate(fromPart.trim());
13849
+ const to = parseLocalDate(toPart.trim());
13781
13850
  if (isNaN(from.getTime()) || isNaN(to.getTime())) {
13782
13851
  return null;
13783
13852
  }
@@ -23640,6 +23709,13 @@ function useAutosizeInput({
23640
23709
  ] });
23641
23710
  return { inputRef, inputStyle, sizerElement };
23642
23711
  }
23712
+ var useStableCallback = (callback) => {
23713
+ const latestRef = React26.useRef(callback);
23714
+ React26.useLayoutEffect(() => {
23715
+ latestRef.current = callback;
23716
+ }, [callback]);
23717
+ return React26.useCallback((...args) => latestRef.current(...args), []);
23718
+ };
23643
23719
  var MIN_SCALE = 0.5;
23644
23720
  var MAX_SCALE = 3;
23645
23721
  var ZOOM_STEP = 0.1;
@@ -24809,6 +24885,7 @@ exports.useMergeDiff = useMergeDiff;
24809
24885
  exports.useNotification = useNotification;
24810
24886
  exports.useScrollbarPosition = useScrollbarPosition;
24811
24887
  exports.useSidebar = useSidebar;
24888
+ exports.useStableCallback = useStableCallback;
24812
24889
  exports.useTargetRect = useTargetRect;
24813
24890
  exports.useTheme = useTheme;
24814
24891
  exports.wcagLevel = wcagLevel;