@constructor-io/constructorio-ui-autocomplete 1.23.25 → 1.23.27

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.
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const react_1 = tslib_1.__importStar(require("react"));
5
+ const CioAutocompleteProvider_1 = require("../CioAutocompleteProvider");
6
+ function CustomSectionItem(props) {
7
+ const { renderItem, item, query } = props;
8
+ const { getItemProps } = (0, react_1.useContext)(CioAutocompleteProvider_1.CioAutocompleteContext);
9
+ const ref = (0, react_1.useRef)(null);
10
+ // eslint-disable-next-line react-hooks/exhaustive-deps
11
+ const customElement = (0, react_1.useMemo)(() => renderItem({ item, query, getItemProps }), [item]);
12
+ const isDomElement = customElement instanceof HTMLElement;
13
+ const isReactNode = (0, react_1.isValidElement)(customElement);
14
+ (0, react_1.useEffect)(() => {
15
+ if (isDomElement && ref.current) {
16
+ ref.current.innerHTML = ''; // Clear previous content
17
+ ref.current.appendChild(customElement);
18
+ }
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
20
+ }, [item]);
21
+ if (isReactNode) {
22
+ return customElement;
23
+ }
24
+ if (isDomElement) {
25
+ return react_1.default.createElement("div", Object.assign({}, getItemProps(item), { ref: ref }));
26
+ }
27
+ return null;
28
+ }
29
+ exports.default = CustomSectionItem;
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importStar(require("react"));
5
5
  const SectionItem_1 = tslib_1.__importDefault(require("../SectionItem/SectionItem"));
6
+ const CustomSectionItem_1 = tslib_1.__importDefault(require("../SectionItem/CustomSectionItem"));
6
7
  const utils_1 = require("../../../utils");
7
8
  const CioAutocompleteProvider_1 = require("../CioAutocompleteProvider");
8
9
  const NoResults_1 = tslib_1.__importDefault(require("../AutocompleteResults/NoResults"));
9
10
  // eslint-disable-next-line func-names
