@reykjavik/hanna-react 0.10.146 → 0.10.147

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.
@@ -30,7 +30,23 @@ export type AutosuggestSearchProps<T extends string | object> = {
30
30
  value: string;
31
31
  option: T;
32
32
  }) => void;
33
- onClearOptions: () => void;
33
+ onClearOptions?: () => void;
34
+ /**
35
+ * Called when the sarch input receives focus, except when the user clicks
36
+ * a suggestion.
37
+ *
38
+ * Clicking a suggestion briefly blurs the input, and then when the
39
+ * input is immediately re-focused, this event is not triggered.
40
+ */
41
+ onInputFocus?: () => void;
42
+ /**
43
+ * Called when the search input loses focus, except when the user clicks
44
+ * a suggestion and the input is immediately re-focused.
45
+ *
46
+ * Thus the triggering of this callback is thus always delayed by about 100ms
47
+ * and may occur after other elements have received focused.
48
+ */
49
+ onInputBlurred?: () => void;
34
50
  getOptionValue?: (option: T) => string;
35
51
  renderSuggestion?: (option: T, context: {
36
52
  query: string;
@@ -43,6 +59,6 @@ export type AutosuggestSearchProps<T extends string | object> = {
43
59
  renderInputField?: (inputProps: RenderInputComponentProps, texts: AutosuggestSearchI18n) => JSX.Element;
44
60
  texts?: AutosuggestSearchI18n;
45
61
  lang?: HannaLang;
46
- } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button'> & WrapperElmProps;
62
+ } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button' | 'invalid' | 'errorMessage' | 'assistText'> & WrapperElmProps<'div', 'aria-label'>;
47
63
  export declare const AutosuggestSearch: <T extends string | object>(props: AutosuggestSearchProps<T>) => JSX.Element;
48
64
  export {};
@@ -40,20 +40,40 @@ const AutosuggestSearch = (props) => {
40
40
  ? opt.value.toString()
41
41
  : '', renderSuggestion = (opt) => typeof opt === 'object' && 'label' in opt && opt.label != null
42
42
  ? opt.label.toString()
43
- : getOptionValue(opt), InputComponent = SearchInput_js_1.default, renderInputField, wrapperProps, } = props;
43
+ : getOptionValue(opt), InputComponent = SearchInput_js_1.default, renderInputField, invalid, errorMessage, assistText, onInputFocus, onInputBlurred, } = props;
44
44
  const [inputValue, setInputValue] = (0, utils_js_1.useMixedControlState)(props, 'inputValue', '');
45
45
  const inputRef = (0, react_1.useRef)(null);
46
46
  const txt = (0, i18n_1.getTexts)(props, exports.defaultAutosuggestSearchTexts);
47
47
  const showEmptyMessage = !options.length && emptyMessage;
48
+ const _a = props.wrapperProps || {}, { className } = _a, wrapperProps = tslib_1.__rest(_a, ["className"]);
49
+ const blurTimeout = (0, react_1.useRef)();
50
+ const inputFocusProps = onInputFocus || onInputBlurred
51
+ ? {
52
+ onFocus: () => {
53
+ if (!blurTimeout.current) {
54
+ onInputFocus === null || onInputFocus === void 0 ? void 0 : onInputFocus();
55
+ }
56
+ clearTimeout(blurTimeout.current);
57
+ blurTimeout.current = undefined;
58
+ },
59
+ onBlur: () => {
60
+ clearTimeout(blurTimeout.current);
61
+ blurTimeout.current = setTimeout(() => {
62
+ blurTimeout.current = undefined;
63
+ onInputBlurred === null || onInputBlurred === void 0 ? void 0 : onInputBlurred();
64
+ }, 100);
65
+ },
66
+ }
67
+ : undefined;
48
68
  return (react_1.default.createElement(react_autosuggest_1.default, { theme: {
49
- container: 'AutosuggestSearch',
69
+ container: `AutosuggestSearch ${className || ''}`,
50
70
  containerOpen: 'AutosuggestSearch--open',
51
71
  suggestionsContainer: 'AutosuggestSearch__container',
52
72
  suggestionsContainerOpen: 'AutosuggestSearch__container--open',
53
73
  suggestionsList: (0, hanna_utils_1.modifiedClass)('AutosuggestSearch__list', itemActionIcon && `action--${itemActionIcon}`),
54
74
  suggestion: 'AutosuggestSearch__item',
55
75
  suggestionHighlighted: 'AutosuggestSearch__item--highlighted',
56
- }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions, onSuggestionsFetchRequested: ( /* { value } */) => {
76
+ }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions || (() => undefined), onSuggestionsFetchRequested: ( /* { value } */) => {
57
77
  // weirdly required prop, but we don't need to do anything here
58
78
  // as we run onInput on input change below.
59
79
  }, getSuggestionValue: showEmptyMessage
@@ -75,22 +95,18 @@ const AutosuggestSearch = (props) => {
75
95
  contents = (react_1.default.createElement("div", { className: (0, hanna_utils_1.modifiedClass)('AutosuggestSearch__emptyMessage', type !== 'empty' && type) }, message));
76
96
  }
77
97
  return (react_1.default.createElement("div", Object.assign({}, containerProps, { "aria-label": options.length ? txt.suggestionsLabel : undefined }), contents));
78
- }, inputProps: {
79
- ref: inputRef,
80
- value: inputValue,
81
- onChange: (_, { newValue, method }) => {
98
+ }, inputProps: Object.assign({ ref: inputRef, value: inputValue, onChange: (_, { newValue, method }) => {
82
99
  if (!inputChangeMethods.has(method)) {
83
100
  return;
84
101
  }
85
102
  onInput(newValue);
86
103
  setInputValue(newValue);
87
- },
88
- }, renderInputComponent: renderInputField
104
+ } }, inputFocusProps), renderInputComponent: renderInputField
89
105
  ? (inputProps) => renderInputField(inputProps, txt)
90
106
  : (inputProps) => {
91
107
  /* prettier-ignore */
92
108
  const { className, type, disabled, readOnly, required, children, ref, defaultValue } = inputProps, siteSearchProps = tslib_1.__rest(inputProps, ["className", "type", "disabled", "readOnly", "required", "children", "ref", "defaultValue"]);
93
- return (react_1.default.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
109
+ return (react_1.default.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, invalid: invalid, errorMessage: errorMessage, assistText: assistText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
94
110
  } }));
