@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
|
-
|
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 (
|
36
|
-
// If there is
|
37
|
-
//
|
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
|
-
|
43
|
-
|
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,
|
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
|
+
"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"
|