10
11
  const DefaultRenderSectionItemsList = function ({ section }) {
11
12
  var _a, _b, _c;
12
- const { getSectionProps, query, getFormProps, advancedParameters, getItemProps } = (0, react_1.useContext)(CioAutocompleteProvider_1.CioAutocompleteContext);
13
+ const { getSectionProps, query, getFormProps, advancedParameters } = (0, react_1.useContext)(CioAutocompleteProvider_1.CioAutocompleteContext);
13
14
  const { displayShowAllResultsButton, translations } = advancedParameters || {};
14
15
  const { onSubmit } = getFormProps();
15
16
  const { type, displayName, displayNoResultsMessage } = section;
@@ -42,7 +43,7 @@ const DefaultRenderSectionItemsList = function ({ section }) {
42
43
  react_1.default.createElement("h5", { className: 'cio-section-name cio-sectionName', "aria-hidden": true }, (0, utils_1.camelToStartCase)(sectionTitle)),
43
44
  react_1.default.createElement("ul", { className: 'cio-section-items', role: 'none' }, (_c = section === null || section === void 0 ? void 0 : section.data) === null || _c === void 0 ? void 0 : _c.map((item) => {
44
45
  if (typeof (section === null || section === void 0 ? void 0 : section.renderItem) === 'function') {
45
- return section.renderItem({ item, query, getItemProps });
46
+ return (react_1.default.createElement(CustomSectionItem_1.default, { renderItem: section.renderItem, item: item, query: query, key: item.id }));
46
47
  }
47
48
  return (react_1.default.createElement(SectionItem_1.default, { item: item, key: item === null || item === void 0 ? void 0 : item.id, displaySearchTermHighlights: section.displaySearchTermHighlights }));
48
49
  })),
@@ -245,5 +245,6 @@ exports.translationsDescription = `Pass a \`translations\` object to display tra
245
245
  `;
246
246
  exports.customRenderItemDescription = `Customize the rendering of individual items within a Section by providing a \`renderItem\` function. This function allows you to define how each item should be rendered.
247
247
  - Make sure to call \`getItemProps(item)\` and spread like this \`<div {...getItemProps(item)}/>\` on the container of each custom rendered item or else tracking will not work.
248
+ - Supports both ReactNode and HTMLElement as a return type
248
249
  `;
249
250
  exports.displayShowAllResultsButtonDescription = `Pass a boolean to \`displayShowAllResultsButton\` to display a button at the bottom of the Products section to show all results. This button will submit the form and trigger the \`onSubmit\` callback.`;
@@ -13,6 +13,7 @@ const useSections_1 = tslib_1.__importDefault(require("./useSections"));
13
13
  const useRecommendationsObserver_1 = tslib_1.__importDefault(require("./useRecommendationsObserver"));
14
14
  const typeGuards_1 = require("../typeGuards");
15
15
  const useNormalizedProps_1 = tslib_1.__importDefault(require("./useNormalizedProps"));
16
+ const useCustomBlur_1 = tslib_1.__importDefault(require("./useCustomBlur"));
16
17
  exports.defaultSections = [
17
18
  {
18
19
  indexSectionName: 'Search Suggestions',
@@ -39,6 +40,7 @@ const useCioAutocomplete = (options) => {
39
40
  onSubmit,
40
41
  cioClient,
41
42
  previousQuery }, rest));
43
+ (0, useCustomBlur_1.default)(isOpen, closeMenu, autocompleteClassName);
42
44
  // Log console errors
43
45
  (0, useConsoleErrors_1.default)(sections, activeSections);
44
46
  // Track recommendation view
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_1 = require("react");
4
+ const useCustomBlur = (isOpen, closeMenu, autocompleteClassName) => {
5
+ const handleDocumentClick = (0, react_1.useCallback)((event) => {
6
+ var _a;
7
+ if (isOpen && !((_a = event.target) === null || _a === void 0 ? void 0 : _a.closest(`.${autocompleteClassName}`))) {
8
+ closeMenu();
9
+ }
10
+ }, [closeMenu, isOpen, autocompleteClassName]);
11
+ (0, react_1.useEffect)(() => {
12
+ document.addEventListener('mousedown', handleDocumentClick);
13
+ return () => {
14
+ document.removeEventListener('mousedown', handleDocumentClick);
15
+ };
16
+ }, [handleDocumentClick]);
17
+ };
18
+ exports.default = useCustomBlur;
@@ -49,6 +49,13 @@ const useDownShift = (_a) => {
49
49
  }
50
50
  }
51
51
  }
52
+ }, stateReducer: (state, actionAndChanges) => {
53
+ const { type, changes } = actionAndChanges;
54
+ // Override dropdown close on blur
55
+ if (type === downshift_1.useCombobox.stateChangeTypes.InputBlur) {
56
+ return Object.assign(Object.assign({}, changes), { isOpen: state.isOpen });
57
+ }
58
+ return changes;
52
59
  } }, rest));
53
60
  };
54
61
  exports.default = useDownShift;
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '1.23.25';
3
+ exports.default = '1.23.27';
@@ -0,0 +1,25 @@
1
+ import React, { useContext, useMemo, useEffect, useRef, isValidElement } from 'react';
2
+ import { CioAutocompleteContext } from '../CioAutocompleteProvider';
3
+ export default function CustomSectionItem(props) {
4
+ const { renderItem, item, query } = props;
5
+ const { getItemProps } = useContext(CioAutocompleteContext);
6
+ const ref = useRef(null);
7
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8
+ const customElement = useMemo(() => renderItem({ item, query, getItemProps }), [item]);
9
+ const isDomElement = customElement instanceof HTMLElement;
10
+ const isReactNode = isValidElement(customElement);
11
+ useEffect(() => {
12
+ if (isDomElement && ref.current) {
13
+ ref.current.innerHTML = ''; // Clear previous content
14
+ ref.current.appendChild(customElement);
15
+ }
16
+ // eslint-disable-next-line react-hooks/exhaustive-deps
17
+ }, [item]);
18
+ if (isReactNode) {
19
+ return customElement;
20
+ }
21
+ if (isDomElement) {
22
+ return React.createElement("div", { ...getItemProps(item), ref: ref });
23
+ }
24
+ return null;
25
+ }
@@ -1,11 +1,12 @@
1
1
  import React, { useContext } from 'react';
