@searchcraft/javascript-sdk 0.10.5 → 0.10.6

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.
Files changed (44) hide show
  1. package/dist/components/index.d.ts +5 -3
  2. package/dist/components/index.js +4 -4
  3. package/dist/components/{p-6c471ee0.js → p-2735dceb.js} +2 -2
  4. package/dist/components/{p-6c471ee0.js.map → p-2735dceb.js.map} +1 -1
  5. package/dist/components/{p-f1afb299.js → p-6e5ed641.js} +2 -2
  6. package/dist/components/{p-f1afb299.js.map → p-6e5ed641.js.map} +1 -1
  7. package/dist/components/{p-8a4cfe99.js → p-75134ca7.js} +2 -2
  8. package/dist/components/{p-8a4cfe99.js.map → p-75134ca7.js.map} +1 -1
  9. package/dist/components/{p-f4b0ff45.js → p-9f4c50ed.js} +2 -2
  10. package/dist/components/{p-f4b0ff45.js.map → p-9f4c50ed.js.map} +1 -1
  11. package/dist/components/{p-bfad84c6.js → p-a7d611cd.js} +8 -12
  12. package/dist/components/p-a7d611cd.js.map +1 -0
  13. package/dist/components/{p-d9222264.js → p-aba24c85.js} +4 -4
  14. package/dist/components/{p-d9222264.js.map → p-aba24c85.js.map} +1 -1
  15. package/dist/components/{p-bb700499.js → p-b194d081.js} +104 -36
  16. package/dist/components/p-b194d081.js.map +1 -0
  17. package/dist/components/{p-7c10ca44.js → p-b54873ff.js} +315 -304
  18. package/dist/components/p-b54873ff.js.map +1 -0
  19. package/dist/components/{p-b72015a6.js → p-f2976b33.js} +2 -2
  20. package/dist/components/{p-b72015a6.js.map → p-f2976b33.js.map} +1 -1
  21. package/dist/components/searchcraft-ad.js +1 -1
  22. package/dist/components/searchcraft-facet-list.js +1 -1
  23. package/dist/components/searchcraft-filter-panel.js +3 -3
  24. package/dist/components/searchcraft-input-form.js +1 -1
  25. package/dist/components/searchcraft-pagination.js +1 -1
  26. package/dist/components/searchcraft-popover-button.js +1 -1
  27. package/dist/components/searchcraft-popover-footer.js +1 -1
  28. package/dist/components/searchcraft-popover-form.js +6 -6
  29. package/dist/components/searchcraft-popover-list-item.js +1 -1
  30. package/dist/components/searchcraft-popover-list-view.js +1 -1
  31. package/dist/components/searchcraft-results-info.js +1 -1
  32. package/dist/components/searchcraft-search-result.js +1 -1
  33. package/dist/components/searchcraft-search-results-per-page.js +1 -1
  34. package/dist/components/searchcraft-search-results.js +3 -3
  35. package/dist/components/searchcraft-toggle-button.js +1 -1
  36. package/dist/hydrate/index.js +277 -216
  37. package/dist/hydrate/index.mjs +277 -216
  38. package/dist/types/components/searchcraft-facet-list/searchcraft-facet-list.d.ts +17 -6
  39. package/dist/types/store/SearchcraftStore.types.d.ts +4 -3
  40. package/dist/types/utils/facets.d.ts +11 -1
  41. package/package.json +3 -3
  42. package/dist/components/p-7c10ca44.js.map +0 -1
  43. package/dist/components/p-bb700499.js.map +0 -1
  44. package/dist/components/p-bfad84c6.js.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import { p as proxyCustomElement, H, c as createEvent, h } from './p-5365011f.js';
2
- import { s as searchcraftStore } from './p-7c10ca44.js';
2
+ import { s as searchcraftStore } from './p-b54873ff.js';
3
3
  import './p-a64b4cba.js';
4
4
 
5
5
  /**
6
6
  * Helper function for getting a Node (FacetWithChildrenObject)
7
7
  * at a given path. Traverses the node tree to get the node.
8
8
  */
