@sustaina/shared-ui 1.26.0 → 1.27.1

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.d.mts CHANGED
@@ -2,6 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React$1 from 'react';
3
3
  import React__default, { CSSProperties, ReactNode, SVGProps } from 'react';
4
4
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
5
+ import { ClassValue } from 'clsx';
5
6
  import * as class_variance_authority_dist_types from 'class-variance-authority/dist/types';
6
7
  import { VariantProps } from 'class-variance-authority';
7
8
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
@@ -24,7 +25,6 @@ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
24
25
  import * as SwitchPrimitive from '@radix-ui/react-switch';
25
26
  import { CropperProps, Area } from 'react-easy-crop';
26
27
  import { NumericFormatProps } from 'react-number-format';
27
- import { ClassValue } from 'clsx';
28
28
 
29
29
  declare function Accordion({ ...props }: React$1.ComponentProps<typeof AccordionPrimitive.Root>): react_jsx_runtime.JSX.Element;
30
30
  declare function AccordionItem({ className, ...props }: React$1.ComponentProps<typeof AccordionPrimitive.Item>): react_jsx_runtime.JSX.Element;
@@ -33,6 +33,8 @@ declare function AccordionContent({ className, children, ...props }: React$1.Com
33
33
 
34
34
  type FieldType = "text" | "number" | "date" | "datetime" | "datemonth" | "checkbox" | "dropdown" | "lookup" | "uuid" | "json";
35
35
  type Option = {
36
+ decorator?: React.ReactNode;
37
+ labelStyle?: ClassValue;
36
38
  value: string;
37
39
  label: string;
38
40
  };
@@ -835,6 +837,7 @@ interface LookupSelectProps {
835
837
  noOptionsMessage?: string;
836
838
  loadingMessage?: string;
837
839
  dropdownPortalId?: string;
840
+ multiple?: boolean;
838
841
  }
839
842
  declare const LookupSelect: React__default.FC<LookupSelectProps>;
840
843
 
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React$1 from 'react';
3
3
  import React__default, { CSSProperties, ReactNode, SVGProps } from 'react';
4
4
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
5
+ import { ClassValue } from 'clsx';
5
6
  import * as class_variance_authority_dist_types from 'class-variance-authority/dist/types';
6
7
  import { VariantProps } from 'class-variance-authority';
7
8
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
@@ -24,7 +25,6 @@ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
24
25
  import * as SwitchPrimitive from '@radix-ui/react-switch';
25
26
  import { CropperProps, Area } from 'react-easy-crop';
26
27
  import { NumericFormatProps } from 'react-number-format';
27
- import { ClassValue } from 'clsx';
28
28
 
29
29
  declare function Accordion({ ...props }: React$1.ComponentProps<typeof AccordionPrimitive.Root>): react_jsx_runtime.JSX.Element;
30
30
  declare function AccordionItem({ className, ...props }: React$1.ComponentProps<typeof AccordionPrimitive.Item>): react_jsx_runtime.JSX.Element;
@@ -33,6 +33,8 @@ declare function AccordionContent({ className, children, ...props }: React$1.Com
33
33
 
34
34
  type FieldType = "text" | "number" | "date" | "datetime" | "datemonth" | "checkbox" | "dropdown" | "lookup" | "uuid" | "json";
35
35
  type Option = {
36
+ decorator?: React.ReactNode;
37
+ labelStyle?: ClassValue;
36
38
  value: string;
37
39
  label: string;
38
40
  };
@@ -835,6 +837,7 @@ interface LookupSelectProps {
835
837
  noOptionsMessage?: string;
836
838
  loadingMessage?: string;
837
839
  dropdownPortalId?: string;
840
+ multiple?: boolean;
838
841
  }
839
842
  declare const LookupSelect: React__default.FC<LookupSelectProps>;
840
843
 
package/dist/index.js CHANGED
@@ -2487,7 +2487,10 @@ var ConditionDropdownInput = ({ row, control, fieldSchema, onClear }) => /* @__P
2487
2487
  children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select value" })
2488
2488
  }
2489
2489
  ) }),
2490
- /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "w-full min-w-[unset]", children: fieldSchema && "options" in fieldSchema && fieldSchema.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
2490
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "w-full min-w-[unset]", children: fieldSchema && "options" in fieldSchema && fieldSchema.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(SelectItem, { value: opt.value, className: cn(opt.labelStyle), children: [
2491
+ opt.decorator,
2492
+ opt.label
2493
+ ] }, opt.value)) })
2491
2494
  ] }),
2492
2495
  hasValue && /* @__PURE__ */ jsxRuntime.jsx(
2493
2496
  ClearButton,
@@ -2558,7 +2561,8 @@ var LookupSelect = ({
2558
2561
  suggestionDebounce = 250,
2559
2562
  noOptionsMessage = "No records found. Please try a different search.",
2560
2563
  loadingMessage = "Loading...",
2561
- dropdownPortalId
2564
+ dropdownPortalId,
2565
+ multiple = false
2562
2566
  }) => {
2563
2567
  const [inputValue, setInputValue] = React4.useState("");
2564
2568
  const inputRef = React4.useRef(null);
@@ -2575,6 +2579,7 @@ var LookupSelect = ({
2575
2579
  const assignDropdownContentRef = React4.useCallback((node) => {
2576
2580
  dropdownContentRef.current = node;
2577
2581
  }, []);
2582
+ const [activeSuggestionIndex, setActiveSuggestionIndex] = React4.useState(-1);
2578
2583
  const dropdownPortalElement = React4.useMemo(() => {
2579
2584
  if (typeof document === "undefined") return null;
2580
2585
  if (dropdownPortalId) {
@@ -2583,7 +2588,9 @@ var LookupSelect = ({
2583
2588
  }
2584
2589
  return document.body;
2585
2590
  }, [dropdownPortalId]);
2586
- const limitReached = value.length >= maxTags;
2591
+ const limitReached = multiple && value.length >= maxTags;
2592
+ const selectedValue = !multiple && value.length > 0 ? value[0] : void 0;
2593
+ const selectedLabel = selectedValue ? optionLabels[selectedValue] ?? selectedValue : void 0;
2587
2594
  const upsertOptionLabels = React4.useCallback((options) => {
2588
2595
  setOptionLabels((prev) => {
2589
2596
  let next = null;
@@ -2602,13 +2609,17 @@ var LookupSelect = ({
2602
2609
  const trimmed = val.trim();
2603
2610
  if (!trimmed) return;
2604
2611
  if (value.includes(trimmed)) return;
2605
- if (value.length >= maxTags) return;
2606
- onChange([...value, trimmed]);
2612
+ if (multiple) {
2613
+ if (value.length >= maxTags) return;
2614
+ onChange([...value, trimmed]);
2615
+ } else {
2616
+ onChange([trimmed]);
2617
+ }
2607
2618
  setInputValue("");
2608
2619
  setSuggestions([]);
2609
2620
  setIsDropdownOpen(false);
2610
2621
  },
2611
- [value, onChange, maxTags]
2622
+ [value, onChange, maxTags, multiple]
2612
2623
  );
2613
2624
  const removeTag = React4.useCallback(
2614
2625
  (index) => {
@@ -2618,17 +2629,6 @@ var LookupSelect = ({
2618
2629
  },
2619
2630
  [value, onChange]
2620
2631
  );
2621
- const handleKeyDown = React4.useCallback(
2622
- (e) => {
2623
- if (e.key === "Enter" || e.key === ",") {
2624
- e.preventDefault();
2625
- }
2626
- if (e.key === "Backspace" && !inputValue) {
2627
- removeTag(value.length - 1);
2628
- }
2629
- },
2630
- [inputValue, removeTag, value.length]
2631
- );
2632
2632
  const handleClear = React4.useCallback(() => {
2633
2633
  setInputValue("");
2634
2634
  setSuggestions([]);
@@ -2647,6 +2647,33 @@ var LookupSelect = ({
2647
2647
  },
2648
2648
  [addTag, upsertOptionLabels]
2649
2649
  );
2650
+ const handleKeyDown = React4.useCallback(
2651
+ (e) => {
2652
+ if (e.key === "ArrowDown" && suggestions.length > 0) {
2653
+ e.preventDefault();
2654
+ setIsDropdownOpen(true);
2655
+ setActiveSuggestionIndex((prev) => Math.min(prev + 1, suggestions.length - 1));
2656
+ return;
2657
+ }
2658
+ if (e.key === "ArrowUp" && suggestions.length > 0) {
2659
+ e.preventDefault();
2660
+ setActiveSuggestionIndex((prev) => Math.max(prev - 1, 0));
2661
+ return;
2662
+ }
2663
+ if (e.key === "Enter" && activeSuggestionIndex >= 0 && suggestions[activeSuggestionIndex]) {
2664
+ e.preventDefault();
2665
+ handleSuggestionSelect(suggestions[activeSuggestionIndex]);
2666
+ return;
2667
+ }
2668
+ if (e.key === "," || e.key === "Enter") {
2669
+ e.preventDefault();
2670
+ }
2671
+ if (e.key === "Backspace" && !inputValue) {
2672
+ removeTag(value.length - 1);
2673
+ }
2674
+ },
2675
+ [suggestions, activeSuggestionIndex, handleSuggestionSelect, inputValue, removeTag, value.length]
2676
+ );
2650
2677
  const updateDropdownPosition = React4.useCallback(() => {
2651
2678
  if (!dropdownPortalElement || !containerRef.current) return;
2652
2679
  const rect = containerRef.current.getBoundingClientRect();
@@ -2673,6 +2700,7 @@ var LookupSelect = ({
2673
2700
  }
2674
2701
  setLoading(true);
2675
2702
  setFetchError(null);
2703
+ setIsDropdownOpen(true);
2676
2704
  const requestId = ++requestIdRef.current;
2677
2705
  fetchDelayRef.current = setTimeout(() => {
2678
2706
  fetchSuggestions(query).then((items) => {
@@ -2726,21 +2754,33 @@ var LookupSelect = ({
2726
2754
  window.removeEventListener("scroll", handleReposition, true);
2727
2755
  };
2728
2756
  }, [dropdownPortalElement, isDropdownOpen, updateDropdownPosition]);
2757
+ React4.useEffect(() => {
2758
+ if (suggestions.length === 0) {
2759
+ setActiveSuggestionIndex(-1);
2760
+ return;
2761
+ }
2762
+ setActiveSuggestionIndex((prev) => {
2763
+ if (prev === -1) return 0;
2764
+ if (prev >= suggestions.length) return suggestions.length - 1;
2765
+ return prev;
2766
+ });
2767
+ }, [suggestions]);
2729
2768
  const resolvedPlaceholder = placeholder2 ?? "Select";
2730
2769
  const showDropdown = isDropdownOpen && !fetchError;
2731
2770
  const dropdownContent = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-md border border-gray-200 bg-white shadow-lg", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-inherit", children: loadingMessage }) : suggestions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center px-3 py-4 text-sm text-inherit text-center", children: [
2732
- /* @__PURE__ */ jsxRuntime.jsx(not_found_default, { className: "w-full max-w-[80px] h-auto" }),
2771
+ /* @__PURE__ */ jsxRuntime.jsx(not_found_default, { className: "w-full max-w-20 h-auto" }),
2733
2772
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pt-3", children: noOptionsMessage })
2734
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "max-h-48 overflow-y-auto py-1", children: suggestions.map((option) => {
2735
- const disabled = value.includes(option.value) || limitReached;
2773
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "max-h-48 overflow-y-auto py-1", children: suggestions.map((option, index) => {
2774
+ const disabled = value.includes(option.value) || multiple && limitReached;
2736
2775
  return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
2737
2776
  "button",
2738
2777
  {
2739
2778
  type: "button",
2740
- className: `flex w-full items-center justify-between px-3 py-2 text-left text-sm transition hover:bg-gray-100
2741
- ${disabled ? "cursor-not-allowed text-gray-400" : "text-gray-700"}`,
2779
+ className: `flex w-full items-center justify-between px-3 py-2 text-left text-sm transition
2780
+ ${disabled ? "cursor-not-allowed text-gray-400" : index === activeSuggestionIndex ? "bg-gray-100 text-gray-900" : "hover:bg-gray-100 text-gray-700"}`,
2742
2781
  onClick: () => handleSuggestionSelect(option),
2743
2782
  disabled,
2783
+ "aria-selected": index === activeSuggestionIndex,
2744
2784
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: option.label })
2745
2785
  }
2746
2786
  ) }, option.value);
@@ -2759,16 +2799,16 @@ var LookupSelect = ({
2759
2799
  /* @__PURE__ */ jsxRuntime.jsxs(
2760
2800
  "div",
2761
2801
  {
2762
- className: `flex min-h-[36px] items-center gap-3 rounded-md border bg-white px-3 transition-all
2802
+ className: `flex min-h-9 items-center gap-3 rounded-md border bg-white px-3 transition-all
2763
2803
  ${error ? "border-red-500 focus-within:ring-red-500" : "border-gray-300 "}`,
2764
2804
  children: [
2765
2805
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-wrap content-start gap-1 overflow-y-auto max-h-[72px] hide-scrollbar pr-4", children: [
2766
- value.map((tag, i) => {
2806
+ multiple && value.map((tag, i) => {
2767
2807
  const label = optionLabels[tag] ?? tag;
2768
2808
  return /* @__PURE__ */ jsxRuntime.jsxs(
2769
2809
  "span",
2770
2810
  {
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 break-words",
2811
+ 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",
2772
2812
  children: [
2773
2813
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-pre-wrap break-all", children: label }),
2774
2814
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2777,6 +2817,7 @@ var LookupSelect = ({
2777
2817
  type: "button",
2778
2818
  onClick: () => removeTag(i),
2779
2819
  className: "text-inherit transition hover:text-red-500",
2820
+ "aria-label": `Remove ${label}`,
2780
2821
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
2781
2822
  }
2782
2823
  )
@@ -2785,7 +2826,8 @@ var LookupSelect = ({
2785
2826
  `${tag}-${i}`
2786
2827
  );
2787
2828
  }),
2788
- !limitReached && /* @__PURE__ */ jsxRuntime.jsx(
2829
+ !multiple && selectedLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm text-gray-700", children: selectedLabel }),
2830
+ /* @__PURE__ */ jsxRuntime.jsx(
2789
2831
  "input",
2790
2832
  {
2791
2833
  ref: inputRef,
@@ -2800,7 +2842,7 @@ var LookupSelect = ({
2800
2842
  },
2801
2843
  placeholder: value.length === 0 && resolvedPlaceholder || "",
2802
2844
  className: "min-w-[120px] flex-1 border-none bg-transparent py-1 text-sm text-gray-600 placeholder:text-gray-400 outline-none",
2803
- disabled: limitReached
2845
+ disabled: multiple && limitReached
2804
2846
  }
2805
2847
  )
2806
2848
  ] }),
@@ -2860,7 +2902,8 @@ var ConditionLookupInput = ({
2860
2902
  noOptionsMessage: fieldSchema?.noOptionsMessage,
2861
2903
  loadingMessage: fieldSchema?.loadingMessage,
2862
2904
  dropdownPortalId,
2863
- "data-testid": "advsearch-lookup-list"
2905
+ "data-testid": "advsearch-lookup-list",
2906
+ multiple: true
2864
2907
  }
2865
2908
  ) }),
2866
2909
  /* @__PURE__ */ jsxRuntime.jsx(FormMessage, { className: "absolute left-0 top-full mt-1 text-xs text-red-600" })