@linzjs/lui 23.12.0 → 23.13.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [23.13.1](https://github.com/linz/lui/compare/v23.13.0...v23.13.1) (2025-10-08)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **LuiComboSelect:** Fix LuiComboSelect to properly set custom classnames ([#1254](https://github.com/linz/lui/issues/1254)) ([8fdc579](https://github.com/linz/lui/commit/8fdc579e44bb496295a4acfa4b1d069641a694da))
7
+
8
+ # [23.13.0](https://github.com/linz/lui/compare/v23.12.0...v23.13.0) (2025-10-08)
9
+
10
+
11
+ ### Features
12
+
13
+ * **LuiSearchInput:** add 2 new optional variations to support design for CS-8444 ([#1256](https://github.com/linz/lui/issues/1256)) ([469f0b7](https://github.com/linz/lui/commit/469f0b7747e8e94a3436dca2f8086eae8f2ee198))
14
+
1
15
  # [23.12.0](https://github.com/linz/lui/compare/v23.11.2...v23.12.0) (2025-10-06)
2
16
 
3
17
 
@@ -23,5 +23,7 @@ export interface ISearchInputProps<SearchResult extends ISearchResult = ISearchR
23
23
  validateInput?: (input: string) => InputValidationResult | undefined;
24
24
  hideDisclaimerOnFail?: boolean;
25
25
  hideNoOptionsMessage?: boolean;
26
+ hideSearchIcon?: boolean;
27
+ filterElement?: ReactElement;
26
28
  }
27
29
  export declare const LuiSearchInput: <SearchResult extends ISearchResult = ISearchResult>(props: React.PropsWithChildren<ISearchInputProps<SearchResult>>) => JSX.Element;
package/dist/index.d.ts CHANGED
@@ -29,7 +29,7 @@ export { LuiIcon } from './components/LuiIcon/LuiIcon';
29
29
  export { LuiBearingInput } from './components/LuiBearingInput/LuiBearingInput';
30
30
  export { LuiFooter } from './components/LuiFooter/LuiFooter';
31
31
  export { LuiAppFooterSml } from './components/LuiFooter/LuiAppFooterSml';
32
- export { LuiComboSelect, type LuiComboSelectProps, type LuiComboSelectOption, } from './components/LuiForms/LuiComboSelect/LuiComboSelect';
32
+ export { LuiComboSelect, type LuiComboSelectProps, type LuiComboSelectOption, } from './components/LuiFormElements/LuiComboSelect/LuiComboSelect';
33
33
  export { LuiFormSectionHeader, type IFormSectionHeaderProps, } from './components/LuiForms/LuiFormSection/LuiFormSectionHeader';
34
34
  export * from './components/LuiHeader/LuiHeader';
35
35
  export * from './components/LuiHeaderV2/LuiHeaderV2';
package/dist/index.js CHANGED
@@ -15042,7 +15042,7 @@ function LuiComboSelectActual(givenProps, ref) {
15042
15042
  // box-shadow: "-8px 0px 0 0 #cc0000";
15043
15043
  // border-radius: "4px";
15044
15044
  var id = useGenerateOrDefaultId(props === null || props === void 0 ? void 0 : props.id);
15045
- var selectProp = __assign(__assign({ inputId: id }, props), { classNamePrefix: 'LuiComboSelect', theme: function (theme) { return (__assign(__assign({}, theme), { colors: __assign(__assign({}, theme.colors), { primary: colors['sea'], primary75: colors['electric'], primary50: colors['spray'], primary25: colors['polar'], neutral90: colors['charcoal'], neutral80: colors['charcoal'], neutral70: colors['charcoal'], neutral60: colors['fuscous'], neutral50: colors['fuscous'], neutral40: colors['gray'], neutral30: colors['gray'], neutral20: colors['silver'], neutral10: colors['lily'], neutral5: colors['hint'], danger: colors['error'], dangerLight: colors['error-bg'] }) })); }, styles: {
15045
+ var selectProp = __assign(__assign({ inputId: id }, props), { classNamePrefix: 'LuiComboSelect', className: undefined, theme: function (theme) { return (__assign(__assign({}, theme), { colors: __assign(__assign({}, theme.colors), { primary: colors['sea'], primary75: colors['electric'], primary50: colors['spray'], primary25: colors['polar'], neutral90: colors['charcoal'], neutral80: colors['charcoal'], neutral70: colors['charcoal'], neutral60: colors['fuscous'], neutral50: colors['fuscous'], neutral40: colors['gray'], neutral30: colors['gray'], neutral20: colors['silver'], neutral10: colors['lily'], neutral5: colors['hint'], danger: colors['error'], dangerLight: colors['error-bg'] }) })); }, styles: {
15046
15046
  control: function (provided, state) { return (__assign(__assign({}, provided), {
15047
15047
  /* matches style of .LuiTextInput-input */
15048
15048
  boxShadow: 'none', border: state.isFocused ? '1px solid #053d52' : '1px solid #b2b2b2', '&:hover, &:active': {
@@ -15062,12 +15062,13 @@ function LuiComboSelectActual(givenProps, ref) {
15062
15062
  return (__assign(__assign({}, provided), { color: colors['input-text'], backgroundColor: isSelected ? colors['selection'] : colors['white'] }));
15063
15063
  }
15064
15064
  } });
15065
- return (React__default["default"].createElement("label", { htmlFor: id, className: clsx('LuiComboSelect-label', props.error && 'hasError') },
15066
- React__default["default"].createElement("span", { className: clsx('LuiSelect-label-text', props.hideLabel ? 'LuiScreenReadersOnly' : '') }, props.label),
15067
- props.isCreateable ? (React__default["default"].createElement(CreatableSelect, __assign({ formatCreateLabel: function (inputValue) { return inputValue; }, createOptionPosition: "first", ref: ref }, selectProp))) : (React__default["default"].createElement(Select, __assign({ ref: ref }, selectProp))),
15068
- props.error && (React__default["default"].createElement("span", { className: "LuiComboSelect-error" },
15069
- React__default["default"].createElement(LuiIcon, { alt: 'Error', name: "ic_error", className: "LuiComboSelect-error-icon", size: "sm", status: "error" }),
15070
- props.error))));
15065
+ return (React__default["default"].createElement("div", { className: clsx('LuiComboSelect', props.className) },
15066
+ React__default["default"].createElement("label", { htmlFor: id, className: clsx('LuiComboSelect-label', props.error && 'hasError') },
15067
+ React__default["default"].createElement("span", { className: clsx('LuiSelect-label-text', props.hideLabel ? 'LuiScreenReadersOnly' : '') }, props.label),
15068
+ props.isCreateable ? (React__default["default"].createElement(CreatableSelect, __assign({ formatCreateLabel: function (inputValue) { return inputValue; }, createOptionPosition: "first", ref: ref }, selectProp))) : (React__default["default"].createElement(Select, __assign({ ref: ref }, selectProp))),
15069
+ props.error && (React__default["default"].createElement("span", { className: "LuiComboSelect-error" },
15070
+ React__default["default"].createElement(LuiIcon, { alt: 'Error', name: "ic_error", className: "LuiComboSelect-error-icon", size: "sm", status: "error" }),
15071
+ props.error)))));
15071
15072
  }
15072
15073
 
15073
15074
  var LuiShadow = function (props) {
@@ -17844,8 +17845,13 @@ var LuiSearchInput = function (props) {
17844
17845
  if (props.focusUpdate !== undefined)
17845
17846
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
17846
17847
  }, [props.focusUpdate]);
17847
- //clear result after search types changed
17848
17848
  React.useEffect(function () {
17849
+ //if there is a filter under way, recalculate the results
17850
+ if (props.filterElement) {
17851
+ retrieveResults(typedValue);
17852
+ return;
17853
+ }
17854
+ //if there is not a filter under way, then the results are no longer valid, clear them
17849
17855
  setResults([]);
17850
17856
  }, [props.getOptions]);
17851
17857
  React.useEffect(function () {
@@ -18004,16 +18010,36 @@ var LuiSearchInput = function (props) {
18004
18010
  }
18005
18011
  return null;
18006
18012
  }
18013
+ var filterRef = React.useRef(null);
18014
+ function determineFilterElement() {
18015
+ if (props.filterElement && haveFocus && results.length > 0) {
18016
+ return (React__default["default"].createElement(React__default["default"].Fragment, null,
18017
+ React__default["default"].createElement("hr", { className: "LuiSearchInput-resultSeparator" }),
18018
+ React__default["default"].createElement("div", { ref: filterRef, tabIndex: -1, "data-testid": "filter", className: "LuiSearchInput-filter" }, props.filterElement)));
18019
+ }
18020
+ return null;
18021
+ }
18007
18022
  return props.onSearch ? (React__default["default"].createElement(ControlledPassiveSearchInputComponent, __assign({ typedValue: typedValue, setTypedValue: setTypedValue }, props, { minCharactersForSearch: props.minCharactersForSearch, placeholderText: props.placeholderText, onSearch: props.onSearch, disclaimer: props.disclaimer, initialValue: props.initialValue, inputTransformer: props.inputTransformer, focusUpdate: props.focusUpdate, onClearCallback: props.onClearCallback, onClickInput: props.onClickInput, validateInput: props.validateInput }))) : (React__default["default"].createElement("div", { className: "LuiSearchInput", onClick: props.onClickInput },
18008
18023
  React__default["default"].createElement("span", { className: "LuiSearchInput-inputWrapper" },
18009
- searchIcon,
18010
- React__default["default"].createElement("input", { type: "text", className: clsx('LuiSearchInput-input'), ref: inputRef, value: typedValue, placeholder: props.placeholderText, "aria-label": "Search", onChange: function (e) { return setInputValue(e.target.value); }, onKeyDown: handleKeyDown, onFocus: function (e) {
18024
+ !props.hideSearchIcon && searchIcon,
18025
+ React__default["default"].createElement("input", { type: "text", className: clsx("LuiSearchInput-input ".concat(props.hideSearchIcon ? 'LuiSearchInput-input-hiddenIcon' : '')), ref: inputRef, value: typedValue, placeholder: props.placeholderText, "aria-label": "Search", onChange: function (e) {
18026
+ setInputValue(e.target.value);
18027
+ }, onKeyDown: handleKeyDown, onFocus: function (e) {
18011
18028
  e.target.select();
18012
18029
  retrieveResults(typedValue);
18013
18030
  setHaveFocus(true);
18014
18031
  }, style: { pointerEvents: props.externalSearch ? 'none' : 'auto' },
18015
18032
  // This timeout could be a little brittle but allows the menu to stay open long enough to click it
18016
- onBlur: function () { return setTimeout(function () { return setHaveFocus(false); }, 200); }, disabled: props.externalSearch }),
18033
+ onBlur: function (event) {
18034
+ // retain haveFocus while interacting with any supplied filter element
18035
+ // this is not a perfect solution, the filter ref will still get the focus, and maybe not have an onBlur
18036
+ if (filterRef.current &&
18037
+ event.relatedTarget === filterRef.current) {
18038
+ return;
18039
+ }
18040
+ // otherwise set the focus to false (close the results etc)
18041
+ setTimeout(function () { return setHaveFocus(false); }, 200);
18042
+ }, disabled: props.externalSearch }),
18017
18043
  cancelIcon),
18018
18044
  (isLoading || results.length > 0) && haveFocus && (React__default["default"].createElement("div", null,
18019
18045
  React__default["default"].createElement(ResultsDisplay, { results: results, selectedId: selectedId, setSelectedId: setSelectedId, selectedRef: selectedRef, onClick: selectItem, isLoading: isLoading, renderItem: props.renderItem }))),
@@ -18024,7 +18050,8 @@ var LuiSearchInput = function (props) {
18024
18050
  noOptionsMessage(typedValue) && (React__default["default"].createElement(React__default["default"].Fragment, null,
18025
18051
  React__default["default"].createElement("hr", { className: "LuiSearchInput-resultSeparator" }),
18026
18052
  React__default["default"].createElement("div", { "data-testid": "no-result-msg", className: "LuiSearchInput-disclaimer" }, noOptionsMessage(typedValue)))),
18027
- determineDisclaimer()));
18053
+ determineDisclaimer(),
18054
+ determineFilterElement()));
18028
18055
  };
18029
18056
 
18030
18057
  var resultStyle = { verticalAlign: 'middle' };