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