@seekora-ai/ui-sdk-react 0.2.25 → 0.2.26

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/dist/src/index.js CHANGED
@@ -2711,11 +2711,8 @@ function getCurrentBreakpoint(width) {
2711
2711
  * Hook to get current breakpoint
2712
2712
  */
2713
2713
  function useBreakpoint() {
2714
- const [breakpoint, setBreakpoint] = React.useState(() => {
2715
- if (typeof window === 'undefined')
2716
- return 'lg';
2717
- return getCurrentBreakpoint(window.innerWidth);
2718
- });
2714
+ // Always start with 'lg' to avoid hydration mismatch between server and client
2715
+ const [breakpoint, setBreakpoint] = React.useState('lg');
2719
2716
  React.useEffect(() => {
2720
2717
  const handleResize = () => {
2721
2718
  setBreakpoint(getCurrentBreakpoint(window.innerWidth));
@@ -2730,11 +2727,8 @@ function useBreakpoint() {
2730
2727
  * Hook to check if current viewport matches breakpoint
2731
2728
  */
2732
2729
  function useMediaQuery(query) {
2733
- const [matches, setMatches] = React.useState(() => {
2734
- if (typeof window === 'undefined')
2735
- return false;
2736
- return window.matchMedia(query).matches;
2737
- });
2730
+ // Always start with false to avoid hydration mismatch between server and client
2731
+ const [matches, setMatches] = React.useState(false);
2738
2732
  React.useEffect(() => {
2739
2733
  const mediaQuery = window.matchMedia(query);
2740
2734
  const handleChange = () => setMatches(mediaQuery.matches);
@@ -3530,15 +3524,22 @@ const useFilters = (options) => {
3530
3524
  const [error, setError] = React.useState(null);
3531
3525
  const mountedRef = React.useRef(true);
3532
3526
  const autoFetch = options?.autoFetch !== false;
3527
+ // Track whether we've completed the first fetch (to avoid skeleton flash on refetches)
3528
+ const hasDataRef = React.useRef(false);
3533
3529
  // Extract non-autoFetch options to pass to fetchFilters
3534
3530
  const fetchFilters = React.useCallback(async () => {
3535
- setLoading(true);
3531
+ // Only show loading spinner on the very first fetch; subsequent refetches
3532
+ // keep previous data visible to avoid skeleton/flash on facet changes.
3533
+ if (!hasDataRef.current) {
3534
+ setLoading(true);
3535
+ }
3536
3536
  setError(null);
3537
3537
  try {
3538
3538
  const { autoFetch: _, ...filterOptions } = options || {};
3539
3539
  const response = await stateManager.fetchFilters(filterOptions);
3540
3540
  if (mountedRef.current) {
3541
3541
  setFilters(response?.filters || []);
3542
+ hasDataRef.current = true;
3542
3543
  setLoading(false);
3543
3544
  }
3544
3545
  }
@@ -3549,14 +3550,18 @@ const useFilters = (options) => {
3549
3550
  }
3550
3551
  }
3551
3552
  }, [stateManager, options?.facetBy, options?.maxFacetValues, options?.disjunctiveFacets?.join(',')]);
3552
- // Track query + refinements to only refetch when they actually change
3553
- const prevKeyRef = React.useRef('');
3553
+ // Track query to only refetch filters when query actually changes.
3554
+ // Sentinel ensures the first subscribe callback always triggers a fetch.
3555
+ const prevKeyRef = React.useRef(null);
3554
3556
  // Refetch when query or refinements change (not on every state update)
3555
3557
  React.useEffect(() => {
3556
3558
  if (!autoFetch)
3557
3559
  return;
3558
3560
  const unsubscribe = stateManager.subscribe((state) => {
3559
- const key = `${state.query}|${state.refinements.map(r => `${r.field}:${r.value}`).sort().join(',')}`;
3561
+ // Only track query changes — refinements are NOT passed to the Filters API
3562
+ // (facets are generated from search query only, not narrowed by active filters).
3563
+ // This prevents redundant filters refetches on every facet toggle.
3564
+ const key = state.query;
3560
3565
  if (key === prevKeyRef.current)
3561
3566
  return;
3562
3567
  prevKeyRef.current = key;
@@ -3906,7 +3911,7 @@ const CSS_VAR_DEFAULTS = {
3906
3911
  // ---------------------------------------------------------------------------
3907
3912
  // Component
3908
3913
  // ---------------------------------------------------------------------------
3909
- const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, renderFacet, renderFacetItem, maxItems = 10, showMore = true, className, style, theme: customTheme, variant = 'checkbox', searchable = false, showCounts = true, colorMap, defaultCollapsed = false, size = 'medium', facetRanges, useFiltersApi = false, disjunctiveFacets, hideEmptyFacets = true, defaultCollapsedFields, }) => {
3914
+ const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, renderFacet, renderFacetItem, maxItems = 10, showMore = true, className, style, theme: customTheme, variant = 'checkbox', searchable = false, showCounts = true, colorMap, defaultCollapsed = false, size = 'medium', facetRanges, useFiltersApi = false, disjunctiveFacets, hideEmptyFacets = true, defaultCollapsedFields, onFacetsAvailable, }) => {
3910
3915
  const { theme } = useSearchContext();
3911
3916
  const { results: stateResults, refinements, addRefinement, removeRefinement } = useSearchState();
3912
3917
  const facetsTheme = customTheme || {};
@@ -3984,9 +3989,22 @@ const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, rende
3984
3989
  return extracted;
3985
3990
  };
3986
3991
  const rawFacetList = extractFacets();
3992
+ const hasStats = (stats) => stats != null && (stats.min != null || stats.max != null);
3987
3993
  const facets = hideEmptyFacets
3988
- ? rawFacetList.filter(f => f.items.length > 0 || f.stats != null)
3994
+ ? rawFacetList.filter(f => f.items.length > 0 || hasStats(f.stats))
3989
3995
  : rawFacetList;
3996
+ // Notify parent about facet availability
3997
+ const facetCount = facets.length;
3998
+ const prevFacetAvailableRef = React.useRef(null);
3999
+ React.useEffect(() => {
4000
+ if (!onFacetsAvailable)
4001
+ return;
4002
+ const available = facetCount > 0;
4003
+ if (prevFacetAvailableRef.current !== available) {
4004
+ prevFacetAvailableRef.current = available;
4005
+ onFacetsAvailable(available);
4006
+ }
4007
+ }, [facetCount, onFacetsAvailable]);
3990
4008
  // -------------------------------------------------------------------
3991
4009
  // Handlers
3992
4010
  // -------------------------------------------------------------------