2
2
  import SectionItem from '../SectionItem/SectionItem';
3
+ import CustomSectionItem from '../SectionItem/CustomSectionItem';
3
4
  import { camelToStartCase, translate } from '../../../utils';
4
5
  import { CioAutocompleteContext } from '../CioAutocompleteProvider';
5
6
  import NoResults from '../AutocompleteResults/NoResults';
6
7
  // eslint-disable-next-line func-names
7
8
  const DefaultRenderSectionItemsList = function ({ section }) {
8
- const { getSectionProps, query, getFormProps, advancedParameters, getItemProps } = useContext(CioAutocompleteContext);
9
+ const { getSectionProps, query, getFormProps, advancedParameters } = useContext(CioAutocompleteContext);
9
10
  const { displayShowAllResultsButton, translations } = advancedParameters || {};
10
11
  const { onSubmit } = getFormProps();
11
12
  const { type, displayName, displayNoResultsMessage } = section;
@@ -38,7 +39,7 @@ const DefaultRenderSectionItemsList = function ({ section }) {
38
39
  React.createElement("h5", { className: 'cio-section-name cio-sectionName', "aria-hidden": true }, camelToStartCase(sectionTitle)),
39
40
  React.createElement("ul", { className: 'cio-section-items', role: 'none' }, section?.data?.map((item) => {
40
41
  if (typeof section?.renderItem === 'function') {
41
- return section.renderItem({ item, query, getItemProps });
42
+ return (React.createElement(CustomSectionItem, { renderItem: section.renderItem, item: item, query: query, key: item.id }));
42
43
  }
43
44
  return (React.createElement(SectionItem, { item: item, key: item?.id, displaySearchTermHighlights: section.displaySearchTermHighlights }));
44
45
  })),
@@ -241,5 +241,6 @@ export const translationsDescription = `Pass a \`translations\` object to displa
241
241
  `;
242
242
  export const customRenderItemDescription = `Customize the rendering of individual items within a Section by providing a \`renderItem\` function. This function allows you to define how each item should be rendered.
243
243
  - Make sure to call \`getItemProps(item)\` and spread like this \`<div {...getItemProps(item)}/>\` on the container of each custom rendered item or else tracking will not work.
244
+ - Supports both ReactNode and HTMLElement as a return type
244
245
  `;
245
246
  export const displayShowAllResultsButtonDescription = `Pass a boolean to \`displayShowAllResultsButton\` to display a button at the bottom of the Products section to show all results. This button will submit the form and trigger the \`onSubmit\` callback.`;
@@ -9,6 +9,7 @@ import useSections from './useSections';
9
9
  import useRecommendationsObserver from './useRecommendationsObserver';
10
10
  import { isCustomSection, isRecommendationsSection } from '../typeGuards';
11
11
  import useNormalizedProps from './useNormalizedProps';
12
+ import useCustomBlur from './useCustomBlur';
12
13
  export const defaultSections = [
13
14
  {
14
15
  indexSectionName: 'Search Suggestions',
@@ -38,6 +39,7 @@ const useCioAutocomplete = (options) => {
38
39
  previousQuery,
39
40
  ...rest,
40
41
  });
42
+ useCustomBlur(isOpen, closeMenu, autocompleteClassName);
41
43
  // Log console errors
42
44
  useConsoleErrors(sections, activeSections);
43
45
  // Track recommendation view
@@ -0,0 +1,15 @@
1
+ import { useCallback, useEffect } from 'react';
2
+ const useCustomBlur = (isOpen, closeMenu, autocompleteClassName) => {
3
+ const handleDocumentClick = useCallback((event) => {
4
+ if (isOpen && !event.target?.closest(`.${autocompleteClassName}`)) {
5
+ closeMenu();
6
+ }
7
+ }, [closeMenu, isOpen, autocompleteClassName]);
8
+ useEffect(() => {
9
+ document.addEventListener('mousedown', handleDocumentClick);
10
+ return () => {
11
+ document.removeEventListener('mousedown', handleDocumentClick);
12
+ };
13
+ }, [handleDocumentClick]);
14
+ };
15
+ export default useCustomBlur;
@@ -47,6 +47,14 @@ const useDownShift = ({ setQuery, items, onSubmit, cioClient, previousQuery = ''
47
47
  }
48
48
  }
49
49
  },
50
+ stateReducer: (state, actionAndChanges) => {
51
+ const { type, changes } = actionAndChanges;
52
+ // Override dropdown close on blur
53
+ if (type === useCombobox.stateChangeTypes.InputBlur) {
54
+ return { ...changes, isOpen: state.isOpen };
55
+ }
56
+ return changes;
57
+ },
50
58
  ...rest,
51
59
  });
