@sustaina/shared-ui 1.26.0 → 1.27.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.mjs CHANGED
@@ -2447,7 +2447,10 @@ var ConditionDropdownInput = ({ row, control, fieldSchema, onClear }) => /* @__P
2447
2447
  children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select value" })
2448
2448
  }
2449
2449
  ) }),
2450
- /* @__PURE__ */ jsx(SelectContent, { className: "w-full min-w-[unset]", children: fieldSchema && "options" in fieldSchema && fieldSchema.options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
2450
+ /* @__PURE__ */ jsx(SelectContent, { className: "w-full min-w-[unset]", children: fieldSchema && "options" in fieldSchema && fieldSchema.options.map((opt) => /* @__PURE__ */ jsxs(SelectItem, { value: opt.value, className: cn(opt.labelStyle), children: [
2451
+ opt.decorator,
2452
+ opt.label
2453
+ ] }, opt.value)) })
2451
2454
  ] }),
2452
2455
  hasValue && /* @__PURE__ */ jsx(
2453
2456
  ClearButton,
@@ -2518,7 +2521,8 @@ var LookupSelect = ({
2518
2521
  suggestionDebounce = 250,
2519
2522
  noOptionsMessage = "No records found. Please try a different search.",
2520
2523
  loadingMessage = "Loading...",
2521
- dropdownPortalId
2524
+ dropdownPortalId,
2525
+ multiple = false
2522
2526
  }) => {
2523
2527
  const [inputValue, setInputValue] = useState("");
2524
2528
  const inputRef = useRef(null);
@@ -2535,6 +2539,7 @@ var LookupSelect = ({
2535
2539
  const assignDropdownContentRef = useCallback((node) => {
2536
2540
  dropdownContentRef.current = node;
2537
2541
  }, []);
2542
+ const [activeSuggestionIndex, setActiveSuggestionIndex] = useState(-1);
2538
2543
  const dropdownPortalElement = useMemo(() => {
2539
2544
  if (typeof document === "undefined") return null;
2540
2545
  if (dropdownPortalId) {
@@ -2543,7 +2548,9 @@ var LookupSelect = ({
2543
2548
  }
2544
2549
  return document.body;
2545
2550
  }, [dropdownPortalId]);
2546
- const limitReached = value.length >= maxTags;
2551
+ const limitReached = multiple && value.length >= maxTags;
2552
+ const selectedValue = !multiple && value.length > 0 ? value[0] : void 0;
2553
+ const selectedLabel = selectedValue ? optionLabels[selectedValue] ?? selectedValue : void 0;
2547
2554
  const upsertOptionLabels = useCallback((options) => {
2548
2555
  setOptionLabels((prev) => {
2549
2556
  let next = null;
@@ -2562,13 +2569,17 @@ var LookupSelect = ({
2562
2569
  const trimmed = val.trim();
2563
2570
  if (!trimmed) return;
2564
2571
  if (value.includes(trimmed)) return;
2565
- if (value.length >= maxTags) return;
2566
- onChange([...value, trimmed]);
2572
+ if (multiple) {
2573
+ if (value.length >= maxTags) return;
2574
+ onChange([...value, trimmed]);
2575
+ } else {
2576
+ onChange([trimmed]);
2577
+ }
2567
2578
  setInputValue("");
2568
2579
  setSuggestions([]);
2569
2580
  setIsDropdownOpen(false);
2570
2581
  },
2571
- [value, onChange, maxTags]
2582
+ [value, onChange, maxTags, multiple]
2572
2583
  );
2573
2584
  const removeTag = useCallback(
2574
2585
  (index) => {
@@ -2578,17 +2589,6 @@ var LookupSelect = ({
2578
2589
  },
2579
2590
  [value, onChange]
2580
2591
  );
2581
- const handleKeyDown = useCallback(
2582
- (e) => {
2583
- if (e.key === "Enter" || e.key === ",") {
2584
- e.preventDefault();
2585
- }
2586
- if (e.key === "Backspace" && !inputValue) {
2587
- removeTag(value.length - 1);
2588
- }
2589
- },
2590
- [inputValue, removeTag, value.length]
2591
- );
2592
2592
  const handleClear = useCallback(() => {
2593
2593
  setInputValue("");
2594
2594
  setSuggestions([]);
@@ -2607,6 +2607,33 @@ var LookupSelect = ({
2607
2607
  },
2608
2608
  [addTag, upsertOptionLabels]
2609
2609
  );
2610
+ const handleKeyDown = useCallback(
2611
+ (e) => {
2612
+ if (e.key === "ArrowDown" && suggestions.length > 0) {
2613
+ e.preventDefault();
2614
+ setIsDropdownOpen(true);
2615
+ setActiveSuggestionIndex((prev) => Math.min(prev + 1, suggestions.length - 1));
2616
+ return;
2617
+ }
2618
+ if (e.key === "ArrowUp" && suggestions.length > 0) {
2619
+ e.preventDefault();
2620
+ setActiveSuggestionIndex((prev) => Math.max(prev - 1, 0));
2621
+ return;
2622
+ }
2623
+ if (e.key === "Enter" && activeSuggestionIndex >= 0 && suggestions[activeSuggestionIndex]) {
2624
+ e.preventDefault();
2625
+ handleSuggestionSelect(suggestions[activeSuggestionIndex]);
2626
+ return;
2627
+ }
2628
+ if (e.key === "," || e.key === "Enter") {
2629
+ e.preventDefault();
2630
+ }
2631
+ if (e.key === "Backspace" && !inputValue) {
2632
+ removeTag(value.length - 1);
2633
+ }
2634
+ },
2635
+ [suggestions, activeSuggestionIndex, handleSuggestionSelect, inputValue, removeTag, value.length]
2636
+ );
2610
2637
  const updateDropdownPosition = useCallback(() => {
2611
2638
  if (!dropdownPortalElement || !containerRef.current) return;
2612
2639
  const rect = containerRef.current.getBoundingClientRect();
@@ -2633,6 +2660,7 @@ var LookupSelect = ({
2633
2660
  }
2634
2661
  setLoading(true);
2635
2662
  setFetchError(null);
2663
+ setIsDropdownOpen(true);
2636
2664
  const requestId = ++requestIdRef.current;
2637
2665
  fetchDelayRef.current = setTimeout(() => {
2638
2666
  fetchSuggestions(query).then((items) => {
@@ -2686,21 +2714,33 @@ var LookupSelect = ({
2686
2714
  window.removeEventListener("scroll", handleReposition, true);
2687
2715
  };
2688
2716
  }, [dropdownPortalElement, isDropdownOpen, updateDropdownPosition]);
2717
+ useEffect(() => {
2718
+ if (suggestions.length === 0) {
2719
+ setActiveSuggestionIndex(-1);
2720
+ return;
2721
+ }
2722
+ setActiveSuggestionIndex((prev) => {
2723
+ if (prev === -1) return 0;
2724
+ if (prev >= suggestions.length) return suggestions.length - 1;
2725
+ return prev;
2726
+ });
2727
+ }, [suggestions]);
2689
2728
  const resolvedPlaceholder = placeholder2 ?? "Select";
2690
2729
  const showDropdown = isDropdownOpen && !fetchError;
2691
2730
  const dropdownContent = /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg", children: loading ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-inherit", children: loadingMessage }) : suggestions.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center px-3 py-4 text-sm text-inherit text-center", children: [
2692
- /* @__PURE__ */ jsx(not_found_default, { className: "w-full max-w-[80px] h-auto" }),
2731
+ /* @__PURE__ */ jsx(not_found_default, { className: "w-full max-w-20 h-auto" }),
2693
2732
  /* @__PURE__ */ jsx("span", { className: "pt-3", children: noOptionsMessage })
2694
- ] }) : /* @__PURE__ */ jsx("ul", { className: "max-h-48 overflow-y-auto py-1", children: suggestions.map((option) => {
2695
- const disabled = value.includes(option.value) || limitReached;
2733
+ ] }) : /* @__PURE__ */ jsx("ul", { className: "max-h-48 overflow-y-auto py-1", children: suggestions.map((option, index) => {
2734
+ const disabled = value.includes(option.value) || multiple && limitReached;
2696
2735
  return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2697
2736
  "button",
2698
2737
  {
2699
2738
  type: "button",
2700
- className: `flex w-full items-center justify-between px-3 py-2 text-left text-sm transition hover:bg-gray-100
2701
- ${disabled ? "cursor-not-allowed text-gray-400" : "text-gray-700"}`,
2739
+ className: `flex w-full items-center justify-between px-3 py-2 text-left text-sm transition
2740
+ ${disabled ? "cursor-not-allowed text-gray-400" : index === activeSuggestionIndex ? "bg-gray-100 text-gray-900" : "hover:bg-gray-100 text-gray-700"}`,
2702
2741
  onClick: () => handleSuggestionSelect(option),
2703
2742
  disabled,
2743
+ "aria-selected": index === activeSuggestionIndex,
2704
2744
  children: /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label })
2705
2745
  }
2706
2746
  ) }, option.value);
@@ -2719,16 +2759,16 @@ var LookupSelect = ({
2719
2759
  /* @__PURE__ */ jsxs(
2720
2760
  "div",
2721
2761
  {
2722
- className: `flex min-h-[36px] items-center gap-3 rounded-md border bg-white px-3 transition-all
2762
+ className: `flex min-h-9 items-center gap-3 rounded-md border bg-white px-3 transition-all
2723
2763
  ${error ? "border-red-500 focus-within:ring-red-500" : "border-gray-300 "}`,
2724
2764
  children: [
2725
2765
  /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-wrap content-start gap-1 overflow-y-auto max-h-[72px] hide-scrollbar pr-4", children: [
2726
- value.map((tag, i) => {
2766
+ multiple && value.map((tag, i) => {
2727
2767
  const label = optionLabels[tag] ?? tag;
2728
2768
  return /* @__PURE__ */ jsxs(
2729
2769
  "span",
2730
2770
  {
2731
- className: "flex items-center gap-1 rounded-lg border border-[#f0f0f0] bg-[#f9f9f9] px-2 py-1 my-1 text-sm text-inherit max-w-full break-words",
2771
+ className: "flex items-center gap-1 rounded-lg border border-[#f0f0f0] bg-[#f9f9f9] px-2 py-1 my-1 text-sm text-inherit max-w-full wrap-break-word",
2732
2772
  children: [
2733
2773
  /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap break-all", children: label }),
2734
2774
  /* @__PURE__ */ jsx(
@@ -2737,6 +2777,7 @@ var LookupSelect = ({
2737
2777
  type: "button",
2738
2778
  onClick: () => removeTag(i),
2739
2779
  className: "text-inherit transition hover:text-red-500",
2780
+ "aria-label": `Remove ${label}`,
2740
2781
  children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
2741
2782
  }
2742
2783
  )
@@ -2745,7 +2786,8 @@ var LookupSelect = ({
2745
2786
  `${tag}-${i}`
2746
2787
  );
2747
2788
  }),
2748
- !limitReached && /* @__PURE__ */ jsx(
2789
+ !multiple && selectedLabel && /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-gray-700", children: selectedLabel }),
2790
+ /* @__PURE__ */ jsx(
2749
2791
  "input",
2750
2792
  {
2751
2793
  ref: inputRef,
@@ -2760,7 +2802,7 @@ var LookupSelect = ({
2760
2802
  },
2761
2803
  placeholder: value.length === 0 && resolvedPlaceholder || "",
2762
2804
  className: "min-w-[120px] flex-1 border-none bg-transparent py-1 text-sm text-gray-600 placeholder:text-gray-400 outline-none",
2763
- disabled: limitReached
2805
+ disabled: multiple && limitReached
2764
2806
  }
2765
2807
  )
2766
2808
  ] }),
@@ -2820,7 +2862,8 @@ var ConditionLookupInput = ({
2820
2862
  noOptionsMessage: fieldSchema?.noOptionsMessage,
2821
2863
  loadingMessage: fieldSchema?.loadingMessage,
2822
2864
  dropdownPortalId,
2823
- "data-testid": "advsearch-lookup-list"
2865
+ "data-testid": "advsearch-lookup-list",
2866
+ multiple: true
2824
2867
  }
2825
2868
  ) }),
2826
2869
  /* @__PURE__ */ jsx(FormMessage, { className: "absolute left-0 top-full mt-1 text-xs text-red-600" })