@oneblink/apps-react 4.3.0-beta.4 → 4.3.0-beta.5

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.
@@ -15,45 +15,47 @@ function AutocompleteDropdown({ id, label, value, placeholder, required, disable
15
15
  onClose();
16
16
  }, [onChangeLabel, onChangeValue, onClose]);
17
17
  const handleClickOption = React.useCallback((event, option) => {
18
- var _a;
19
18
  console.log('Selected element option in autocomplete', option);
20
19
  event.preventDefault();
21
20
  event.stopPropagation();
22
21
  onSelectOption(option);
23
- (_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.blur();
24
- }, [onSelectOption, id]);
22
+ }, [onSelectOption]);
25
23
  const onFocus = React.useCallback(() => {
26
24
  setCurrentFocusedOptionIndex(0);
27
25
  onOpen();
28
26
  }, [onOpen]);
29
27
  // When moving away from the input, if this is no value remove
30
28
  // the label to show the user they have not selected a value
31
- const handleBlur = React.useCallback(() => {
29
+ const handleBlur = React.useCallback((inputOption) => {
32
30
  setIsDirty();
33
31
  setError(null);
34
32
  onClose();
35
- if (!value && Array.isArray(options)) {
36
- // If there is no option currently selected but the typed in label
37
- // matches an option's label, set that option as the value, otherwise remove label
38
- if (label) {
39
- const lowerCase = label.toLowerCase();
33
+ if (Array.isArray(options)) {
34
+ // If there is a label that resembles an option, set that option to be the new value.
35
+ // UNLESS it was the previously selected label. Else, clear the label and the value.
36
+ if (inputOption.label) {
37
+ const lowerCase = inputOption.label.toLowerCase();
40
38
  const option = options.find((option) => option.label.toLowerCase() === lowerCase);
41
39
  if (option) {
42
- console.log('Setting value after blurring away from autocomplete');
43
- onSelectOption(option);
40
+ // If the new option is not equal to the option that is currently selected,
41
+ // we want to update to the new option to ensure that it gets changed.
42
+ if (inputOption.value !== option.value) {
43
+ console.log('Setting value after blurring away from autocomplete');
44
+ onSelectOption(option);
45
+ }
44
46
  return;
45
47
  }
46
48
  }
47
49
  console.log('Removing label after blurring away from autocomplete');
50
+ onChangeValue(undefined);
48
51
  onChangeLabel('');
49
52
  }
50
53
  }, [
51
- label,
52
54
  onChangeLabel,
55
+ onChangeValue,
53
56
  onClose,
54
57
  onSelectOption,
55
58
  options,
56
- value,
57
59
  setIsDirty,
58
60
  ]);
59
61
  const onKeyDown = React.useCallback((event) => {
@@ -99,10 +101,8 @@ function AutocompleteDropdown({ id, label, value, placeholder, required, disable
99
101
  const newLabel = e.target.value;
100
102
  onOpen();
101
103
  setCurrentFocusedOptionIndex(0);
102
- // Remove value when changing label
103
- onChangeValue(undefined);
104
104
  onChangeLabel(newLabel);
105
- }, [onChangeLabel, onChangeValue, onOpen]);
105
+ }, [onChangeLabel, onOpen]);
106
106
  React.useEffect(() => {
107
107
  setError(null);
108
108
  if (!isOpen || label.length < searchMinCharacters) {
@@ -138,6 +138,13 @@ function AutocompleteDropdown({ id, label, value, placeholder, required, disable
138
138
  abortController.abort();
139
139
  };
140
140
  }, [isOpen, label, onSearch, searchDebounceMs, searchMinCharacters]);
141
+ React.useEffect(() => {
142
+ //If there is no value set, we want to clear the label
143
+ //This is to satisfy lookups that return undefined.
144
+ if (!value) {
145
+ onChangeLabel('');
146
+ }
147
+ }, [onChangeLabel, value]);
141
148
  const isShowingLoading = isFetchingOptions || !!isLoading;
142
149
  const isShowingValid = !isShowingLoading && value !== undefined;
143
150
  const isShowingError = !isShowingLoading && !!hasError;
@@ -150,7 +157,7 @@ function AutocompleteDropdown({ id, label, value, placeholder, required, disable
150
157
  'is-loading': isShowingLoading,
151
158
  'has-icons-right': isShowingValid || isShowingError,
152
159
  }) },
153
- React.createElement("input", { type: "text", placeholder: placeholder, id: id, autoComplete: "off", className: "cypress-autocomplete-control input ob-input", required: required, value: label, disabled: disabled, onFocus: onFocus, onBlur: handleBlur, onKeyDown: onKeyDown, onChange: handleChangeLabel }),
160
+ React.createElement("input", { type: "text", placeholder: placeholder, id: id, autoComplete: "off", className: "cypress-autocomplete-control input ob-input", required: required, value: label, disabled: disabled, onFocus: onFocus, onBlur: () => handleBlur({ label, value }), onKeyDown: onKeyDown, onChange: handleChangeLabel }),
154
161
  isShowingValid && (React.createElement("span", { className: " ob-input-icon icon is-small is-right" },
155
162
  React.createElement("i", { className: "material-icons is-size-5 has-text-success" }, "check"))),
156
163
  isShowingError && (React.createElement("span", { className: " ob-input-icon icon is-small is-right" },
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompleteDropdown.js","sourceRoot":"","sources":["../../../src/components/renderer/AutocompleteDropdown.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport clsx from 'clsx'\nimport { Sentry } from '@oneblink/apps'\n\nimport useBooleanState from '../../hooks/useBooleanState'\nimport { IsDirtyProps } from '../../types/form'\n\ntype AutocompleteOption<T> = {\n label: string\n value: string\n data?: T\n}\n\ntype Props<T> = {\n id: string\n label: string\n value: unknown | undefined\n placeholder: string | undefined\n required: boolean | undefined\n disabled: boolean | undefined\n isLoading?: boolean\n hasError?: boolean\n validationMessage: string | undefined\n displayValidationMessage: boolean\n searchDebounceMs: number\n searchMinCharacters: number\n onChangeValue: (\n newValue: string | undefined,\n data?: T,\n ) => Promise<void> | void\n onChangeLabel: (newLabel: string) => void\n onSearch: (\n label: string,\n abortSignal: AbortSignal,\n ) => Promise<AutocompleteOption<T>[]>\n} & IsDirtyProps\n\nfunction AutocompleteDropdown<T>({\n id,\n label,\n value,\n placeholder,\n required,\n disabled,\n validationMessage,\n displayValidationMessage,\n searchDebounceMs,\n searchMinCharacters,\n isLoading,\n hasError,\n onChangeValue,\n onChangeLabel,\n onSearch,\n isDirty,\n setIsDirty,\n}: Props<T>) {\n const optionsContainerElement = React.useRef<HTMLDivElement>(null)\n const [currentFocusedOptionIndex, setCurrentFocusedOptionIndex] =\n React.useState(0)\n const [options, setOptions] = React.useState<AutocompleteOption<T>[]>([])\n const [error, setError] = React.useState<Error | null>(null)\n const [isFetchingOptions, setIsFetchingOptions] = React.useState(false)\n const [isOpen, onOpen, onClose] = useBooleanState(false)\n\n const onSelectOption = React.useCallback(\n (option: AutocompleteOption<T>) => {\n onChangeLabel(option.label)\n onChangeValue(option.value, option.data)\n onClose()\n },\n [onChangeLabel, onChangeValue, onClose],\n )\n\n const handleClickOption = React.useCallback(\n (\n event: React.MouseEvent<HTMLAnchorElement, MouseEvent>,\n option: AutocompleteOption<T>,\n ) => {\n console.log('Selected element option in autocomplete', option)\n\n event.preventDefault()\n event.stopPropagation()\n\n onSelectOption(option)\n document.getElementById(id)?.blur()\n },\n [onSelectOption, id],\n )\n\n const onFocus = React.useCallback(() => {\n setCurrentFocusedOptionIndex(0)\n onOpen()\n }, [onOpen])\n\n // When moving away from the input, if this is no value remove\n // the label to show the user they have not selected a value\n const handleBlur = React.useCallback(() => {\n setIsDirty()\n setError(null)\n onClose()\n\n if (!value && Array.isArray(options)) {\n // If there is no option currently selected but the typed in label\n // matches an option's label, set that option as the value, otherwise remove label\n if (label) {\n const lowerCase = label.toLowerCase()\n const option = options.find(\n (option) => option.label.toLowerCase() === lowerCase,\n )\n if (option) {\n console.log('Setting value after blurring away from autocomplete')\n onSelectOption(option)\n return\n }\n }\n console.log('Removing label after blurring away from autocomplete')\n onChangeLabel('')\n }\n }, [\n label,\n onChangeLabel,\n onClose,\n onSelectOption,\n options,\n value,\n setIsDirty,\n ])\n\n const onKeyDown = React.useCallback<\n React.KeyboardEventHandler<HTMLInputElement>\n >(\n (event) => {\n if (!options) {\n return\n }\n const enterPressed = event.keyCode === 13\n const upArrowPressed = event.keyCode === 38\n const downArrowPressed = event.keyCode === 40\n if (!upArrowPressed && !downArrowPressed && !enterPressed) {\n return\n }\n\n event.preventDefault()\n\n const previousFocusedOptionIndex = currentFocusedOptionIndex\n let nextFocusedOptionIndex = currentFocusedOptionIndex\n if (upArrowPressed) {\n nextFocusedOptionIndex = Math.max(0, currentFocusedOptionIndex - 1)\n } else if (downArrowPressed) {\n nextFocusedOptionIndex = Math.min(\n options.length - 1,\n currentFocusedOptionIndex + 1,\n )\n } else if (enterPressed) {\n const option = options[nextFocusedOptionIndex]\n if (option) {\n onSelectOption(option)\n }\n }\n\n // If the index has changed, need to ensure the active option is visible\n if (\n previousFocusedOptionIndex !== nextFocusedOptionIndex &&\n optionsContainerElement.current\n ) {\n const activeStepElement = optionsContainerElement.current.querySelector(\n `.ob-autocomplete__drop-down-item-${nextFocusedOptionIndex}`,\n )\n if (activeStepElement) {\n activeStepElement.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'start',\n })\n }\n setCurrentFocusedOptionIndex(nextFocusedOptionIndex)\n }\n },\n [currentFocusedOptionIndex, options, onSelectOption],\n )\n\n const handleChangeLabel = React.useCallback<\n React.ChangeEventHandler<HTMLInputElement>\n >(\n (e) => {\n const newLabel = e.target.value\n onOpen()\n setCurrentFocusedOptionIndex(0)\n\n // Remove value when changing label\n onChangeValue(undefined)\n onChangeLabel(newLabel)\n },\n [onChangeLabel, onChangeValue, onOpen],\n )\n\n React.useEffect(() => {\n setError(null)\n\n if (!isOpen || label.length < searchMinCharacters) {\n setIsFetchingOptions(false)\n return\n }\n\n setIsFetchingOptions(true)\n\n let ignore = false\n const abortController = new AbortController()\n\n const timeoutId = setTimeout(async () => {\n let newOptions: AutocompleteOption<T>[] = []\n let newError = null\n\n try {\n newOptions = await onSearch(label, abortController.signal)\n } catch (error) {\n // Cancelling will throw an error.\n if (!abortController.signal.aborted) {\n console.warn('Error while fetching autocomplete options', error)\n Sentry.captureException(error)\n newError = error as Error\n }\n }\n if (!ignore) {\n setError(newError)\n setOptions(newOptions)\n setIsFetchingOptions(false)\n }\n }, searchDebounceMs)\n\n return () => {\n ignore = true\n clearTimeout(timeoutId)\n abortController.abort()\n }\n }, [isOpen, label, onSearch, searchDebounceMs, searchMinCharacters])\n\n const isShowingLoading = isFetchingOptions || !!isLoading\n const isShowingValid = !isShowingLoading && value !== undefined\n const isShowingError = !isShowingLoading && !!hasError\n\n return (\n <>\n <div\n className={clsx('dropdown', {\n 'is-active': isOpen && Array.isArray(options),\n })}\n >\n <div className=\"field\">\n <div\n className={clsx(\n 'cypress-autocomplete-field-control control is-expanded',\n {\n 'is-loading': isShowingLoading,\n 'has-icons-right': isShowingValid || isShowingError,\n },\n )}\n >\n <input\n type=\"text\"\n placeholder={placeholder}\n id={id}\n autoComplete=\"off\"\n className=\"cypress-autocomplete-control input ob-input\"\n required={required}\n value={label}\n disabled={disabled}\n onFocus={onFocus}\n onBlur={handleBlur}\n onKeyDown={onKeyDown}\n onChange={handleChangeLabel}\n />\n {isShowingValid && (\n <span className=\" ob-input-icon icon is-small is-right\">\n <i className=\"material-icons is-size-5 has-text-success\">\n check\n </i>\n </span>\n )}\n {isShowingError && (\n <span className=\" ob-input-icon icon is-small is-right\">\n <i className=\"material-icons is-size-5 has-text-danger\">\n error\n </i>\n </span>\n )}\n </div>\n </div>\n\n <div className=\"dropdown-menu\">\n <div\n ref={optionsContainerElement}\n className=\"ob-autocomplete__dropdown-content dropdown-content cypress-autocomplete-dropdown-content\"\n >\n {error ? (\n <a className=\"dropdown-item cypress-autocomplete-error ob-autocomplete__drop-down-item-error\">\n <span className=\"has-text-danger\">{error.message}</span>\n </a>\n ) : label.length < searchMinCharacters ? (\n <a className=\"dropdown-item cypress-max-characters ob-autocomplete__drop-down-item-max-characters\">\n <i>\n Enter at least {searchMinCharacters} character(s) to search\n </i>\n </a>\n ) : isShowingLoading ? (\n <a className=\"dropdown-item cypress-searching-options ob-autocomplete__drop-down-item-searching\">\n <i>Searching...</i>\n </a>\n ) : options && options.length ? (\n options.map((option, index) => (\n <a\n key={option.value}\n className={clsx(\n `dropdown-item cypress-autocomplete-dropdown-item ob-autocomplete__drop-down-item-${index}`,\n {\n 'is-active': currentFocusedOptionIndex === index,\n },\n )}\n onMouseDown={(e) => handleClickOption(e, option)}\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{\n __html: highlightLabel(option.label, label),\n }}\n />\n ))\n ) : (\n <a className=\"dropdown-item cypress-no-matches-found ob-autocomplete__drop-down-item-no-matches\">\n <i>No matches found</i>\n </a>\n )}\n </div>\n </div>\n </div>\n\n {(isDirty || displayValidationMessage) &&\n !!validationMessage &&\n !isShowingLoading && (\n <div role=\"alert\" className=\"has-margin-top-8\">\n <div className=\"has-text-danger ob-error__text cypress-validation-message\">\n {validationMessage}\n </div>\n </div>\n )}\n </>\n )\n}\n\nexport default React.memo(AutocompleteDropdown) as typeof AutocompleteDropdown\n\nfunction highlightLabel(text: string, phrase: string) {\n if (phrase) {\n text = text.replace(\n new RegExp(\n '(' +\n phrase.replace(\n /[`~!@#$%^&*()_|+\\-=?;:'\",.<>{}[\\]\\\\/]/gi,\n (char: string) => `\\\\${char}`,\n ) +\n ')',\n 'gi',\n ),\n '<b>$1</b>',\n )\n }\n\n return text\n}\n"]}
1
+ {"version":3,"file":"AutocompleteDropdown.js","sourceRoot":"","sources":["../../../src/components/renderer/AutocompleteDropdown.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport clsx from 'clsx'\nimport { Sentry } from '@oneblink/apps'\n\nimport useBooleanState from '../../hooks/useBooleanState'\nimport { IsDirtyProps } from '../../types/form'\n\ntype AutocompleteOption<T> = {\n label: string\n value: string\n data?: T\n}\n\ntype Props<T> = {\n id: string\n label: string\n value: unknown | undefined\n placeholder: string | undefined\n required: boolean | undefined\n disabled: boolean | undefined\n isLoading?: boolean\n hasError?: boolean\n validationMessage: string | undefined\n displayValidationMessage: boolean\n searchDebounceMs: number\n searchMinCharacters: number\n onChangeValue: (\n newValue: string | undefined,\n data?: T,\n ) => Promise<void> | void\n onChangeLabel: (newLabel: string) => void\n onSearch: (\n label: string,\n abortSignal: AbortSignal,\n ) => Promise<AutocompleteOption<T>[]>\n} & IsDirtyProps\n\nfunction AutocompleteDropdown<T>({\n id,\n label,\n value,\n placeholder,\n required,\n disabled,\n validationMessage,\n displayValidationMessage,\n searchDebounceMs,\n searchMinCharacters,\n isLoading,\n hasError,\n onChangeValue,\n onChangeLabel,\n onSearch,\n isDirty,\n setIsDirty,\n}: Props<T>) {\n const optionsContainerElement = React.useRef<HTMLDivElement>(null)\n const [currentFocusedOptionIndex, setCurrentFocusedOptionIndex] =\n React.useState(0)\n const [options, setOptions] = React.useState<AutocompleteOption<T>[]>([])\n const [error, setError] = React.useState<Error | null>(null)\n const [isFetchingOptions, setIsFetchingOptions] = React.useState(false)\n const [isOpen, onOpen, onClose] = useBooleanState(false)\n\n const onSelectOption = React.useCallback(\n (option: AutocompleteOption<T>) => {\n onChangeLabel(option.label)\n onChangeValue(option.value, option.data)\n onClose()\n },\n [onChangeLabel, onChangeValue, onClose],\n )\n\n const handleClickOption = React.useCallback(\n (\n event: React.MouseEvent<HTMLAnchorElement, MouseEvent>,\n option: AutocompleteOption<T>,\n ) => {\n console.log('Selected element option in autocomplete', option)\n\n event.preventDefault()\n event.stopPropagation()\n\n onSelectOption(option)\n },\n [onSelectOption],\n )\n\n const onFocus = React.useCallback(() => {\n setCurrentFocusedOptionIndex(0)\n onOpen()\n }, [onOpen])\n\n // When moving away from the input, if this is no value remove\n // the label to show the user they have not selected a value\n const handleBlur = React.useCallback(\n (inputOption: { label: string; value: unknown | undefined }) => {\n setIsDirty()\n setError(null)\n onClose()\n\n if (Array.isArray(options)) {\n // If there is a label that resembles an option, set that option to be the new value.\n // UNLESS it was the previously selected label. Else, clear the label and the value.\n if (inputOption.label) {\n const lowerCase = inputOption.label.toLowerCase()\n const option = options.find(\n (option) => option.label.toLowerCase() === lowerCase,\n )\n if (option) {\n // If the new option is not equal to the option that is currently selected,\n // we want to update to the new option to ensure that it gets changed.\n if (inputOption.value !== option.value) {\n console.log('Setting value after blurring away from autocomplete')\n onSelectOption(option)\n }\n return\n }\n }\n\n console.log('Removing label after blurring away from autocomplete')\n onChangeValue(undefined)\n onChangeLabel('')\n }\n },\n [\n onChangeLabel,\n onChangeValue,\n onClose,\n onSelectOption,\n options,\n setIsDirty,\n ],\n )\n\n const onKeyDown = React.useCallback<\n React.KeyboardEventHandler<HTMLInputElement>\n >(\n (event) => {\n if (!options) {\n return\n }\n const enterPressed = event.keyCode === 13\n const upArrowPressed = event.keyCode === 38\n const downArrowPressed = event.keyCode === 40\n if (!upArrowPressed && !downArrowPressed && !enterPressed) {\n return\n }\n\n event.preventDefault()\n\n const previousFocusedOptionIndex = currentFocusedOptionIndex\n let nextFocusedOptionIndex = currentFocusedOptionIndex\n if (upArrowPressed) {\n nextFocusedOptionIndex = Math.max(0, currentFocusedOptionIndex - 1)\n } else if (downArrowPressed) {\n nextFocusedOptionIndex = Math.min(\n options.length - 1,\n currentFocusedOptionIndex + 1,\n )\n } else if (enterPressed) {\n const option = options[nextFocusedOptionIndex]\n if (option) {\n onSelectOption(option)\n }\n }\n\n // If the index has changed, need to ensure the active option is visible\n if (\n previousFocusedOptionIndex !== nextFocusedOptionIndex &&\n optionsContainerElement.current\n ) {\n const activeStepElement = optionsContainerElement.current.querySelector(\n `.ob-autocomplete__drop-down-item-${nextFocusedOptionIndex}`,\n )\n if (activeStepElement) {\n activeStepElement.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'start',\n })\n }\n setCurrentFocusedOptionIndex(nextFocusedOptionIndex)\n }\n },\n [currentFocusedOptionIndex, options, onSelectOption],\n )\n\n const handleChangeLabel = React.useCallback<\n React.ChangeEventHandler<HTMLInputElement>\n >(\n (e) => {\n const newLabel = e.target.value\n onOpen()\n setCurrentFocusedOptionIndex(0)\n\n onChangeLabel(newLabel)\n },\n [onChangeLabel, onOpen],\n )\n\n React.useEffect(() => {\n setError(null)\n\n if (!isOpen || label.length < searchMinCharacters) {\n setIsFetchingOptions(false)\n return\n }\n\n setIsFetchingOptions(true)\n\n let ignore = false\n const abortController = new AbortController()\n\n const timeoutId = setTimeout(async () => {\n let newOptions: AutocompleteOption<T>[] = []\n let newError = null\n\n try {\n newOptions = await onSearch(label, abortController.signal)\n } catch (error) {\n // Cancelling will throw an error.\n if (!abortController.signal.aborted) {\n console.warn('Error while fetching autocomplete options', error)\n Sentry.captureException(error)\n newError = error as Error\n }\n }\n if (!ignore) {\n setError(newError)\n setOptions(newOptions)\n setIsFetchingOptions(false)\n }\n }, searchDebounceMs)\n\n return () => {\n ignore = true\n clearTimeout(timeoutId)\n abortController.abort()\n }\n }, [isOpen, label, onSearch, searchDebounceMs, searchMinCharacters])\n\n React.useEffect(() => {\n //If there is no value set, we want to clear the label\n //This is to satisfy lookups that return undefined.\n if (!value) {\n onChangeLabel('')\n }\n }, [onChangeLabel, value])\n\n const isShowingLoading = isFetchingOptions || !!isLoading\n const isShowingValid = !isShowingLoading && value !== undefined\n const isShowingError = !isShowingLoading && !!hasError\n\n return (\n <>\n <div\n className={clsx('dropdown', {\n 'is-active': isOpen && Array.isArray(options),\n })}\n >\n <div className=\"field\">\n <div\n className={clsx(\n 'cypress-autocomplete-field-control control is-expanded',\n {\n 'is-loading': isShowingLoading,\n 'has-icons-right': isShowingValid || isShowingError,\n },\n )}\n >\n <input\n type=\"text\"\n placeholder={placeholder}\n id={id}\n autoComplete=\"off\"\n className=\"cypress-autocomplete-control input ob-input\"\n required={required}\n value={label}\n disabled={disabled}\n onFocus={onFocus}\n onBlur={() => handleBlur({ label, value })}\n onKeyDown={onKeyDown}\n onChange={handleChangeLabel}\n />\n {isShowingValid && (\n <span className=\" ob-input-icon icon is-small is-right\">\n <i className=\"material-icons is-size-5 has-text-success\">\n check\n </i>\n </span>\n )}\n {isShowingError && (\n <span className=\" ob-input-icon icon is-small is-right\">\n <i className=\"material-icons is-size-5 has-text-danger\">\n error\n </i>\n </span>\n )}\n </div>\n </div>\n\n <div className=\"dropdown-menu\">\n <div\n ref={optionsContainerElement}\n className=\"ob-autocomplete__dropdown-content dropdown-content cypress-autocomplete-dropdown-content\"\n >\n {error ? (\n <a className=\"dropdown-item cypress-autocomplete-error ob-autocomplete__drop-down-item-error\">\n <span className=\"has-text-danger\">{error.message}</span>\n </a>\n ) : label.length < searchMinCharacters ? (\n <a className=\"dropdown-item cypress-max-characters ob-autocomplete__drop-down-item-max-characters\">\n <i>\n Enter at least {searchMinCharacters} character(s) to search\n </i>\n </a>\n ) : isShowingLoading ? (\n <a className=\"dropdown-item cypress-searching-options ob-autocomplete__drop-down-item-searching\">\n <i>Searching...</i>\n </a>\n ) : options && options.length ? (\n options.map((option, index) => (\n <a\n key={option.value}\n className={clsx(\n `dropdown-item cypress-autocomplete-dropdown-item ob-autocomplete__drop-down-item-${index}`,\n {\n 'is-active': currentFocusedOptionIndex === index,\n },\n )}\n onMouseDown={(e) => handleClickOption(e, option)}\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{\n __html: highlightLabel(option.label, label),\n }}\n />\n ))\n ) : (\n <a className=\"dropdown-item cypress-no-matches-found ob-autocomplete__drop-down-item-no-matches\">\n <i>No matches found</i>\n </a>\n )}\n </div>\n </div>\n </div>\n\n {(isDirty || displayValidationMessage) &&\n !!validationMessage &&\n !isShowingLoading && (\n <div role=\"alert\" className=\"has-margin-top-8\">\n <div className=\"has-text-danger ob-error__text cypress-validation-message\">\n {validationMessage}\n </div>\n </div>\n )}\n </>\n )\n}\n\nexport default React.memo(AutocompleteDropdown) as typeof AutocompleteDropdown\n\nfunction highlightLabel(text: string, phrase: string) {\n if (phrase) {\n text = text.replace(\n new RegExp(\n '(' +\n phrase.replace(\n /[`~!@#$%^&*()_|+\\-=?;:'\",.<>{}[\\]\\\\/]/gi,\n (char: string) => `\\\\${char}`,\n ) +\n ')',\n 'gi',\n ),\n '<b>$1</b>',\n )\n }\n\n return text\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oneblink/apps-react",
3
3
  "description": "Helper functions for OneBlink apps in ReactJS.",
4
- "version": "4.3.0-beta.4",
4
+ "version": "4.3.0-beta.5",
5
5
  "author": "OneBlink <developers@oneblink.io> (https://oneblink.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/oneblink/apps-react/issues"