52
60
  export default useDownShift;
@@ -1 +1 @@
1
- export default '1.23.25';
1
+ export default '1.23.27';
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ import { Item } from '../../../types';
3
+ interface CustomItemProps {
4
+ item: Item;
5
+ renderItem: (props: {
6
+ item: Item;
7
+ query: string;
8
+ getItemProps: (item: Item) => any;
9
+ }) => HTMLElement | ReactNode;
10
+ query: string;
11
+ }
12
+ export default function CustomSectionItem(props: CustomItemProps): JSX.Element | null;
13
+ export {};
@@ -36,5 +36,5 @@ export declare const termsWithImagesAndCountsDescription = "Pass boolean flags f
36
36
  export declare const debounceDescription = "Pass an integer to `debounce` to override the recommended, default delay employed for debouncing autocomplete network requests between keystrokes as your users type into the text input field. The default value is 250, which results in a debounce delay of 250 milliseconds.";
37
37
  export declare const fetchZeroStateOnFocusDescription = "Pass a boolean to `fetchZeroStateOnFocus` to override the zero state fetching behavior from initial render to input focus.";
38
38
  export declare const translationsDescription = "Pass a `translations` object to display translatable words in your preferred language.\n\n- Current translatable keys:\n```\n {\n \"in\": \"...\",\n \"show all results\": \"...\"\n }\n```\n";
39
- export declare const customRenderItemDescription = "Customize the rendering of individual items within a Section by providing a `renderItem` function. This function allows you to define how each item should be rendered.\n- Make sure to call `getItemProps(item)` and spread like this `<div {...getItemProps(item)}/>` on the container of each custom rendered item or else tracking will not work.\n";
39
+ export declare const customRenderItemDescription = "Customize the rendering of individual items within a Section by providing a `renderItem` function. This function allows you to define how each item should be rendered.\n- Make sure to call `getItemProps(item)` and spread like this `<div {...getItemProps(item)}/>` on the container of each custom rendered item or else tracking will not work.\n- Supports both ReactNode and HTMLElement as a return type\n";
40
40
  export declare const displayShowAllResultsButtonDescription = "Pass a boolean to `displayShowAllResultsButton` to display a button at the bottom of the Products section to show all results. This button will submit the form and trigger the `onSubmit` callback.";
@@ -0,0 +1,2 @@
1
+ declare const useCustomBlur: (isOpen: boolean, closeMenu: () => void, autocompleteClassName: string) => void;
2
+ export default useCustomBlur;
@@ -1,2 +1,2 @@
1
- declare const _default: "1.23.25";
1
+ declare const _default: "1.23.27";
2
2
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructor-io/constructorio-ui-autocomplete",
3
- "version": "1.23.25",
3
+ "version": "1.23.27",
4
4
  "description": "Constructor.io Autocomplete UI library for web applications",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",