95
111
  };
96
112
  exports.AutosuggestSearch = AutosuggestSearch;
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.10.147
8
+
9
+ _2025-02-25_
10
+
11
+ - `AutosuggestSearch`:
12
+ - feat: Add props `errorMessage`, `invalid`, `assistText`
13
+ - feat: Add props `onInputFocus` and `onInputBlur`
14
+ - fix: `wrapperProps.className` overrides wrapper's default class
15
+ - fix: Make `onClearOptions` prop optional
16
+
7
17
  ## 0.10.146
8
18
 
9
19
  _2025-02-10_
@@ -30,7 +30,23 @@ export type AutosuggestSearchProps<T extends string | object> = {
30
30
  value: string;
31
31
  option: T;
32
32
  }) => void;
33
- onClearOptions: () => void;
33
+ onClearOptions?: () => void;
34
+ /**
35
+ * Called when the sarch input receives focus, except when the user clicks
36
+ * a suggestion.
37
+ *
38
+ * Clicking a suggestion briefly blurs the input, and then when the
39
+ * input is immediately re-focused, this event is not triggered.
40
+ */
41
+ onInputFocus?: () => void;
42
+ /**
43
+ * Called when the search input loses focus, except when the user clicks
44
+ * a suggestion and the input is immediately re-focused.
45
+ *
46
+ * Thus the triggering of this callback is thus always delayed by about 100ms
47
+ * and may occur after other elements have received focused.
48
+ */
49
+ onInputBlurred?: () => void;
34
50
  getOptionValue?: (option: T) => string;
35
51
  renderSuggestion?: (option: T, context: {
36
52
  query: string;
@@ -43,6 +59,6 @@ export type AutosuggestSearchProps<T extends string | object> = {
43
59
  renderInputField?: (inputProps: RenderInputComponentProps, texts: AutosuggestSearchI18n) => JSX.Element;
44
60
  texts?: AutosuggestSearchI18n;
45
61
  lang?: HannaLang;
46
- } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button'> & WrapperElmProps;
62
+ } & Pick<SearchInputProps, 'onSubmit' | 'onButtonClick' | 'button' | 'invalid' | 'errorMessage' | 'assistText'> & WrapperElmProps<'div', 'aria-label'>;
47
63
  export declare const AutosuggestSearch: <T extends string | object>(props: AutosuggestSearchProps<T>) => JSX.Element;
48
64
  export {};
@@ -37,20 +37,40 @@ export const AutosuggestSearch = (props) => {
37
37
  ? opt.value.toString()
38
38
  : '', renderSuggestion = (opt) => typeof opt === 'object' && 'label' in opt && opt.label != null
39
39
  ? opt.label.toString()
40
- : getOptionValue(opt), InputComponent = SearchInput, renderInputField, wrapperProps, } = props;
40
+ : getOptionValue(opt), InputComponent = SearchInput, renderInputField, invalid, errorMessage, assistText, onInputFocus, onInputBlurred, } = props;
41
41
  const [inputValue, setInputValue] = useMixedControlState(props, 'inputValue', '');
