@seekora-ai/ui-sdk-react 0.2.24 → 0.2.25
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/ClearRefinements.d.ts.map +1 -1
- package/dist/components/ClearRefinements.js +7 -6
- package/dist/components/CurrentRefinements.d.ts.map +1 -1
- package/dist/components/CurrentRefinements.js +32 -30
- package/dist/components/SearchLayout.js +1 -1
- package/dist/hooks/useFilters.d.ts.map +1 -1
- package/dist/hooks/useFilters.js +11 -2
- package/dist/index.umd.js +1 -1
- package/dist/src/index.esm.js +68 -40
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +68 -40
- package/dist/src/index.js.map +1 -1
- package/package.json +3 -3
package/dist/src/index.esm.js
CHANGED
|
@@ -302,6 +302,8 @@ class SearchStateManager {
|
|
|
302
302
|
this.listeners = [];
|
|
303
303
|
this.debounceTimer = null;
|
|
304
304
|
this.notifyScheduled = false;
|
|
305
|
+
this.searchCoalesceTimer = null;
|
|
306
|
+
this.searchCoalesceResolvers = [];
|
|
305
307
|
this.client = config.client;
|
|
306
308
|
this.autoSearch = config.autoSearch !== false;
|
|
307
309
|
this.debounceMs = config.debounceMs || 300;
|
|
@@ -446,13 +448,27 @@ class SearchStateManager {
|
|
|
446
448
|
this.debouncedSearch();
|
|
447
449
|
}
|
|
448
450
|
}
|
|
449
|
-
// Manual search trigger
|
|
451
|
+
// Manual search trigger — coalesces rapid calls within 10ms into a single API request
|
|
450
452
|
async search(additionalOptions) {
|
|
451
453
|
// Clear debounce timer if exists
|
|
452
454
|
if (this.debounceTimer) {
|
|
453
455
|
clearTimeout(this.debounceTimer);
|
|
454
456
|
this.debounceTimer = null;
|
|
455
457
|
}
|
|
458
|
+
return new Promise((resolve, reject) => {
|
|
459
|
+
this.searchCoalesceResolvers.push({ resolve, reject });
|
|
460
|
+
if (this.searchCoalesceTimer) {
|
|
461
|
+
clearTimeout(this.searchCoalesceTimer);
|
|
462
|
+
}
|
|
463
|
+
this.searchCoalesceTimer = setTimeout(() => {
|
|
464
|
+
this.searchCoalesceTimer = null;
|
|
465
|
+
const resolvers = [...this.searchCoalesceResolvers];
|
|
466
|
+
this.searchCoalesceResolvers = [];
|
|
467
|
+
this._executeSearch(additionalOptions).then((result) => resolvers.forEach(r => r.resolve(result)), (err) => resolvers.forEach(r => r.reject(err)));
|
|
468
|
+
}, 10);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
async _executeSearch(additionalOptions) {
|
|
456
472
|
this.setState({ loading: true, error: null });
|
|
457
473
|
try {
|
|
458
474
|
const searchOptions = this.buildSearchOptions(additionalOptions);
|
|
@@ -3531,13 +3547,22 @@ const useFilters = (options) => {
|
|
|
3531
3547
|
}
|
|
3532
3548
|
}
|
|
3533
3549
|
}, [stateManager, options?.facetBy, options?.maxFacetValues, options?.disjunctiveFacets?.join(',')]);
|
|
3534
|
-
//
|
|
3550
|
+
// Track query + refinements to only refetch when they actually change
|
|
3551
|
+
const prevKeyRef = useRef('');
|
|
3552
|
+
// Refetch when query or refinements change (not on every state update)
|
|
3535
3553
|
useEffect(() => {
|
|
3536
3554
|
if (!autoFetch)
|
|
3537
3555
|
return;
|
|
3538
|
-
const unsubscribe = stateManager.subscribe((
|
|
3556
|
+
const unsubscribe = stateManager.subscribe((state) => {
|
|
3557
|
+
const key = `${state.query}|${state.refinements.map(r => `${r.field}:${r.value}`).sort().join(',')}`;
|
|
3558
|
+
if (key === prevKeyRef.current)
|
|
3559
|
+
return;
|
|
3560
|
+
prevKeyRef.current = key;
|
|
3539
3561
|
fetchFilters();
|
|
3540
3562
|
});
|
|
3563
|
+
// subscribe() immediately invokes the listener with current state,
|
|
3564
|
+
// which handles the initial fetch (prevKeyRef starts as '' so key
|
|
3565
|
+
// will differ). No explicit fetchFilters() call needed here.
|
|
3541
3566
|
return unsubscribe;
|
|
3542
3567
|
}, [stateManager, autoFetch, fetchFilters]);
|
|
3543
3568
|
// Fetch schema once on mount
|
|
@@ -5199,9 +5224,6 @@ const CurrentRefinements = ({ refinements: refinementsProp, onRefinementClear, o
|
|
|
5199
5224
|
opacity: 0.6,
|
|
5200
5225
|
}, "aria-label": `Clear ${refinement.label || refinement.field}: ${refinement.value}`, onMouseEnter: e => (e.currentTarget.style.opacity = '1'), onMouseLeave: e => (e.currentTarget.style.opacity = '0.6') }, renderCloseIcon ? renderCloseIcon() : defaultCloseIcon())));
|
|
5201
5226
|
};
|
|
5202
|
-
if (refinements.length === 0) {
|
|
5203
|
-
return null;
|
|
5204
|
-
}
|
|
5205
5227
|
// Group refinements by field for grouped layout
|
|
5206
5228
|
const groupedRefinements = layout === 'grouped'
|
|
5207
5229
|
? refinements.reduce((acc, r) => {
|
|
@@ -5213,13 +5235,17 @@ const CurrentRefinements = ({ refinements: refinementsProp, onRefinementClear, o
|
|
|
5213
5235
|
: null;
|
|
5214
5236
|
const containerStyles = {
|
|
5215
5237
|
...style,
|
|
5238
|
+
// When a custom list theme is provided, make the container transparent to layout
|
|
5239
|
+
// so the list div becomes the direct layout child (enables overflow scroll from parent)
|
|
5240
|
+
...(refinementsTheme.list && !style?.display ? { display: 'contents' } : {}),
|
|
5216
5241
|
};
|
|
5217
5242
|
const listStyles = {
|
|
5218
5243
|
display: 'flex',
|
|
5219
|
-
flexWrap: layout === 'vertical' ? 'nowrap' : 'wrap',
|
|
5220
5244
|
flexDirection: layout === 'vertical' ? 'column' : 'row',
|
|
5221
5245
|
alignItems: layout === 'vertical' ? 'flex-start' : 'center',
|
|
5222
|
-
marginBottom: showClearAll ? theme.spacing.medium : 0,
|
|
5246
|
+
marginBottom: showClearAll && refinements.length > 0 ? theme.spacing.medium : 0,
|
|
5247
|
+
// Only apply flex-wrap if no custom list theme is provided (let theme classes control wrapping)
|
|
5248
|
+
...(!refinementsTheme.list ? { flexWrap: layout === 'vertical' ? 'nowrap' : 'wrap' } : {}),
|
|
5223
5249
|
};
|
|
5224
5250
|
return (React.createElement("div", { className: clsx(refinementsTheme.container, className), style: containerStyles },
|
|
5225
5251
|
React.createElement("style", null, `
|
|
@@ -5228,34 +5254,35 @@ const CurrentRefinements = ({ refinements: refinementsProp, onRefinementClear, o
|
|
|
5228
5254
|
to { opacity: 1; transform: scale(1); }
|
|
5229
5255
|
}
|
|
5230
5256
|
`),
|
|
5231
|
-
|
|
5232
|
-
React.createElement("div", { className: refinementsTheme.
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5257
|
+
refinements.length > 0 && (React.createElement(React.Fragment, null,
|
|
5258
|
+
layout === 'grouped' && groupedRefinements ? (Object.entries(groupedRefinements).map(([field, items]) => (React.createElement("div", { key: field, className: refinementsTheme.group, style: { marginBottom: theme.spacing.medium } },
|
|
5259
|
+
React.createElement("div", { className: refinementsTheme.groupLabel, style: {
|
|
5260
|
+
fontSize: theme.typography.fontSize.small,
|
|
5261
|
+
fontWeight: 600,
|
|
5262
|
+
color: theme.colors.text,
|
|
5263
|
+
marginBottom: theme.spacing.small,
|
|
5264
|
+
textTransform: 'capitalize',
|
|
5265
|
+
} }, items[0]?.label || field),
|
|
5266
|
+
React.createElement("div", { role: "list", style: listStyles }, items.map((refinement, index) => {
|
|
5267
|
+
return renderRefinement
|
|
5268
|
+
? renderRefinement(refinement, index)
|
|
5269
|
+
: defaultRenderRefinement(refinement, index);
|
|
5270
|
+
})))))) : (React.createElement("div", { role: "list", className: refinementsTheme.list, style: listStyles }, refinements.map((refinement, index) => {
|
|
5240
5271
|
return renderRefinement
|
|
5241
5272
|
? renderRefinement(refinement, index)
|
|
5242
5273
|
: defaultRenderRefinement(refinement, index);
|
|
5243
|
-
})))
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
fontSize: theme.typography.fontSize.small,
|
|
5256
|
-
textDecoration: 'underline',
|
|
5257
|
-
transition: `background-color ${TRANSITIONS$7.fast}`,
|
|
5258
|
-
} }, "Clear all filters"))));
|
|
5274
|
+
}))),
|
|
5275
|
+
showClearAll && refinements.length > 1 && (React.createElement("button", { type: "button", onClick: handleClearAll, className: refinementsTheme.clearAllButton, style: {
|
|
5276
|
+
padding: `${theme.spacing.small} ${theme.spacing.medium}`,
|
|
5277
|
+
border: `1px solid ${theme.colors.border}`,
|
|
5278
|
+
borderRadius: typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium,
|
|
5279
|
+
backgroundColor: theme.colors.background,
|
|
5280
|
+
color: theme.colors.text,
|
|
5281
|
+
cursor: 'pointer',
|
|
5282
|
+
fontSize: theme.typography.fontSize.small,
|
|
5283
|
+
textDecoration: 'underline',
|
|
5284
|
+
transition: `background-color ${TRANSITIONS$7.fast}`,
|
|
5285
|
+
} }, "Clear all filters"))))));
|
|
5259
5286
|
};
|
|
5260
5287
|
|
|
5261
5288
|
/**
|
|
@@ -5323,16 +5350,17 @@ const ClearRefinements = ({ clearsQuery = false, resetLabel = 'Clear all filters
|
|
|
5323
5350
|
padding: `${theme.spacing.small} ${theme.spacing.medium}`,
|
|
5324
5351
|
fontSize: theme.typography.fontSize.medium,
|
|
5325
5352
|
fontWeight: theme.typography.fontWeight?.medium || 500,
|
|
5326
|
-
backgroundColor:
|
|
5327
|
-
color:
|
|
5353
|
+
backgroundColor: theme.colors.primary,
|
|
5354
|
+
color: '#ffffff',
|
|
5328
5355
|
border: 'none',
|
|
5329
5356
|
borderRadius: typeof theme.borderRadius === 'string'
|
|
5330
5357
|
? theme.borderRadius
|
|
5331
5358
|
: theme.borderRadius.medium,
|
|
5332
|
-
cursor: canClear ? 'pointer' : '
|
|
5333
|
-
opacity: canClear ? 1 : 0
|
|
5334
|
-
|
|
5335
|
-
|
|
5359
|
+
cursor: canClear ? 'pointer' : 'default',
|
|
5360
|
+
opacity: canClear ? 1 : 0,
|
|
5361
|
+
pointerEvents: canClear ? 'auto' : 'none',
|
|
5362
|
+
transition: 'none',
|
|
5363
|
+
}, "aria-label": resetLabel }, resetLabel)));
|
|
5336
5364
|
};
|
|
5337
5365
|
|
|
5338
5366
|
/**
|
|
@@ -5370,7 +5398,7 @@ const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px
|
|
|
5370
5398
|
padding: responsivePadding,
|
|
5371
5399
|
backgroundColor: theme.colors.background,
|
|
5372
5400
|
color: theme.colors.text,
|
|
5373
|
-
overflow:
|
|
5401
|
+
overflow: 'visible',
|
|
5374
5402
|
} },
|
|
5375
5403
|
sidebar && (!isMobile || showSidebarOnMobile) && (React.createElement("aside", { className: layoutTheme.sidebar, style: {
|
|
5376
5404
|
width: isMobile ? '100%' : sidebarWidth,
|