@pro6pp/infer-react 0.0.2-beta.15 → 0.0.2-beta.16

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/README.md CHANGED
@@ -45,7 +45,7 @@ You can customize the appearance of the component via the following props:
45
45
  | `debounceMs` | Delay in ms before API search. Defaults to `150` (min `50`). |
46
46
  | `maxRetries` | Maximum retry attempts for transient network errors. Valid range: `0` to `10`. |
47
47
  | `showClearButton` | If `true`, displays a button to empty the input field. Defaults to `true`. |
48
- | `loadMoreText` | The text to display on the pagination button. |
48
+ | `loadingText` | The text displayed at the bottom of the list when fetching more results. |
49
49
 
50
50
  ---
51
51
 
package/dist/index.cjs CHANGED
@@ -524,17 +524,6 @@ var DEFAULT_STYLES = `
524
524
  background-color: #f3f4f6;
525
525
  }
526
526
 
527
- .pro6pp-loader {
528
- width: 20px;
529
- height: 20px;
530
- margin: 0 8px;
531
- border: 2px solid #e0e0e0;
532
- border-top-color: #6b7280;
533
- border-radius: 50%;
534
- animation: pro6pp-spin 0.6s linear infinite;
535
- flex-shrink: 0;
536
- }
537
-
538
527
  .pro6pp-dropdown {
539
528
  position: absolute;
540
529
  top: 100%;
@@ -636,6 +625,27 @@ var DEFAULT_STYLES = `
636
625
  touch-action: manipulation;
637
626
  }
638
627
 