42
42
  const inputRef = useRef(null);
43
43
  const txt = getTexts(props, defaultAutosuggestSearchTexts);
44
44
  const showEmptyMessage = !options.length && emptyMessage;
45
+ const _a = props.wrapperProps || {}, { className } = _a, wrapperProps = __rest(_a, ["className"]);
46
+ const blurTimeout = useRef();
47
+ const inputFocusProps = onInputFocus || onInputBlurred
48
+ ? {
49
+ onFocus: () => {
50
+ if (!blurTimeout.current) {
51
+ onInputFocus === null || onInputFocus === void 0 ? void 0 : onInputFocus();
52
+ }
53
+ clearTimeout(blurTimeout.current);
54
+ blurTimeout.current = undefined;
55
+ },
56
+ onBlur: () => {
57
+ clearTimeout(blurTimeout.current);
58
+ blurTimeout.current = setTimeout(() => {
59
+ blurTimeout.current = undefined;
60
+ onInputBlurred === null || onInputBlurred === void 0 ? void 0 : onInputBlurred();
61
+ }, 100);
62
+ },
63
+ }
64
+ : undefined;
45
65
  return (React.createElement(Autosuggest, { theme: {
46
- container: 'AutosuggestSearch',
66
+ container: `AutosuggestSearch ${className || ''}`,
47
67
  containerOpen: 'AutosuggestSearch--open',
48
68
  suggestionsContainer: 'AutosuggestSearch__container',
49
69
  suggestionsContainerOpen: 'AutosuggestSearch__container--open',
50
70
  suggestionsList: modifiedClass('AutosuggestSearch__list', itemActionIcon && `action--${itemActionIcon}`),
51
71
  suggestion: 'AutosuggestSearch__item',
52
72
  suggestionHighlighted: 'AutosuggestSearch__item--highlighted',
53
- }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions, onSuggestionsFetchRequested: ( /* { value } */) => {
73
+ }, focusInputOnSuggestionClick: true, suggestions: showEmptyMessage ? [true] : options, onSuggestionsClearRequested: onClearOptions || (() => undefined), onSuggestionsFetchRequested: ( /* { value } */) => {
54
74
  // weirdly required prop, but we don't need to do anything here
55
75
  // as we run onInput on input change below.
56
76
  }, getSuggestionValue: showEmptyMessage
@@ -72,21 +92,17 @@ export const AutosuggestSearch = (props) => {
72
92
  contents = (React.createElement("div", { className: modifiedClass('AutosuggestSearch__emptyMessage', type !== 'empty' && type) }, message));
73
93
  }
74
94
  return (React.createElement("div", Object.assign({}, containerProps, { "aria-label": options.length ? txt.suggestionsLabel : undefined }), contents));
75
- }, inputProps: {
76
- ref: inputRef,
77
- value: inputValue,
78
- onChange: (_, { newValue, method }) => {
95
+ }, inputProps: Object.assign({ ref: inputRef, value: inputValue, onChange: (_, { newValue, method }) => {
79
96
  if (!inputChangeMethods.has(method)) {
80
97
  return;
81
98
  }
82
99
  onInput(newValue);
83
100
  setInputValue(newValue);
84
- },
85
- }, renderInputComponent: renderInputField
101
+ } }, inputFocusProps), renderInputComponent: renderInputField
86
102
  ? (inputProps) => renderInputField(inputProps, txt)
87
103
  : (inputProps) => {
88
104
  /* prettier-ignore */
89
105
  const { className, type, disabled, readOnly, required, children, ref, defaultValue } = inputProps, siteSearchProps = __rest(inputProps, ["className", "type", "disabled", "readOnly", "required", "children", "ref", "defaultValue"]);
90
- return (React.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
106
+ return (React.createElement(InputComponent, Object.assign({ lang: props.lang, defaultValue: defaultValue }, siteSearchProps, { inputRef: ref, button: button, label: txt.inputLabel, placeholder: txt.placeholder, buttonText: txt.buttonText, invalid: invalid, errorMessage: errorMessage, assistText: assistText, onSubmit: onSubmit && (() => onSubmit(inputValue)), onButtonClick: onButtonClick && (() => onButtonClick(inputValue)) })));
91
107
  } }));
92
108
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/hanna-react",
3
- "version": "0.10.146",
3
+ "version": "0.10.147",
4
4
  "author": "Reykjavík (http://www.reykjavik.is)",
5
5
  "contributors": [
6
6
  "Hugsmiðjan ehf (http://www.hugsmidjan.is)",