9
- const getNodeAtPath = (tree, nodePaths) => {
9
+ const getFacetTreeNodeAtPath = (tree, nodePaths) => {
10
10
  let cursor = tree;
11
11
  for (const nodePath of nodePaths) {
12
12
  cursor = cursor?.children[nodePath];
@@ -51,7 +51,7 @@ const mergeFacetTrees = (currentTree, incomingTree) => {
51
51
  const mergedTree = structuredClone(currentTree);
52
52
  const merge = (currentBranch, nodePath) => {
53
53
  const mergedBranch = structuredClone(currentBranch);
54
- const incomingBranch = getNodeAtPath(incomingTree, nodePath);
54
+ const incomingBranch = getFacetTreeNodeAtPath(incomingTree, nodePath);
55
55
  if (!incomingBranch) {
56
56
  return mergedBranch;
57
57
  }
@@ -136,17 +136,35 @@ const SearchcraftFacetList = /*@__PURE__*/ proxyCustomElement(class SearchcraftF
136
136
  */
137
137
  facetSelectionUpdated;
138
138
  /**
139
- * A Tree Representing all of the facets we know about to be rendered.
139
+ * The currently selected facet paths.
140
+ */
141
+ selectedPaths = {};
142
+ /**
143
+ * A Tree representing all of the facets collected from search responses.
140
144
  */
141
- facetTree = {
145
+ facetTreeCollectedFromSearchResponse = {
142
146
  path: '/',
143
147
  count: 0,
144
148
  children: {},
145
149
  };
146
150
  /**
147
- * The currently selected facet paths.
151
+ * A Tree representing the facet paths that are selected, but were not included
152
+ * in any search response.
148
153
  */
149
- selectedPaths = {};
154
+ facetTreeFromFacetPathsNotInSearchResponse = {
155
+ path: '/',
156
+ count: 0,
157
+ children: {},
158
+ };
159
+ /**
160
+ * The facet tree that ultimately gets rendered.
161
+ * This is a mergin of the facetTreeCollectedFromSearchResponse and the facetTreeFromFacetPathsNotInSearchResponse tree
162
+ */
163
+ renderedFacetTree = {
164
+ path: '/',
165
+ count: 0,
166
+ children: {},
167
+ };
150
168
  // Internal vars used to track when to perform various facet actions.
151
169
  lastTimeTaken;
152
170
  lastSearchTerm;
@@ -155,6 +173,9 @@ const SearchcraftFacetList = /*@__PURE__*/ proxyCustomElement(class SearchcraftF
155
173
  lastRangeValues;
156
174
  lastFacetValues;
157
175
  unsubscribe;
176
+ get areAnyFacetPathsSelected() {
177
+ return Object.keys(this.selectedPaths).some((key) => this.selectedPaths[key]);
178
+ }
158
179
  handleIncomingSearchResponse(state, actionType) {
159
180
  // Look at the incoming facet root from the search response, and convert it to a FacetTree
160
181
  const incomingFacetRoot = state.searchResponseFacetPrime?.find((facet) => this.fieldName === Object.keys(facet)[0]);
@@ -164,18 +185,20 @@ const SearchcraftFacetList = /*@__PURE__*/ proxyCustomElement(class SearchcraftF
164
185
  count: 0,
165
186
  children: incomingFacetsWithChildrenArray || [],
166
187
  });
167
- // Determine what action to take (merge with existing FacetTree vs overwrite FacetTree)
188
+ // Determine what action to take to accumulate items into the `facetTreeCollectedFromSearchResponse`.
189
+ // This facet tree gets accumulated in different ways depending on what action type occured.
168
190
  switch (actionType) {
169
191
  case 'SEARCH_TERM_EMPTY':
170
- this.facetTree = {
192
+ this.facetTreeCollectedFromSearchResponse = {
171
193
  path: '/',
172
194
  count: 0,
173
195
  children: {},
174
196
  };
175
197
  break;
176
198
  case 'NEW_SEARCH_TERM':
177
- this.facetTree = incomingFacetTree;
199
+ this.facetTreeCollectedFromSearchResponse = incomingFacetTree;
178
200
  break;
201
+ case 'NEW_SEARCH_TERM_WHILE_FACETS_ACTIVE':
179
202
  case 'EXACT_MATCH_UPDATE':
180
203
  case 'RANGE_VALUE_UPDATE': {
181
204
  if (state.supplementalFacetPrime) {
@@ -186,53 +209,95 @@ const SearchcraftFacetList = /*@__PURE__*/ proxyCustomElement(class SearchcraftF
186
209
  count: 0,
187
210
  children: supplementalFacetsWithChildrenArray || [],
188
211
  });
189
- this.facetTree = mergeFacetTrees(supplementalFacetTree, incomingFacetTree);
212
+ this.facetTreeCollectedFromSearchResponse = mergeFacetTrees(supplementalFacetTree, incomingFacetTree);
190
213
  }
191
214
  else {
192
- this.facetTree = incomingFacetTree;
215
+ this.facetTreeCollectedFromSearchResponse = incomingFacetTree;
193
216
  }
194
217
  break;
195
218
  }
196
219
  case 'FACET_UPDATE':
197
220
  case 'SORT_ORDER_UPDATE':
198
- this.facetTree = mergeFacetTrees(this.facetTree, incomingFacetTree);
221
+ this.facetTreeCollectedFromSearchResponse = mergeFacetTrees(this.facetTreeCollectedFromSearchResponse, incomingFacetTree);
199
222
  break;
200
223
  default:
201
- this.facetTree = mergeFacetTrees(this.facetTree, incomingFacetTree);
224
+ return;
202
225
  }
226
+ // Determine if there are any selected facet paths not in the current tree.
227
+ // If there are, we add them to "facetTreeFromFacetPathsNotInSearchResponse"
228
+ this.facetTreeFromFacetPathsNotInSearchResponse = {
229
+ path: '/',
230
+ count: 0,
231
+ children: {},
232
+ };
233
+ const collectedFacetArray = {
234
+ path: '/',
235
+ count: 0,
236
+ children: [],
237
+ };
238
+ for (const pathName of Object.keys(this.selectedPaths).filter((path) => this.selectedPaths[path])) {
239
+ const nodePaths = pathName.startsWith('/')
240
+ ? pathName.substring(1).split('/')
241
+ : pathName.split('/');
242
+ const wasFoundInFacetTree = !!getFacetTreeNodeAtPath(this.facetTreeCollectedFromSearchResponse, nodePaths);
243
+ const pathKeyName = nodePaths.at(-1);
244
+ if (!wasFoundInFacetTree && pathKeyName) {
245
+ collectedFacetArray.children?.push({
246
+ children: [],
247
+ count: 0,
248
+ path: pathName,
249
+ });
250
+ }
251
+ }
252
+ this.facetTreeFromFacetPathsNotInSearchResponse =
253
+ facetWithChildrenArrayToCompleteFacetTree(collectedFacetArray);
254
+ // Merges facetTreeCollectedFromSearchResponse with selectedFacetPathsNotInCurrentFacetTree.
255
+ // This results in a single, final facet tree that gets rendered in as Checkboxes
256
+ this.renderedFacetTree = deepMergeWithSpread(this.facetTreeFromFacetPathsNotInSearchResponse, this.facetTreeCollectedFromSearchResponse);
203
257
  }
204
- handleStateUpdate(state) {
258
+ handleStateUpdate(_state) {
259
+ const state = { ..._state };
205
260
  // Determine what action to take based on the current State
206
- if (this.lastSearchTerm !== state.searchTerm &&
207
- state.searchTerm.trim() === '') {
261
+ if (state.searchTerm.trim() === '') {
208
262
  this.handleIncomingSearchResponse(state, 'SEARCH_TERM_EMPTY');
263
+ this.lastSearchTerm = '';
209
264
  }
210
- else if (this.lastTimeTaken !== state.searchResponseTimeTaken) {
265
+ else if (this.lastTimeTaken !== state.searchResponseTimeTaken &&
266
+ state.searchClientRequest &&
267
+ typeof state.searchClientRequest === 'object') {
268
+ const request = state.searchClientRequest;
211
269
  let actionType = 'UNKNOWN';
212
- if (this.lastSearchTerm !== state.searchTerm) {
213
- actionType = 'NEW_SEARCH_TERM';
270
+ if (this.lastSearchTerm !== request.searchTerm) {
271
+ if (this.areAnyFacetPathsSelected) {
272
+ actionType = 'NEW_SEARCH_TERM_WHILE_FACETS_ACTIVE';
273
+ }
274
+ else {
275
+ actionType = 'NEW_SEARCH_TERM';
276
+ }
214
277
  }
215
- else if (this.lastRangeValues !== JSON.stringify(state.rangeValueForIndexFields)) {
278
+ else if (this.lastRangeValues !==
279
+ JSON.stringify(request.rangeValueForIndexFields)) {
216
280
  actionType = 'RANGE_VALUE_UPDATE';
217
281
  }
218
- else if (this.lastFacetValues !== JSON.stringify(state.facetPathsForIndexFields)) {
282
+ else if (this.lastFacetValues !==
283
+ JSON.stringify(request.facetPathsForIndexFields)) {
219
284
  actionType = 'FACET_UPDATE';
220
285
  }
221
- else if (this.lastSortType !== state.sortType) {
286
+ else if (this.lastSortType !== request.order_by) {
222
287
  actionType = 'SORT_ORDER_UPDATE';
223
288
  }
224
- else if (this.lastSearchMode !== state.searchMode) {
289
+ else if (this.lastSearchMode !== request.mode) {
225
290
  actionType = 'EXACT_MATCH_UPDATE';
226
291
  }
292
+ this.lastRangeValues = JSON.stringify(request.rangeValueForIndexFields);
293
+ this.lastFacetValues = JSON.stringify(request.facetPathsForIndexFields);
294
+ this.lastSortType = request.order_by;
295
+ this.lastSearchMode = request.mode;
296
+ this.lastSearchTerm = request.searchTerm;
297
+ this.lastTimeTaken = state.searchResponseTimeTaken;
227
298
  // Handle the incoming response, using the action we have determined.
228
299
  this.handleIncomingSearchResponse(state, actionType);
229
- this.lastSearchTerm = state.searchTerm;
230
- this.lastRangeValues = JSON.stringify(state.rangeValueForIndexFields);
231
- this.lastFacetValues = JSON.stringify(state.facetPathsForIndexFields);
232
- this.lastSortType = state.sortType;
233
- this.lastSearchMode = state.searchMode;
234
300
  }
235
- this.lastTimeTaken = state.searchResponseTimeTaken;
236
301
  }
237
302
  connectedCallback() {
238
303
  this.handleStateUpdate(searchcraftStore.getState());
@@ -304,20 +369,23 @@ const SearchcraftFacetList = /*@__PURE__*/ proxyCustomElement(class SearchcraftF
304
369
  if (!this.fieldName) {
305
370
  return;
306
371
  }
307
- if (Object.keys(this.facetTree.children).length === 0 &&
372
+ if (Object.keys(this.facetTreeCollectedFromSearchResponse.children).length ===
373
+ 0 &&
308
374
  (this.lastSearchTerm || '').trim().length === 0) {
309
375
  return;
310
376
  }
311
- if (Object.keys(this.facetTree.children).length === 0 &&
377
+ if (Object.keys(this.facetTreeCollectedFromSearchResponse.children).length ===
378
+ 0 &&
312
379
  (this.lastSearchTerm || '').trim().length > 0) {
313
380
  return (h("p", { class: 'searchcraft-facet-list-message' }, "No facets are available for this search query."));
314
381
  }
315
- return (h("div", { class: 'searchcraft-facet-list' }, this.renderFacet('@@root', this.facetTree)));
382
+ return (h("div", { class: 'searchcraft-facet-list' }, this.renderFacet('@@root', this.renderedFacetTree)));
316
383
  }
317
384
  }, [0, "searchcraft-facet-list", {
318
385
  "fieldName": [1, "field-name"],
319
- "facetTree": [32],
320
- "selectedPaths": [32]
386
+ "selectedPaths": [32],
387
+ "facetTreeCollectedFromSearchResponse": [32],
388
+ "renderedFacetTree": [32]
321
389
  }]);
322
390
  function defineCustomElement() {
323
391
  if (typeof customElements === "undefined") {
@@ -335,4 +403,4 @@ function defineCustomElement() {
335
403
 
336
404
  export { SearchcraftFacetList as S, defineCustomElement as d };
337
405
 
338
- //# sourceMappingURL=p-bb700499.js.map
406
+ //# sourceMappingURL=p-b194d081.js.map
@@ -0,0 +1 @@
1
+ {"file":"p-b194d081.js","mappings":"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","names":[],"sources":["src/utils/facets.ts","src/components/searchcraft-facet-list/searchcraft-facet-list.tsx"],"sourcesContent":["import type {\n Facet,\n FacetTree,\n FacetWithChildrenArray,\n FacetWithChildrenObject,\n} from '@searchcraft/core';\n\n/**\n * Helper function for getting a Node (FacetWithChildrenObject)\n * at a given path. Traverses the node tree to get the node.\n */\nexport const getFacetTreeNodeAtPath = (\n tree: FacetTree,\n nodePaths: string[],\n): FacetWithChildrenObject | undefined => {\n let cursor: FacetWithChildrenObject | undefined = tree;\n for (const nodePath of nodePaths) {\n cursor = cursor?.children[nodePath];\n }\n\n return cursor;\n};\n\n/**\n * Helper function to perform a deep merge.\n * Used for merging two branches of a facet tree together.\n */\nexport const deepMergeWithSpread = (obj1, obj2) => {\n const result = { ...obj1 };\n\n for (const key in obj2) {\n // biome-ignore lint/suspicious/noPrototypeBuiltins: <explanation>\n if (obj2.hasOwnProperty(key)) {\n if (obj2[key] instanceof Object && obj1[key] instanceof Object) {\n result[key] = deepMergeWithSpread(obj1[key], obj2[key]);\n } else {\n result[key] = obj2[key];\n }\n }\n }\n\n return result;\n};\n\n/**\n * Given an array of facet paths, removes parent facet paths so that only the\n * Leaf facets are sent with the search request.\n */\nexport function removeSubstringMatches(arr: string[]): string[] {\n return arr.filter(\n (entry, index, array) =>\n !array.some(\n (otherEntry, otherIndex) =>\n otherIndex !== index && otherEntry.includes(entry),\n ),\n );\n}\n\n/**\n * Merges a current facet tree with an incoming facet tree.\n *\n * At each branch, the incoming facet tree's facets override current facet tree\n *\n * @param currentTree\n * @param incomingTree\n */\nexport const mergeFacetTrees = (\n currentTree: FacetTree,\n incomingTree: FacetTree,\n): FacetTree => {\n const mergedTree: FacetTree = structuredClone(currentTree);\n\n const merge = (\n currentBranch: FacetWithChildrenObject,\n nodePath: string[],\n ): FacetWithChildrenObject => {\n const mergedBranch = structuredClone(currentBranch);\n const incomingBranch = getFacetTreeNodeAtPath(incomingTree, nodePath);\n\n if (!incomingBranch) {\n return mergedBranch;\n }\n\n for (const nodeName of Object.keys(currentBranch.children)) {\n if (incomingBranch.children[nodeName]) {\n mergedBranch.children[nodeName] = {\n ...deepMergeWithSpread(\n currentBranch.children[nodeName],\n incomingBranch.children[nodeName],\n ),\n count: mergedBranch.children[nodeName]?.count || 0,\n };\n } else if (mergedBranch.children[nodeName]) {\n mergedBranch.children[nodeName] = merge(\n mergedBranch.children[nodeName],\n [...nodePath, nodeName],\n );\n }\n }\n\n return mergedBranch;\n };\n\n return merge(mergedTree, []);\n};\n\n/**\n * A function that converts a FacetWithChidrenArray to a complete FacetTree object.\n *\n * It uses the `path` of each Facet to build the tree.\n *\n * @param facetWithChildArray\n */\nexport const facetWithChildrenArrayToCompleteFacetTree = (\n rootArray: FacetWithChildrenArray,\n): FacetTree => {\n // 1) Start with an empty tree at root \"/\"\n const tree: FacetTree = {\n path: '/',\n count: 0,\n children: {},\n };\n\n // 2) Recursively collect all nodes except the implicit \"/\" itself\n const allFacets: Facet[] = [];\n const collect = (node: FacetWithChildrenArray) => {\n if (node.path !== '/') {\n allFacets.push({ path: node.path, count: node.count });\n }\n if (node.children) {\n for (const child of node.children) {\n collect(child);\n }\n }\n };\n collect(rootArray);\n\n // 3) Insert each flat node into our tree, creating missing ancestors\n for (const { path, count } of allFacets) {\n const segments = path.split('/').filter(Boolean); // \"/sports/outdoors\" -> [\"sports\",\"outdoors\"]\n let cursor: FacetWithChildrenObject = tree; // start at the root\n for (const segment of segments) {\n // Build the full path of this level\n const prefixPath =\n cursor.path === '/' ? `/${segment}` : `${cursor.path}/${segment}`;\n\n // If this segment doesn't exist yet, create it\n if (!cursor.children[segment]) {\n cursor.children[segment] = {\n path: prefixPath,\n count: count,\n children: {},\n };\n }\n\n // Descend to the next level deeper\n cursor = cursor.children[segment];\n }\n\n // 4) now `cursor` is the node matching `path` — assign its real count\n cursor.count = count;\n }\n\n return tree;\n};\n","import {\n Component,\n h,\n State,\n Prop,\n Event,\n type EventEmitter,\n} from '@stencil/core';\n\nimport type {\n FacetRoot,\n FacetWithChildrenArray,\n FacetWithChildrenObject,\n} from '@searchcraft/core';\n\nimport { searchcraftStore, type SearchcraftState } from '@store';\nimport {\n deepMergeWithSpread,\n facetWithChildrenArrayToCompleteFacetTree,\n getFacetTreeNodeAtPath,\n mergeFacetTrees,\n removeSubstringMatches,\n} from '@utils';\n\ntype HandlerActionType =\n | 'SEARCH_TERM_EMPTY'\n | 'NEW_SEARCH_TERM'\n | 'NEW_SEARCH_TERM_WHILE_FACETS_ACTIVE'\n | 'RANGE_VALUE_UPDATE'\n | 'EXACT_MATCH_UPDATE'\n | 'SORT_ORDER_UPDATE'\n | 'FACET_UPDATE'\n | 'UNKNOWN';\n\n/**\n * This web component is designed to display facets in a search interface, allowing users to refine their search results by applying filters based on various attributes.\n * It is consumed within the `searchcraft-filter-panel`.\n *\n * @js-example\n * ```html\n * <!-- index.html -->\n * <searchcraft-facet-list field-name=\"title\" />\n * ```\n *\n * ```js\n * // index.js\n * const facetList = document.querySelector('searchcraft-facet-list');\n *\n * facetList.addEventListener('facetSelectionUpdated', () => {\n * console.log('Facet selection updated');\n * });\n * ```\n *\n * @internal\n */\n@Component({\n tag: 'searchcraft-facet-list',\n shadow: false,\n})\nexport class SearchcraftFacetList {\n /**\n * The name of the field where facets are applied.\n */\n @Prop() fieldName = '';\n\n /**\n * Emitted when the facets are updated.\n */\n @Event() facetSelectionUpdated?: EventEmitter<{ paths: string[] }>;\n\n /**\n * The currently selected facet paths.\n */\n @State() selectedPaths: Record<string, boolean> = {};\n\n /**\n * A Tree representing all of the facets collected from search responses.\n */\n @State() facetTreeCollectedFromSearchResponse: FacetWithChildrenObject = {\n path: '/',\n count: 0,\n children: {},\n };\n\n /**\n * A Tree representing the facet paths that are selected, but were not included\n * in any search response.\n */\n facetTreeFromFacetPathsNotInSearchResponse: FacetWithChildrenObject = {\n path: '/',\n count: 0,\n children: {},\n };\n\n /**\n * The facet tree that ultimately gets rendered.\n * This is a mergin of the facetTreeCollectedFromSearchResponse and the facetTreeFromFacetPathsNotInSearchResponse tree\n */\n @State() renderedFacetTree: FacetWithChildrenObject = {\n path: '/',\n count: 0,\n children: {},\n };\n\n // Internal vars used to track when to perform various facet actions.\n private lastTimeTaken?: number;\n private lastSearchTerm?: string;\n private lastSearchMode?: string;\n private lastSortType?: string | null;\n private lastRangeValues?: string;\n private lastFacetValues?: string;\n\n private unsubscribe?: () => void;\n\n get areAnyFacetPathsSelected(): boolean {\n return Object.keys(this.selectedPaths).some(\n (key) => this.selectedPaths[key],\n );\n }\n\n handleIncomingSearchResponse(\n state: SearchcraftState,\n actionType: HandlerActionType,\n ) {\n // Look at the incoming facet root from the search response, and convert it to a FacetTree\n const incomingFacetRoot: FacetRoot | undefined =\n state.searchResponseFacetPrime?.find(\n (facet) => this.fieldName === Object.keys(facet)[0],\n );\n const incomingFacetsWithChildrenArray = incomingFacetRoot?.[this.fieldName];\n const incomingFacetTree = facetWithChildrenArrayToCompleteFacetTree({\n path: '/',\n count: 0,\n children: incomingFacetsWithChildrenArray || [],\n });\n\n // Determine what action to take to accumulate items into the `facetTreeCollectedFromSearchResponse`.\n // This facet tree gets accumulated in different ways depending on what action type occured.\n switch (actionType) {\n case 'SEARCH_TERM_EMPTY':\n this.facetTreeCollectedFromSearchResponse = {\n path: '/',\n count: 0,\n children: {},\n };\n break;\n case 'NEW_SEARCH_TERM':\n this.facetTreeCollectedFromSearchResponse = incomingFacetTree;\n break;\n case 'NEW_SEARCH_TERM_WHILE_FACETS_ACTIVE':\n case 'EXACT_MATCH_UPDATE':\n case 'RANGE_VALUE_UPDATE': {\n if (state.supplementalFacetPrime) {\n const supplementalFacetRoot: FacetRoot | undefined =\n state.supplementalFacetPrime?.find(\n (facet) => this.fieldName === Object.keys(facet)[0],\n );\n const supplementalFacetsWithChildrenArray =\n supplementalFacetRoot?.[this.fieldName];\n const supplementalFacetTree =\n facetWithChildrenArrayToCompleteFacetTree({\n path: '/',\n count: 0,\n children: supplementalFacetsWithChildrenArray || [],\n });\n\n this.facetTreeCollectedFromSearchResponse = mergeFacetTrees(\n supplementalFacetTree,\n incomingFacetTree,\n );\n } else {\n this.facetTreeCollectedFromSearchResponse = incomingFacetTree;\n }\n break;\n }\n case 'FACET_UPDATE':\n case 'SORT_ORDER_UPDATE':\n this.facetTreeCollectedFromSearchResponse = mergeFacetTrees(\n this.facetTreeCollectedFromSearchResponse,\n incomingFacetTree,\n );\n break;\n default:\n return;\n }\n\n // Determine if there are any selected facet paths not in the current tree.\n // If there are, we add them to \"facetTreeFromFacetPathsNotInSearchResponse\"\n this.facetTreeFromFacetPathsNotInSearchResponse = {\n path: '/',\n count: 0,\n children: {},\n };\n const collectedFacetArray: FacetWithChildrenArray = {\n path: '/',\n count: 0,\n children: [],\n };\n for (const pathName of Object.keys(this.selectedPaths).filter(\n (path) => this.selectedPaths[path],\n )) {\n const nodePaths = pathName.startsWith('/')\n ? pathName.substring(1).split('/')\n : pathName.split('/');\n\n const wasFoundInFacetTree = !!getFacetTreeNodeAtPath(\n this.facetTreeCollectedFromSearchResponse,\n nodePaths,\n );\n\n const pathKeyName = nodePaths.at(-1);\n if (!wasFoundInFacetTree && pathKeyName) {\n collectedFacetArray.children?.push({\n children: [],\n count: 0,\n path: pathName,\n });\n }\n }\n this.facetTreeFromFacetPathsNotInSearchResponse =\n facetWithChildrenArrayToCompleteFacetTree(collectedFacetArray);\n\n // Merges facetTreeCollectedFromSearchResponse with selectedFacetPathsNotInCurrentFacetTree.\n // This results in a single, final facet tree that gets rendered in as Checkboxes\n this.renderedFacetTree = deepMergeWithSpread(\n this.facetTreeFromFacetPathsNotInSearchResponse,\n this.facetTreeCollectedFromSearchResponse,\n );\n }\n\n handleStateUpdate(_state: SearchcraftState) {\n const state = { ..._state };\n // Determine what action to take based on the current State\n if (state.searchTerm.trim() === '') {\n this.handleIncomingSearchResponse(state, 'SEARCH_TERM_EMPTY');\n this.lastSearchTerm = '';\n } else if (\n this.lastTimeTaken !== state.searchResponseTimeTaken &&\n state.searchClientRequest &&\n typeof state.searchClientRequest === 'object'\n ) {\n const request = state.searchClientRequest;\n let actionType: HandlerActionType = 'UNKNOWN';\n\n if (this.lastSearchTerm !== request.searchTerm) {\n if (this.areAnyFacetPathsSelected) {\n actionType = 'NEW_SEARCH_TERM_WHILE_FACETS_ACTIVE';\n } else {\n actionType = 'NEW_SEARCH_TERM';\n }\n } else if (\n this.lastRangeValues !==\n JSON.stringify(request.rangeValueForIndexFields)\n ) {\n actionType = 'RANGE_VALUE_UPDATE';\n } else if (\n this.lastFacetValues !==\n JSON.stringify(request.facetPathsForIndexFields)\n ) {\n actionType = 'FACET_UPDATE';\n } else if (this.lastSortType !== request.order_by) {\n actionType = 'SORT_ORDER_UPDATE';\n } else if (this.lastSearchMode !== request.mode) {\n actionType = 'EXACT_MATCH_UPDATE';\n }\n\n this.lastRangeValues = JSON.stringify(request.rangeValueForIndexFields);\n this.lastFacetValues = JSON.stringify(request.facetPathsForIndexFields);\n this.lastSortType = request.order_by;\n this.lastSearchMode = request.mode;\n this.lastSearchTerm = request.searchTerm;\n this.lastTimeTaken = state.searchResponseTimeTaken;\n // Handle the incoming response, using the action we have determined.\n this.handleIncomingSearchResponse(state, actionType);\n }\n }\n\n connectedCallback() {\n this.handleStateUpdate(searchcraftStore.getState());\n\n this.unsubscribe = searchcraftStore.subscribe((state) => {\n this.handleStateUpdate(state);\n });\n }\n\n disconnectedCallback() {\n this.unsubscribe?.();\n }\n\n handleCheckboxChange(path: string) {\n const isCheckboxChecked = !this.selectedPaths[path];\n\n if (isCheckboxChecked) {\n /**\n * Checkbox Checked: Add to the selectedPaths record\n * Uses spread operator here so UI updates.\n */\n this.selectedPaths = {\n ...this.selectedPaths,\n [path]: true,\n };\n } else {\n /**\n * Checkbox Uncheck: Remove any paths and sub-paths\n */\n const updatedPaths = Object.keys(this.selectedPaths).filter(\n (testPath) => !testPath.includes(path),\n );\n\n this.selectedPaths = updatedPaths.reduce(\n (acc, str) => {\n acc[str] = true;\n return acc;\n },\n {} as Record<string, boolean>,\n );\n }\n\n /**\n * Emit the paths array, with parent paths removed.\n */\n const paths = Object.keys(this.selectedPaths).filter(\n (path) => this.selectedPaths[path],\n );\n const pathsWithParentPathsRemoved = removeSubstringMatches(paths);\n\n this.facetSelectionUpdated?.emit({ paths: pathsWithParentPathsRemoved });\n }\n\n formatFacetName = (name: string): string => {\n const label = name.replace(/^\\//, '');\n return `${label.replace(/-/g, ' ').replace(/\\b\\w/g, (char) => char.toUpperCase())}`;\n };\n\n renderFacet(keyName: string, facet: FacetWithChildrenObject) {\n let isChildSelected = false;\n for (const path of Object.keys(this.selectedPaths)) {\n if (\n this.selectedPaths[path] &&\n path.startsWith(facet.path) &&\n path.length > facet.path.length\n ) {\n isChildSelected = true;\n }\n }\n\n const isSelected = Object.keys(this.selectedPaths).includes(facet.path);\n\n const shouldRenderChildren =\n (Object.keys(facet.children).length > 0 &&\n (isSelected || isChildSelected)) ||\n keyName === '@@root';\n\n return (\n <div class='searchcraft-facet-list-item'>\n {keyName !== '@@root' && (\n <label class='searchcraft-facet-list-checkbox-label'>\n <div class='searchcraft-facet-list-checkbox-input-wrapper'>\n <input\n class='searchcraft-facet-list-checkbox-input'\n checked={this.selectedPaths[facet.path]}\n onChange={(_event: Event) => {\n this.handleCheckboxChange(facet.path);\n }}\n type='checkbox'\n />\n {isChildSelected ? (\n <div class='searchcraft-facet-list-checkbox-input-dash-icon'>\n <svg viewBox='0 0 14 3' fill='none'>\n <title>Checkbox dash</title>\n <line\n x1='1.5'\n y1='1.5'\n x2='12.5'\n y2='1.5'\n stroke='white'\n stroke-width='3'\n stroke-linecap='round'\n />\n </svg>\n </div>\n ) : (\n <div class='searchcraft-facet-list-checkbox-input-check-icon'>\n <svg width='16' height='16' viewBox='0 0 16 16' fill='none'>\n <title>Checkbox check</title>\n <path\n d='M13.9999 2L5.74988 10L1.99988 6.36364'\n stroke='white'\n stroke-width='3'\n stroke-linecap='round'\n stroke-linejoin='round'\n />\n </svg>\n </div>\n )}\n </div>\n <span>\n {this.formatFacetName(keyName)} ({facet.count})\n </span>\n </label>\n )}\n {shouldRenderChildren && (\n <div\n class='searchcraft-facet-list'\n style={{\n paddingLeft: keyName !== '@@root' ? '24px' : '0px',\n paddingTop: keyName !== '@@root' ? '6px' : '0px',\n }}\n >\n {Object.keys(facet.children).map((key) => {\n if (facet.children[key]) {\n return this.renderFacet(key, facet.children[key]);\n }\n })}\n </div>\n )}\n </div>\n );\n }\n\n render() {\n if (!this.fieldName) {\n return;\n }\n\n if (\n Object.keys(this.facetTreeCollectedFromSearchResponse.children).length ===\n 0 &&\n (this.lastSearchTerm || '').trim().length === 0\n ) {\n return;\n }\n\n if (\n Object.keys(this.facetTreeCollectedFromSearchResponse.children).length ===\n 0 &&\n (this.lastSearchTerm || '').trim().length > 0\n ) {\n return (\n <p class='searchcraft-facet-list-message'>\n No facets are available for this search query.\n </p>\n );\n }\n\n return (\n <div class='searchcraft-facet-list'>\n {this.renderFacet('@@root', this.renderedFacetTree)}\n </div>\n );\n }\n}\n"],"version":3}