@connectif/ui-components 9.0.5 → 9.0.6

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
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
 
3
- ## [9.0.5] - 2026-06- 19
3
+ ## [9.0.6] - 2026-06-23
4
+
5
+ ### Added
6
+
7
+ - `Autocomplete`: added optional `exclusiveOption` property.
8
+
9
+ ## [9.0.5] - 2026-06-19
4
10
 
5
11
  ### Added
6
12
 
@@ -124,9 +124,19 @@ export type AutocompleteProps<T extends string, Multiple extends boolean | undef
124
124
  * Ref to the inner HTMLInputElement
125
125
  */
126
126
  ref?: React.Ref<HTMLInputElement>;
127
+ /**
128
+ * When provided, this option is always displayed as the first item in the
129
+ * dropdown, separated from the rest by a divider. Selecting it clears all
130
+ * other selections; selecting any other option deselects this one.
131
+ * In multiple mode, while this option is active the input renders as plain
132
+ * text (no chips).
133
+ */
134
+ exclusiveOption?: AutocompleteOption<T> & {
135
+ label?: string;
136
+ };
127
137
  };
128
138
  /**
129
139
  * Powered TextField with the ability to select from a predefined list of options, allowing to select one or multiple values.
130
140
  */
131
- declare const Autocomplete: <T extends string, Multiple extends boolean | undefined = undefined, Value = AutocompleteValue<T, Multiple>>({ variant, value, multiple, textFieldProps, options, disabled, mode, isCaseSensitive, isDiacriticSensitive, onChange, renderLabel, isLoading, onSearch, loadingText, noOptionsText, allowFreeText, addNewValueText, closeOnSelect, maxValueLength, maxValues, maxValuesText, startIconId, onClose, onOpen, disableClear, disableCloseOnClickAway, size }: AutocompleteProps<T, Multiple, Value>) => import("react/jsx-runtime").JSX.Element;
141
+ declare const Autocomplete: <T extends string, Multiple extends boolean | undefined = undefined, Value = AutocompleteValue<T, Multiple>>({ variant, value, multiple, textFieldProps, options, disabled, mode, isCaseSensitive, isDiacriticSensitive, onChange, renderLabel, isLoading, onSearch, loadingText, noOptionsText, allowFreeText, addNewValueText, closeOnSelect, maxValueLength, maxValues, maxValuesText, startIconId, onClose, onOpen, disableClear, disableCloseOnClickAway, size, exclusiveOption }: AutocompleteProps<T, Multiple, Value>) => import("react/jsx-runtime").JSX.Element;
132
142
  export default Autocomplete;
@@ -67,9 +67,19 @@ export type AutocompleteInputProps<T extends string, Multiple extends boolean |
67
67
  onSearchValueChange: (event: React.SyntheticEvent, searchValue: string) => void;
68
68
  onPressEnter: (event: React.SyntheticEvent, inputValue: string) => void;
69
69
  onRemoveValue: (event: React.SyntheticEvent, value: Value) => void;
70
+ /**
71
+ * When true (exclusive option selected in multiple mode), chips are hidden
72
+ * and the input displays as a plain text field.
73
+ */
74
+ exclusiveOptionSelected?: boolean;
75
+ /**
76
+ * Label to display in the text field when the exclusive option is selected
77
+ * and the component is in multiple mode.
78
+ */
79
+ exclusiveOptionLabel?: string;
70
80
  };
71
81
  /**
72
82
  * Powered TextField Input.
73
83
  */
74
- declare const AutocompleteInput: <T extends string, Multiple extends boolean | undefined = undefined, Value = AutocompleteValue<T, Multiple>>({ variant, value, multiple, textFieldProps, hasFilteredOptions, disabled, renderLabel, maxValueLength, canAddValues, maxValuesText, disableClear, containerRef, inputRef, onClosePopover, onOpenPopover, isOpenPopover, inputValue, setInputValue, placeholder, startIconId, size, onSearchValueChange, onPressEnter, onRemoveValue }: AutocompleteInputProps<T, Multiple, Value>) => import("react/jsx-runtime").JSX.Element;
84
+ declare const AutocompleteInput: <T extends string, Multiple extends boolean | undefined = undefined, Value = AutocompleteValue<T, Multiple>>({ variant, value, multiple, textFieldProps, hasFilteredOptions, disabled, renderLabel, maxValueLength, canAddValues, maxValuesText, disableClear, containerRef, inputRef, onClosePopover, onOpenPopover, isOpenPopover, inputValue, setInputValue, placeholder, startIconId, size, onSearchValueChange, onPressEnter, onRemoveValue, exclusiveOptionSelected, exclusiveOptionLabel }: AutocompleteInputProps<T, Multiple, Value>) => import("react/jsx-runtime").JSX.Element;
75
85
  export default AutocompleteInput;