628
+ .pro6pp-loader-item {
629
+ padding: 10px 12px;
630
+ color: #6b7280;
631
+ font-size: 0.875rem;
632
+ display: flex;
633
+ align-items: center;
634
+ justify-content: center;
635
+ gap: 8px;
636
+ background-color: #f9fafb;
637
+ border-top: 1px solid #f3f4f6;
638
+ }
639
+
640
+ .pro6pp-mini-spinner {
641
+ width: 14px;
642
+ height: 14px;
643
+ border: 2px solid #e5e7eb;
644
+ border-top-color: #6b7280;
645
+ border-radius: 50%;
646
+ animation: pro6pp-spin 0.6s linear infinite;
647
+ }
648
+
639
649
  @media (max-width: 640px) {
640
650
  .pro6pp-input {
641
651
  font-size: 16px;
@@ -753,7 +763,7 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
753
763
  renderItem,
754
764
  disableDefaultStyles = false,
755
765
  noResultsText = "No results found",
756
- loadMoreText = "Show more results...",
766
+ loadingText = "Loading more...",
757
767
  renderNoResults,
758
768
  showClearButton = true,
759
769
  ...config
@@ -762,6 +772,7 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
762
772
  const [isOpen, setIsOpen] = (0, import_react.useState)(false);
763
773
  const internalInputRef = (0, import_react.useRef)(null);
764
774
  const wrapperRef = (0, import_react.useRef)(null);
775
+ const observerTarget = (0, import_react.useRef)(null);
765
776
  (0, import_react.useImperativeHandle)(ref, () => internalInputRef.current);
766
777
  (0, import_react.useEffect)(() => {
767
778
  if (disableDefaultStyles) return;
@@ -782,6 +793,22 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
782
793
  document.addEventListener("mousedown", handleClickOutside);
783
794
  return () => document.removeEventListener("mousedown", handleClickOutside);
784
795
  }, []);
796
+ (0, import_react.useEffect)(() => {
797
+ const currentTarget = observerTarget.current;
798
+ if (!currentTarget) return;
799
+ const observer = new IntersectionObserver(
800
+ (entries) => {
801
+ if (entries[0].isIntersecting && state.hasMore && !state.isLoading) {
802
+ loadMore();
803
+ }
804
+ },
805
+ { threshold: 0.1 }
806
+ );
807
+ observer.observe(currentTarget);
808
+ return () => {
809
+ if (currentTarget) observer.unobserve(currentTarget);
810
+ };
811
+ }, [state.hasMore, state.isLoading, loadMore, isOpen]);
785
812
  const items = (0, import_react.useMemo)(() => {
786
813
  return [
787
814
  ...state.cities.map((c) => ({ ...c, type: "city" })),
@@ -804,6 +831,7 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
804
831
  const hasResults = items.length > 0;
805
832
  const showNoResults = !state.isLoading && !state.isError && state.query.length > 0 && !hasResults && !state.isValid;
806
833
  const showDropdown = isOpen && (hasResults || state.isLoading || showNoResults);
834
+ const isInfiniteLoading = state.isLoading && items.length > 0;
807
835
  return /* @__PURE__ */ import_react.default.createElement("div", { ref: wrapperRef, className: `pro6pp-wrapper ${className || ""}`, style }, /* @__PURE__ */ import_react.default.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ import_react.default.createElement(
808
836
  "input",
809
837
  {
@@ -824,7 +852,7 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
824
852
  inputProps?.onFocus?.(e);
825
853
  }
826
854
  }
827
- ), /* @__PURE__ */ import_react.default.createElement("div", { className: "pro6pp-input-addons" }, state.isLoading && /* @__PURE__ */ import_react.default.createElement("div", { className: "pro6pp-loader" }), showClearButton && state.query.length > 0 && /* @__PURE__ */ import_react.default.createElement(
855
+ ), /* @__PURE__ */ import_react.default.createElement("div", { className: "pro6pp-input-addons" }, showClearButton && state.query.length > 0 && /* @__PURE__ */ import_react.default.createElement(
828
856
  "button",
829
857
  {
830
858
  type: "button",
@@ -854,7 +882,7 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
854
882
  onWheel: (e) => e.stopPropagation(),
855
883
  onMouseDown: (e) => e.stopPropagation()
856
884
  },
857
- /* @__PURE__ */ import_react.default.createElement("ul", { className: "pro6pp-list", role: "listbox" }, hasResults ? items.map((item, index) => {
885
+ /* @__PURE__ */ import_react.default.createElement("ul", { className: "pro6pp-list", role: "listbox" }, hasResults ? /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, items.map((item, index) => {
858
886
  const isActive = index === state.selectedSuggestionIndex;
859
887
  const secondaryText = item.subtitle || (item.count !== void 0 ? item.count : "");
860
888
  const showChevron = item.value === void 0 || item.value === null;
@@ -883,19 +911,7 @@ var Pro6PPInfer = (0, import_react.forwardRef)(
883
911
  /* @__PURE__ */ import_react.default.createElement("polyline", { points: "9 18 15 12 9 6" })
884
912
  )))
885
913
  );
886
- }) : state.isLoading ? /* @__PURE__ */ import_react.default.createElement("li", { className: "pro6pp-no-results" }, "Loading suggestions...") : /* @__PURE__ */ import_react.default.createElement("li", { className: "pro6pp-no-results" }, renderNoResults ? renderNoResults(state) : noResultsText)),
887
- state.hasMore && /* @__PURE__ */ import_react.default.createElement(
888
- "button",
889
- {
890
- type: "button",
891
- className: "pro6pp-load-more",
892
- onClick: (e) => {
893
- e.preventDefault();
894
- loadMore();
895
- }
896
- },
897
- loadMoreText
898
- )
914
+ }), state.hasMore && !state.isLoading && /* @__PURE__ */ import_react.default.createElement("li", { key: "sentinel", ref: observerTarget, style: { height: "1px", opacity: 0 } }), isInfiniteLoading && /* @__PURE__ */ import_react.default.createElement("li", { key: "loader", className: "pro6pp-loader-item" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "pro6pp-mini-spinner" }), /* @__PURE__ */ import_react.default.createElement("span", null, loadingText))) : state.isLoading ? /* @__PURE__ */ import_react.default.createElement("li", { className: "pro6pp-no-results" }, "Searching...") : /* @__PURE__ */ import_react.default.createElement("li", { className: "pro6pp-no-results" }, renderNoResults ? renderNoResults(state) : noResultsText))
899
915
  ));
900
916
  }
901
917
  );
