@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/components/Facets.d.ts +2 -0
- package/dist/components/Facets.d.ts.map +1 -1
- package/dist/components/Facets.js +16 -3
- package/dist/hooks/useFilters.d.ts.map +1 -1
- package/dist/hooks/useFilters.js +15 -4
- package/dist/index.umd.js +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.esm.js +34 -16
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +34 -16
- package/dist/src/index.js.map +1 -1
- package/dist/utils/responsive.d.ts.map +1 -1
- package/dist/utils/responsive.js +4 -10
- package/package.json +3 -3
package/dist/src/index.d.ts
CHANGED
|
@@ -485,6 +485,8 @@ interface FacetsProps {
|
|
|
485
485
|
hideEmptyFacets?: boolean;
|
|
486
486
|
/** Fields that should start collapsed (collapsible variant only). Overrides defaultCollapsed for listed fields. */
|
|
487
487
|
defaultCollapsedFields?: string[];
|
|
488
|
+
/** Callback fired when facet availability changes. Receives true when facets are available, false when empty. */
|
|
489
|
+
onFacetsAvailable?: (available: boolean) => void;
|
|
488
490
|
}
|
|
489
491
|
declare const Facets: React__default.FC<FacetsProps>;
|
|
490
492
|
|
package/dist/src/index.esm.js
CHANGED
|
@@ -2709,11 +2709,8 @@ function getCurrentBreakpoint(width) {
|
|
|
2709
2709
|
* Hook to get current breakpoint
|
|
2710
2710
|
*/
|
|
2711
2711
|
function useBreakpoint() {
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
return 'lg';
|
|
2715
|
-
return getCurrentBreakpoint(window.innerWidth);
|
|
2716
|
-
});
|
|
2712
|
+
// Always start with 'lg' to avoid hydration mismatch between server and client
|
|
2713
|
+
const [breakpoint, setBreakpoint] = useState('lg');
|
|
2717
2714
|
useEffect(() => {
|
|
2718
2715
|
const handleResize = () => {
|
|
2719
2716
|
setBreakpoint(getCurrentBreakpoint(window.innerWidth));
|
|
@@ -2728,11 +2725,8 @@ function useBreakpoint() {
|
|
|
2728
2725
|
* Hook to check if current viewport matches breakpoint
|
|
2729
2726
|
*/
|
|
2730
2727
|
function useMediaQuery(query) {
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
return false;
|
|
2734
|
-
return window.matchMedia(query).matches;
|
|
2735
|
-
});
|
|
2728
|
+
// Always start with false to avoid hydration mismatch between server and client
|
|
2729
|
+
const [matches, setMatches] = useState(false);
|
|
2736
2730
|
useEffect(() => {
|
|
2737
2731
|
const mediaQuery = window.matchMedia(query);
|
|
2738
2732
|
const handleChange = () => setMatches(mediaQuery.matches);
|
|
@@ -3528,15 +3522,22 @@ const useFilters = (options) => {
|
|
|
3528
3522
|
const [error, setError] = useState(null);
|
|
3529
3523
|
const mountedRef = useRef(true);
|
|
3530
3524
|
const autoFetch = options?.autoFetch !== false;
|
|
3525
|
+
// Track whether we've completed the first fetch (to avoid skeleton flash on refetches)
|
|
3526
|
+
const hasDataRef = useRef(false);
|
|
3531
3527
|
// Extract non-autoFetch options to pass to fetchFilters
|
|
3532
3528
|
const fetchFilters = useCallback(async () => {
|
|
3533
|
-
|
|
3529
|
+
// Only show loading spinner on the very first fetch; subsequent refetches
|
|
3530
|
+
// keep previous data visible to avoid skeleton/flash on facet changes.
|
|
3531
|
+
if (!hasDataRef.current) {
|
|
3532
|
+
setLoading(true);
|
|
3533
|
+
}
|
|
3534
3534
|
setError(null);
|
|
3535
3535
|
try {
|
|
3536
3536
|
const { autoFetch: _, ...filterOptions } = options || {};
|
|
3537
3537
|
const response = await stateManager.fetchFilters(filterOptions);
|
|
3538
3538
|
if (mountedRef.current) {
|
|
3539
3539
|
setFilters(response?.filters || []);
|
|
3540
|
+
hasDataRef.current = true;
|
|
3540
3541
|
setLoading(false);
|
|
3541
3542
|
}
|
|
3542
3543
|
}
|
|
@@ -3547,14 +3548,18 @@ const useFilters = (options) => {
|
|
|
3547
3548
|
}
|
|
3548
3549
|
}
|
|
3549
3550
|
}, [stateManager, options?.facetBy, options?.maxFacetValues, options?.disjunctiveFacets?.join(',')]);
|
|
3550
|
-
// Track query
|
|
3551
|
-
|
|
3551
|
+
// Track query to only refetch filters when query actually changes.
|
|
3552
|
+
// Sentinel ensures the first subscribe callback always triggers a fetch.
|
|
3553
|
+
const prevKeyRef = useRef(null);
|
|
3552
3554
|
// Refetch when query or refinements change (not on every state update)
|
|
3553
3555
|
useEffect(() => {
|
|
3554
3556
|
if (!autoFetch)
|
|
3555
3557
|
return;
|
|
3556
3558
|
const unsubscribe = stateManager.subscribe((state) => {
|
|
3557
|
-
|
|
3559
|
+
// Only track query changes — refinements are NOT passed to the Filters API
|
|
3560
|
+
// (facets are generated from search query only, not narrowed by active filters).
|
|
3561
|
+
// This prevents redundant filters refetches on every facet toggle.
|
|
3562
|
+
const key = state.query;
|
|
3558
3563
|
if (key === prevKeyRef.current)
|
|
3559
3564
|
return;
|
|
3560
3565
|
prevKeyRef.current = key;
|
|
@@ -3904,7 +3909,7 @@ const CSS_VAR_DEFAULTS = {
|
|
|
3904
3909
|
// ---------------------------------------------------------------------------
|
|
3905
3910
|
// Component
|
|
3906
3911
|
// ---------------------------------------------------------------------------
|
|
3907
|
-
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, }) => {
|
|
3912
|
+
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, }) => {
|
|
3908
3913
|
const { theme } = useSearchContext();
|
|
3909
3914
|
const { results: stateResults, refinements, addRefinement, removeRefinement } = useSearchState();
|
|
3910
3915
|
const facetsTheme = customTheme || {};
|
|
@@ -3982,9 +3987,22 @@ const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, rende
|
|
|
3982
3987
|
return extracted;
|
|
3983
3988
|
};
|
|
3984
3989
|
const rawFacetList = extractFacets();
|
|
3990
|
+
const hasStats = (stats) => stats != null && (stats.min != null || stats.max != null);
|
|
3985
3991
|
const facets = hideEmptyFacets
|
|
3986
|
-
? rawFacetList.filter(f => f.items.length > 0 || f.stats
|
|
3992
|
+
? rawFacetList.filter(f => f.items.length > 0 || hasStats(f.stats))
|
|
3987
3993
|
: rawFacetList;
|
|
3994
|
+
// Notify parent about facet availability
|
|
3995
|
+
const facetCount = facets.length;
|
|
3996
|
+
const prevFacetAvailableRef = useRef(null);
|
|
3997
|
+
useEffect(() => {
|
|
3998
|
+
if (!onFacetsAvailable)
|
|
3999
|
+
return;
|
|
4000
|
+
const available = facetCount > 0;
|
|
4001
|
+
if (prevFacetAvailableRef.current !== available) {
|
|
4002
|
+
prevFacetAvailableRef.current = available;
|
|
4003
|
+
onFacetsAvailable(available);
|
|
4004
|
+
}
|
|
4005
|
+
}, [facetCount, onFacetsAvailable]);
|
|
3988
4006
|
// -------------------------------------------------------------------
|
|
3989
4007
|
// Handlers
|
|
3990
4008
|
// -------------------------------------------------------------------
|