@@ -30,10 +30,19 @@ type AutocompleteListProps<T extends string, Multiple extends boolean | undefine
30
30
  highlightedIndex: number;
31
31
  onAddSelectedValue: (event: React.MouseEvent<HTMLElement, MouseEvent>, option: AutocompleteLabeledOption<T>) => void;
32
32
  onClickAwayPopover: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
33
+ /**
34
+ * Option that is always shown first, separated by a divider, and when selected
35
+ * clears all other selections (and vice versa).
36
+ */
37
+ exclusiveOption?: AutocompleteLabeledOption<T>;
38
+ /**
39
+ * Whether the exclusive option is currently selected.
40
+ */
41
+ isExclusiveSelected?: boolean;
33
42
  };
34
43
  export declare const AUTOCOMPLETE_ITEM_HEIGHT = 44;
35
44
  /**
36
45
  * Powered autocomplete list.
37
46
  */
38
- declare const AutocompleteList: <T extends string, Multiple extends boolean | undefined = undefined>({ options, isLoading, loadingText, noOptionsText, addNewValueText, inputContainerRef, isOpen, selectedIndex, highlightedIndex, virtualListRef, filteredOptions, onAddSelectedValue, onClickAwayPopover }: AutocompleteListProps<T, Multiple>) => import("react/jsx-runtime").JSX.Element;
47
+ declare const AutocompleteList: <T extends string, Multiple extends boolean | undefined = undefined>({ options, isLoading, loadingText, noOptionsText, addNewValueText, inputContainerRef, isOpen, selectedIndex, highlightedIndex, virtualListRef, filteredOptions, onAddSelectedValue, onClickAwayPopover, exclusiveOption, isExclusiveSelected }: AutocompleteListProps<T, Multiple>) => import("react/jsx-runtime").JSX.Element;
39
48
  export default AutocompleteList;