package/dist/index.d.cts CHANGED
@@ -58,10 +58,10 @@ interface Pro6PPInferProps extends UseInferConfig {
58
58
  * @default 'No results found'
59
59
  */
60
60
  noResultsText?: string;
61
- /** * The text to show on the load more button.
62
- * @default 'Show more results...'
61
+ /** * The text to show on the bottom loading indicator.
62
+ * @default 'Loading more...'
63
63
  */
64
- loadMoreText?: string;
64
+ loadingText?: string;
65
65
  /** A custom render function for the "no results" state. */
66
66
  renderNoResults?: (state: InferState) => React.ReactNode;
67
67
  /**
package/dist/index.d.ts CHANGED
@@ -58,10 +58,10 @@ interface Pro6PPInferProps extends UseInferConfig {
58
58
  * @default 'No results found'
59
59
  */
60
60
  noResultsText?: string;
61
- /** * The text to show on the load more button.
62
- * @default 'Show more results...'
61
+ /** * The text to show on the bottom loading indicator.
62
+ * @default 'Loading more...'
63
63
  */
64
- loadMoreText?: string;
64
+ loadingText?: string;
65
65
  /** A custom render function for the "no results" state. */
66
66
  renderNoResults?: (state: InferState) => React.ReactNode;
67
67
  /**
package/dist/index.js CHANGED
@@ -498,17 +498,6 @@ var DEFAULT_STYLES = `
498
498
  background-color: #f3f4f6;
499
499
  }
500
500
 
501
- .pro6pp-loader {
502
- width: 20px;
503
- height: 20px;
504
- margin: 0 8px;
505
- border: 2px solid #e0e0e0;
506
- border-top-color: #6b7280;
507
- border-radius: 50%;
508
- animation: pro6pp-spin 0.6s linear infinite;
509
- flex-shrink: 0;
510
- }
511
-
512
501
  .pro6pp-dropdown {
513
502
  position: absolute;
514
503
  top: 100%;
@@ -610,6 +599,27 @@ var DEFAULT_STYLES = `
610
599
  touch-action: manipulation;
611
600
  }
612
601
 
602
+ .pro6pp-loader-item {
603
+ padding: 10px 12px;
604
+ color: #6b7280;
605
+ font-size: 0.875rem;
606
+ display: flex;
607
+ align-items: center;
608
+ justify-content: center;
609
+ gap: 8px;
610
+ background-color: #f9fafb;
611
+ border-top: 1px solid #f3f4f6;
612
+ }
613
+
614
+ .pro6pp-mini-spinner {
615
+ width: 14px;
616
+ height: 14px;
617
+ border: 2px solid #e5e7eb;
618
+ border-top-color: #6b7280;
619
+ border-radius: 50%;
620
+ animation: pro6pp-spin 0.6s linear infinite;
621
+ }
622
+
613
623
  @media (max-width: 640px) {
614
624
  .pro6pp-input {
615
625
  font-size: 16px;
@@ -727,7 +737,7 @@ var Pro6PPInfer = forwardRef(
727
737
  renderItem,
728
738
  disableDefaultStyles = false,
729
739
  noResultsText = "No results found",
730
- loadMoreText = "Show more results...",
740
+ loadingText = "Loading more...",
731
741
  renderNoResults,
732
742
  showClearButton = true,
733
743
  ...config
@@ -736,6 +746,7 @@ var Pro6PPInfer = forwardRef(
736
746
  const [isOpen, setIsOpen] = useState(false);
737
747
  const internalInputRef = useRef(null);
738
748
  const wrapperRef = useRef(null);
749
+ const observerTarget = useRef(null);
739
750
  useImperativeHandle(ref, () => internalInputRef.current);
740
751
  useEffect(() => {
741
752
  if (disableDefaultStyles) return;
@@ -756,6 +767,22 @@ var Pro6PPInfer = forwardRef(
756
767
  document.addEventListener("mousedown", handleClickOutside);
757
768
  return () => document.removeEventListener("mousedown", handleClickOutside);
758
769
  }, []);
770
+ useEffect(() => {
771
+ const currentTarget = observerTarget.current;
772
+ if (!currentTarget) return;
773
+ const observer = new IntersectionObserver(
774
+ (entries) => {
775
+ if (entries[0].isIntersecting && state.hasMore && !state.isLoading) {
776
+ loadMore();
777
+ }
778
+ },
779
+ { threshold: 0.1 }
780
+ );
781
+ observer.observe(currentTarget);
782
+ return () => {
783
+ if (currentTarget) observer.unobserve(currentTarget);
784
+ };
785
+ }, [state.hasMore, state.isLoading, loadMore, isOpen]);
759
786
  const items = useMemo(() => {
760
787
  return [
761
788
  ...state.cities.map((c) => ({ ...c, type: "city" })),
@@ -778,6 +805,7 @@ var Pro6PPInfer = forwardRef(
778
805
  const hasResults = items.length > 0;
779
806
  const showNoResults = !state.isLoading && !state.isError && state.query.length > 0 && !hasResults && !state.isValid;
780
807
  const showDropdown = isOpen && (hasResults || state.isLoading || showNoResults);
808
+ const isInfiniteLoading = state.isLoading && items.length > 0;
781
809
  return /* @__PURE__ */ React.createElement("div", { ref: wrapperRef, className: `pro6pp-wrapper ${className || ""}`, style }, /* @__PURE__ */ React.createElement("div", { style: { position: "relative" } }, /* @__PURE__ */ React.createElement(
782
810
  "input",
783
811
  {
@@ -798,7 +826,7 @@ var Pro6PPInfer = forwardRef(
798
826
  inputProps?.onFocus?.(e);
799
827
  }
800
828
  }
801
- ), /* @__PURE__ */ React.createElement("div", { className: "pro6pp-input-addons" }, state.isLoading && /* @__PURE__ */ React.createElement("div", { className: "pro6pp-loader" }), showClearButton && state.query.length > 0 && /* @__PURE__ */ React.createElement(
829
+ ), /* @__PURE__ */ React.createElement("div", { className: "pro6pp-input-addons" }, showClearButton && state.query.length > 0 && /* @__PURE__ */ React.createElement(
802
830
  "button",
803
831
  {
804
832
  type: "button",
@@ -828,7 +856,7 @@ var Pro6PPInfer = forwardRef(
828
856
  onWheel: (e) => e.stopPropagation(),
829
857
  onMouseDown: (e) => e.stopPropagation()
830
858
  },
831
- /* @__PURE__ */ React.createElement("ul", { className: "pro6pp-list", role: "listbox" }, hasResults ? items.map((item, index) => {
859
+ /* @__PURE__ */ React.createElement("ul", { className: "pro6pp-list", role: "listbox" }, hasResults ? /* @__PURE__ */ React.createElement(React.Fragment, null, items.map((item, index) => {
832
860
  const isActive = index === state.selectedSuggestionIndex;
833
861
  const secondaryText = item.subtitle || (item.count !== void 0 ? item.count : "");
834
862
  const showChevron = item.value === void 0 || item.value === null;
@@ -857,19 +885,7 @@ var Pro6PPInfer = forwardRef(
857
885
  /* @__PURE__ */ React.createElement("polyline", { points: "9 18 15 12 9 6" })
858
886
  )))
859
887
  );
860
- }) : state.isLoading ? /* @__PURE__ */ React.createElement("li", { className: "pro6pp-no-results" }, "Loading suggestions...") : /* @__PURE__ */ React.createElement("li", { className: "pro6pp-no-results" }, renderNoResults ? renderNoResults(state) : noResultsText)),
861
- state.hasMore && /* @__PURE__ */ React.createElement(
862
- "button",
863
- {
864
- type: "button",
865
- className: "pro6pp-load-more",
866
- onClick: (e) => {
867
- e.preventDefault();
868
- loadMore();
869
- }
870
- },
871
- loadMoreText
872
- )
888
+ }), state.hasMore && !state.isLoading && /* @__PURE__ */ React.createElement("li", { key: "sentinel", ref: observerTarget, style: { height: "1px", opacity: 0 } }), isInfiniteLoading && /* @__PURE__ */ React.createElement("li", { key: "loader", className: "pro6pp-loader-item" }, /* @__PURE__ */ React.createElement("div", { className: "pro6pp-mini-spinner" }), /* @__PURE__ */ React.createElement("span", null, loadingText))) : state.isLoading ? /* @__PURE__ */ React.createElement("li", { className: "pro6pp-no-results" }, "Searching...") : /* @__PURE__ */ React.createElement("li", { className: "pro6pp-no-results" }, renderNoResults ? renderNoResults(state) : noResultsText))
873
889
  ));
874
890
  }
875
891
  );
package/package.json CHANGED
@@ -20,7 +20,7 @@
20
20
  "url": "https://github.com/pro6pp/infer-sdk/issues"
21
21
  },
22
22
  "sideEffects": false,
23
- "version": "0.0.2-beta.15",
23
+ "version": "0.0.2-beta.16",
24
24
  "main": "./dist/index.cjs",
25
25
  "module": "./dist/index.js",
26
26
  "types": "./dist/index.d.ts",
@@ -46,7 +46,7 @@
46
46
  "react": ">=16"
47
47
  },
48
48
  "dependencies": {
49
- "@pro6pp/infer-core": "0.0.2-beta.13"
49
+ "@pro6pp/infer-core": "0.0.2-beta.14"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@testing-library/dom": "^10.4.1",