package/dist/index.js CHANGED
@@ -24321,7 +24321,9 @@ var AutocompleteInput = function AutocompleteInput2({
24321
24321
  size,
24322
24322
  onSearchValueChange,
24323
24323
  onPressEnter,
24324
- onRemoveValue
24324
+ onRemoveValue,
24325
+ exclusiveOptionSelected = false,
24326
+ exclusiveOptionLabel
24325
24327
  }) {
24326
24328
  const { palette: palette2 } = useCustomTheme();
24327
24329
  const { t } = useTranslation();
@@ -24367,7 +24369,7 @@ var AutocompleteInput = function AutocompleteInput2({
24367
24369
  },
24368
24370
  maxLength: maxValueLength,
24369
24371
  placeholder: !canAddValues ? maxValuesText || t("AUTOCOMPLETE.MAX_VALUES") : Array.isArray(value) && value.length > 0 || !Array.isArray(value) && !!value ? "" : placeholder,
24370
- value: inputValue || (!Array.isArray(value) ? renderLabel(value) : ""),
24372
+ value: inputValue || exclusiveOptionLabel || (!Array.isArray(value) ? renderLabel(value) : ""),
24371
24373
  multiline: false,
24372
24374
  disabled: disabled || !canAddValues,
24373
24375
  onChange: (event) => {
@@ -24405,7 +24407,7 @@ var AutocompleteInput = function AutocompleteInput2({
24405
24407
  }
24406
24408
  },
24407
24409
  containerRef,
24408
- startAdornment: multiple ? /* @__PURE__ */ jsx135(
24410
+ startAdornment: multiple && !exclusiveOptionSelected ? /* @__PURE__ */ jsx135(
24409
24411
  AutocompleteInputSelection_default,
24410
24412
  {
24411
24413
  value,
@@ -24537,11 +24539,14 @@ var AutocompleteList = function AutocompleteList2({
24537
24539
  virtualListRef,
24538
24540
  filteredOptions,
24539
24541
  onAddSelectedValue,
24540
- onClickAwayPopover
24542
+ onClickAwayPopover,
24543
+ exclusiveOption,
24544
+ isExclusiveSelected
24541
24545
  }) {
24542
24546
  const { palette: palette2 } = useCustomTheme();
24543
24547
  const { t } = useTranslation();
24544
24548
  const listRef = React76.useRef(null);
24549
+ const popoverWidth = inputContainerRef.current?.clientWidth;
24545
24550
  const getText = (option) => {
24546
24551
  const isInOptions = options.some((elem) => elem.id === option.id);
24547
24552
  if (!isInOptions) {
@@ -24598,58 +24603,90 @@ var AutocompleteList = function AutocompleteList2({
24598
24603
  anchorEl: inputContainerRef.current,
24599
24604
  open: isOpen,
24600
24605
  sx: { zIndex: 3e3 },
24601
- children: /* @__PURE__ */ jsx136(Paper_default, { sx: { boxShadow: shadows[1], borderRadius: "8px" }, children: /* @__PURE__ */ jsxs66(
24602
- Box_default2,
24606
+ children: /* @__PURE__ */ jsxs66(
24607
+ Paper_default,
24603
24608
  {
24604
24609
  sx: {
24605
- maxHeight: "40vh",
24606
- height: !isLoading && filteredOptions.length ? filteredOptions.length * 44 : "auto",
24607
- width: inputContainerRef.current ? inputContainerRef.current.clientWidth : null,
24608
- borderRadius: "8px"
24610
+ boxShadow: shadows[1],
24611
+ borderRadius: "8px",
24612
+ width: popoverWidth ?? "auto"
24609
24613
  },
24610
- className: "Slim-Vertical-Scroll",
24611
- ref: listRef,
24612
24614
  children: [
24613
- isLoading && /* @__PURE__ */ jsx136(
24614
- ListItem_default,
24615
- {
24616
- text: loadingText ?? t("AUTOCOMPLETE.LOADING")
24617
- }
24618
- ),
24619
- !isLoading && filteredOptions.length === 0 && /* @__PURE__ */ jsx136(
24620
- ListItem_default,
24621
- {
24622
- text: noOptionsText ?? t("AUTOCOMPLETE.NO_OPTIONS")
24623
- }
24624
- ),
24625
- !isLoading && filteredOptions.length > 0 && /* @__PURE__ */ jsx136(AutoSizer5, { children: ({
24626
- height: height2,
24627
- width: width2
24628
- }) => /* @__PURE__ */ jsx136(
24629
- FixedSizeList3,
24615
+ exclusiveOption && /* @__PURE__ */ jsxs66(Fragment34, { children: [
24616
+ /* @__PURE__ */ jsx136(
24617
+ ListItemButton_default,
24618
+ {
24619
+ selected: isExclusiveSelected,
24620
+ tabIndex: -1,
24621
+ role: "option",
24622
+ baseSx: {
24623
+ color: palette2.grey[900]
24624
+ },
24625
+ text: exclusiveOption.label,
24626
+ onClick: (event) => onAddSelectedValue(
24627
+ event,
24628
+ exclusiveOption
24629
+ ),
24630
+ "data-testid": `autocomplete-option-${exclusiveOption.id}`
24631
+ }
24632
+ ),
24633
+ /* @__PURE__ */ jsx136(Divider_default, {})
24634
+ ] }),
24635
+ (!exclusiveOption || filteredOptions.length > 0 || isLoading) && /* @__PURE__ */ jsxs66(
24636
+ Box_default2,
24630
24637
  {
24631
- overscanCount: 3,
24632
- height: height2,
24633
- itemCount: filteredOptions.length,
24634
- itemSize: AUTOCOMPLETE_ITEM_HEIGHT,
24635
- width: width2,
24638
+ sx: {
24639
+ maxHeight: "40vh",
24640
+ height: !isLoading && filteredOptions.length ? filteredOptions.length * 44 : "auto",
24641
+ width: "100%",
24642
+ borderRadius: "8px"
24643
+ },
24636
24644
  className: "Slim-Vertical-Scroll",
24637
- ref: virtualListRef,
24638
- initialScrollOffset: getInitialListScroll(
24639
- {
24640
- itemHeight: AUTOCOMPLETE_ITEM_HEIGHT,
24641
- containerHeight: listRef.current?.clientHeight ?? 0,
24642
- highlightedIndex,
24643
- selectedIndex,
24644
- totalItems: filteredOptions.length
24645
- }
24646
- ),
24647
- children: ListItemButtonWrapper
24645
+ ref: listRef,
24646
+ children: [
24647
+ isLoading && /* @__PURE__ */ jsx136(
24648
+ ListItem_default,
24649
+ {
24650
+ text: loadingText ?? t("AUTOCOMPLETE.LOADING")
24651
+ }
24652
+ ),
24653
+ !isLoading && filteredOptions.length === 0 && /* @__PURE__ */ jsx136(
24654
+ ListItem_default,
24655
+ {
24656
+ text: noOptionsText ?? t("AUTOCOMPLETE.NO_OPTIONS")
24657
+ }
24658
+ ),
24659
+ !isLoading && filteredOptions.length > 0 && /* @__PURE__ */ jsx136(AutoSizer5, { children: ({
24660
+ height: height2,
24661
+ width: width2
24662
+ }) => /* @__PURE__ */ jsx136(
24663
+ FixedSizeList3,
24664
+ {
24665
+ overscanCount: 3,
24666
+ height: height2,
24667
+ itemCount: filteredOptions.length,
24668
+ itemSize: AUTOCOMPLETE_ITEM_HEIGHT,
24669
+ width: width2,
24670
+ className: "Slim-Vertical-Scroll",
24671
+ ref: virtualListRef,
24672
+ initialScrollOffset: getInitialListScroll(
24673
+ {
24674
+ itemHeight: AUTOCOMPLETE_ITEM_HEIGHT,
24675
+ containerHeight: listRef.current?.clientHeight ?? 0,
24676
+ highlightedIndex,
24677
+ selectedIndex,
24678
+ totalItems: filteredOptions.length
24679
+ }
24680
+ ),
24681
+ children: ListItemButtonWrapper
24682
+ }
24683
+ ) })
24684
+ ]
24648
24685
  }
24649
- ) })
24686
+ )
24650
24687
  ]
24651
24688
  }
24652
- ) })
24689
+ )
24653
24690
  }
24654
24691
  )
24655
24692
  }
@@ -24687,7 +24724,8 @@ var Autocomplete = function Autocomplete2({
24687
24724
  onOpen = () => ({}),
24688
24725
  disableClear,
24689
24726
  disableCloseOnClickAway = false,
24690
- size = "S"
24727
+ size = "S",
24728
+ exclusiveOption
24691
24729
  }) {
24692
24730
  const anchorRef = React77.useRef(null);
24693
24731
  const inputRef = React77.useRef(null);
@@ -24697,6 +24735,24 @@ var Autocomplete = function Autocomplete2({
24697
24735
  const _renderLabel = React77.useCallback((id) => id, []);
24698
24736
  const [inputValue, setInputValue] = React77.useState("");
24699
24737
  const [isDirty, setDirty] = React77.useState(false);
24738
+ const labeledExclusiveOption = React77.useMemo(() => {
24739
+ if (!exclusiveOption) {
24740
+ return void 0;
24741
+ }
24742
+ return {
24743
+ ...exclusiveOption,
24744
+ label: exclusiveOption.label ?? (renderLabel ?? _renderLabel)(exclusiveOption.id)
24745
+ };
24746
+ }, [exclusiveOption, renderLabel, _renderLabel]);
24747
+ const isExclusiveSelected = React77.useMemo(() => {
24748
+ if (!exclusiveOption) {
24749
+ return false;
24750
+ }
24751
+ if (Array.isArray(value)) {
24752
+ return value.includes(exclusiveOption.id);
24753
+ }
24754
+ return value === exclusiveOption.id;
24755
+ }, [exclusiveOption, value]);
24700
24756
  const getOptionsLabeled = React77.useCallback(
24701
24757
  (options2, inputValue2 = "") => {
24702
24758
  const searchValue = escapeRegExp2(inputValue2.trim());
@@ -24898,28 +24954,43 @@ var Autocomplete = function Autocomplete2({
24898
24954
  onChange && onChange(event, newValue);
24899
24955
  onClosePopoverAfterSelect(newValue, newFilteredOptions);
24900
24956
  };
24901
- const onSelectValueMultiple = (newValue, value2, event) => {
24957
+ const onSelectValueMultiple = (newValue, value2, event, options2) => {
24902
24958
  if (!onChange) {
24903
24959
  return;
24904
24960
  }
24905
24961
  let newValues = [];
24906
- if (value2.includes(newValue)) {
24962
+ const exclusiveCurrentlySelected = exclusiveOption && value2.includes(exclusiveOption.id);
24963
+ const selectingExclusive = exclusiveOption && newValue === exclusiveOption.id;
24964
+ if (selectingExclusive && !value2.includes(newValue)) {
24965
+ newValues = [newValue];
24966
+ } else if (value2.includes(newValue)) {
24907
24967
  newValues = value2.filter((v) => v !== newValue);
24908
24968
  } else if (newValue) {
24909
24969
  newValues = [...value2, newValue];
24970
+ if (exclusiveCurrentlySelected) {
24971
+ newValues = newValues.filter((v) => v !== exclusiveOption.id);
24972
+ }
24910
24973
  }
24911
24974
  onChange(event, newValues);
24912
- onClosePopoverAfterSelect(newValues);
24975
+ onClosePopoverAfterSelect(
24976
+ newValues,
24977
+ void 0,
24978
+ options2
24979
+ );
24913
24980
  };
24914
- const onSelectValueSingle = (newValue, event) => {
24981
+ const onSelectValueSingle = (newValue, event, options2) => {
24915
24982
  if (!onChange) {
24916
24983
  return;
24917
24984
  }
24918
24985
  onChange(event, newValue);
24919
- onClosePopoverAfterSelect(newValue);
24986
+ onClosePopoverAfterSelect(
24987
+ newValue,
24988
+ void 0,
24989
+ options2
24990
+ );
24920
24991
  };
24921
- const onClosePopoverAfterSelect = (selectedValue, newFilteredOptions) => {
24922
- if (closeOnSelect) {
24992
+ const onClosePopoverAfterSelect = (selectedValue, newFilteredOptions, options2) => {
24993
+ if (closeOnSelect || options2?.forceClose) {
24923
24994
  closePopover();
24924
24995
  } else {
24925
24996
  setNextIndexByValue(selectedValue, newFilteredOptions);
@@ -24930,10 +25001,22 @@ var Autocomplete = function Autocomplete2({
24930
25001
  if (!onChange) {
24931
25002
  return;
24932
25003
  }
25004
+ const isSelectingExclusiveOption = !!exclusiveOption && option.id === exclusiveOption.id && (!Array.isArray(value) || !value.includes(option.id));
25005
+ if (isSelectingExclusiveOption) {
25006
+ setInputValue("");
25007
+ setDirty(false);
25008
+ setPlaceholder(textFieldProps?.placeholder ?? "");
25009
+ setFilteredOptions(getOptionsLabeled([...options]));
25010
+ onSearch && onSearch("");
25011
+ }
24933
25012
  if (Array.isArray(value)) {
24934
- onSelectValueMultiple(option.id, value, event);
25013
+ onSelectValueMultiple(option.id, value, event, {
25014
+ forceClose: isSelectingExclusiveOption
25015
+ });
24935
25016
  } else {
24936
- onSelectValueSingle(option.id, event);
25017
+ onSelectValueSingle(option.id, event, {
25018
+ forceClose: isSelectingExclusiveOption
25019
+ });
24937
25020
  }
24938
25021
  setPlaceholder(textFieldProps?.placeholder ?? "");
24939
25022
  };
@@ -25013,7 +25096,9 @@ var Autocomplete = function Autocomplete2({
25013
25096
  setInputValue,
25014
25097
  placeholder,
25015
25098
  size,
25016
- onRemoveValue: onRemoveInputValue
25099
+ onRemoveValue: onRemoveInputValue,
25100
+ exclusiveOptionSelected: !!multiple && isExclusiveSelected,
25101
+ exclusiveOptionLabel: isExclusiveSelected ? labeledExclusiveOption?.label : void 0
25017
25102
  }
25018
25103
  ),
25019
25104
  /* @__PURE__ */ jsx137(
@@ -25031,7 +25116,9 @@ var Autocomplete = function Autocomplete2({
25031
25116
  highlightedIndex,
25032
25117
  inputContainerRef: anchorRef,
25033
25118
  onAddSelectedValue: handleSelectValue,
25034
- onClickAwayPopover
25119
+ onClickAwayPopover,
25120
+ exclusiveOption: labeledExclusiveOption,
25121
+ isExclusiveSelected
25035
25122
  }
25036
25123
  )
25037
25124
  ] });