@internetarchive/collection-browser 2.7.2-alpha.0 → 2.7.2-alpha.2

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.
@@ -1,5 +1,5 @@
1
1
  import type { SmartQueryHeuristic, SmartFacet } from '../models';
2
- export declare class WikidataEntityHeuristic implements SmartQueryHeuristic {
2
+ export declare class WikidataHeuristic implements SmartQueryHeuristic {
3
3
  private static readonly ENTITIES;
4
4
  getRecommendedFacets(query: string): Promise<SmartFacet[]>;
5
5
  }
@@ -1,4 +1,4 @@
1
- // If wikidata says the query is an entity of type X, recommend facet Y, e.g.:
1
+ // If wikidata describes the top query result as X, recommend facet Y, e.g.:
2
2
  // X Y
3
3
  // written work mt:texts
4
4
  // film mt:movies
@@ -6,7 +6,8 @@
6
6
  // filmmaker mt:movies and creator:<query>
7
7
  // photographer mt:image and creator:<query>
8
8
  // visual artist mt:image and creator:<query>
9
- export class WikidataEntityHeuristic {
9
+ // etc.
10
+ export class WikidataHeuristic {
10
11
  async getRecommendedFacets(query) {
11
12
  var _a;
12
13
  const recommendations = [];
@@ -14,8 +15,9 @@ export class WikidataEntityHeuristic {
14
15
  const urlQuery = encodeURIComponent(query);
15
16
  const wikidataResponse = await fetch(`https://www.wikidata.org/w/api.php?action=wbsearchentities&search=${urlQuery}&format=json&language=en&uselang=en&origin=*&type=item&limit=5`);
16
17
  const searchResults = await wikidataResponse.json();
17
- for (const [keyword, facets] of Object.entries(WikidataEntityHeuristic.ENTITIES)) {
18
- if ((_a = searchResults.search[0]) === null || _a === void 0 ? void 0 : _a.description.includes(keyword)) {
18
+ for (const [keyword, facets] of Object.entries(WikidataHeuristic.ENTITIES)) {
19
+ const keywordRegex = new RegExp('\\b' + keyword + '\\b');
20
+ if (keywordRegex.test((_a = searchResults.search[0]) === null || _a === void 0 ? void 0 : _a.description)) {
19
21
  const entityName = searchResults.search[0].label;
20
22
  recommendations.push(...facets.map(sf => {
21
23
  var _a;
@@ -43,8 +45,11 @@ export class WikidataEntityHeuristic {
43
45
  }
44
46
  }
45
47
  }
46
- WikidataEntityHeuristic.ENTITIES = {
48
+ WikidataHeuristic.ENTITIES = {
49
+ 'written work': [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
47
50
  literature: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
51
+ book: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
52
+ novel: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
48
53
  filmmaker: [
49
54
  {
50
55
  label: 'Films by __QUERY',
@@ -1 +1 @@
1
- {"version":3,"file":"wikidata-heuristic.js","sourceRoot":"","sources":["../../../../../src/collection-facets/smart-facets/heuristics/wikidata-heuristic.ts"],"names":[],"mappings":"AAMA,8EAA8E;AAC9E,mBAAmB;AACnB,0BAA0B;AAC1B,2BAA2B;AAC3B,8CAA8C;AAC9C,+CAA+C;AAC/C,8CAA8C;AAC9C,8CAA8C;AAC9C,MAAM,OAAO,uBAAuB;IAsFlC,KAAK,CAAC,oBAAoB,CAAC,KAAa;;QACtC,MAAM,eAAe,GAAiB,EAAE,CAAC;QAEzC,IAAI;YACF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAE3C,MAAM,gBAAgB,GAAG,MAAM,KAAK,CAClC,qEAAqE,QAAQ,gEAAgE,CAC9I,CAAC;YACF,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,CAAC;YAEpD,KAAK,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAC5C,uBAAuB,CAAC,QAAQ,CACjC,EAAE;gBACD,IAAI,MAAA,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,0CAAE,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;oBAC1D,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;oBACjD,eAAe,CAAC,IAAI,CAClB,GAAG,MAAM,CAAC,GAAG,CACX,EAAE,CAAC,EAAE;;wBACH,OAAA,CAAC;4BACC,KAAK,EAAE,MAAA,EAAE,CAAC,KAAK,0CAAE,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC;4BAC/C,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;;gCACxB,MAAM,QAAQ,GAAG;oCACf,GAAG,CAAC;oCACJ,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC;iCACjD,CAAC;gCAEF,IAAI,CAAC,CAAC,WAAW,EAAE;oCACjB,QAAQ,CAAC,WAAW,GAAG,MAAA,QAAQ,CAAC,WAAW,0CAAE,OAAO,CAClD,SAAS,EACT,UAAU,CACX,CAAC;iCACH;gCAED,OAAO,QAAQ,CAAC;4BAClB,CAAC,CAAC;yBACY,CAAA,CAAA;qBAAA,CACnB,CACF,CAAC;iBACH;aACF;YAED,OAAO,eAAe,CAAC;SACxB;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,EAAE,CAAC;SACX;IACH,CAAC;;AAnIuB,gCAAQ,GAAoB;IAClD,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC1E,SAAS,EAAE;QACT;YACE,KAAK,EAAE,kBAAkB;YACzB,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE;gBAC/C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,MAAM,EAAE;QACN;YACE,KAAK,EAAE,oBAAoB;YAC3B,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,MAAM,EAAE;QACN;YACE,KAAK,EAAE,oBAAoB;YAC3B,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,IAAI,EAAE;QACJ;YACE,KAAK,EAAE,oBAAoB;YAC3B,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,YAAY,EAAE;QACZ;YACE,KAAK,EAAE,mBAAmB;YAC1B,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,OAAO,EAAE;QACP;YACE,KAAK,EAAE,mBAAmB;YAC1B,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,MAAM,EAAE;QACN;YACE,KAAK,EAAE,kBAAkB;YACzB,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,UAAU,EAAE;QACV;YACE,KAAK,EAAE,kBAAkB;YACzB,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;IACD,QAAQ,EAAE;QACR;YACE,KAAK,EAAE,kBAAkB;YACzB,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE;gBAC9C,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE;aAC/C;SACF;KACF;CACF,CAAC","sourcesContent":["import type {\n SmartQueryHeuristic,\n KeywordFacetMap,\n SmartFacet,\n} from '../models';\n\n// If wikidata says the query is an entity of type X, recommend facet Y, e.g.:\n// X Y\n// written work mt:texts\n// film mt:movies\n// author mt:texts and creator:<query>\n// filmmaker mt:movies and creator:<query>\n// photographer mt:image and creator:<query>\n// visual artist mt:image and creator:<query>\nexport class WikidataEntityHeuristic implements SmartQueryHeuristic {\n private static readonly ENTITIES: KeywordFacetMap = {\n literature: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],\n filmmaker: [\n {\n label: 'Films by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'movies' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n author: [\n {\n label: 'Writing by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'texts' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n writer: [\n {\n label: 'Writing by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'texts' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n poet: [\n {\n label: 'Writing by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'texts' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n photographer: [\n {\n label: 'Images by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'image' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n painter: [\n {\n label: 'Images by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'image' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n singer: [\n {\n label: 'Music by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'audio' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n songwriter: [\n {\n label: 'Music by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'audio' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n musician: [\n {\n label: 'Music by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'audio' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n };\n\n async getRecommendedFacets(query: string): Promise<SmartFacet[]> {\n const recommendations: SmartFacet[] = [];\n\n try {\n const urlQuery = encodeURIComponent(query);\n\n const wikidataResponse = await fetch(\n `https://www.wikidata.org/w/api.php?action=wbsearchentities&search=${urlQuery}&format=json&language=en&uselang=en&origin=*&type=item&limit=5`\n );\n const searchResults = await wikidataResponse.json();\n\n for (const [keyword, facets] of Object.entries(\n WikidataEntityHeuristic.ENTITIES\n )) {\n if (searchResults.search[0]?.description.includes(keyword)) {\n const entityName = searchResults.search[0].label;\n recommendations.push(\n ...facets.map(\n sf =>\n ({\n label: sf.label?.replace('__QUERY', entityName),\n facets: sf.facets.map(f => {\n const replaced = {\n ...f,\n bucketKey: f.bucketKey.replace('__QUERY', query),\n };\n\n if (f.displayText) {\n replaced.displayText = replaced.displayText?.replace(\n '__QUERY',\n entityName\n );\n }\n\n return replaced;\n }),\n } as SmartFacet)\n )\n );\n }\n }\n\n return recommendations;\n } catch (err) {\n return [];\n }\n }\n}\n"]}
1
+ {"version":3,"file":"wikidata-heuristic.js","sourceRoot":"","sources":["../../../../../src/collection-facets/smart-facets/heuristics/wikidata-heuristic.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n SmartQueryHeuristic,\n KeywordFacetMap,\n SmartFacet,\n} from '../models';\n\n// If wikidata describes the top query result as X, recommend facet Y, e.g.:\n// X Y\n// written work mt:texts\n// film mt:movies\n// author mt:texts and creator:<query>\n// filmmaker mt:movies and creator:<query>\n// photographer mt:image and creator:<query>\n// visual artist mt:image and creator:<query>\n// etc.\nexport class WikidataHeuristic implements SmartQueryHeuristic {\n private static readonly ENTITIES: KeywordFacetMap = {\n 'written work': [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],\n literature: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],\n book: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],\n novel: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],\n filmmaker: [\n {\n label: 'Films by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'movies' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n author: [\n {\n label: 'Writing by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'texts' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n writer: [\n {\n label: 'Writing by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'texts' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n poet: [\n {\n label: 'Writing by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'texts' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n photographer: [\n {\n label: 'Images by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'image' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n painter: [\n {\n label: 'Images by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'image' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n singer: [\n {\n label: 'Music by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'audio' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n songwriter: [\n {\n label: 'Music by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'audio' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n musician: [\n {\n label: 'Music by __QUERY',\n facets: [\n { facetType: 'mediatype', bucketKey: 'audio' },\n { facetType: 'creator', bucketKey: '__QUERY' },\n ],\n },\n ],\n };\n\n async getRecommendedFacets(query: string): Promise<SmartFacet[]> {\n const recommendations: SmartFacet[] = [];\n\n try {\n const urlQuery = encodeURIComponent(query);\n\n const wikidataResponse = await fetch(\n `https://www.wikidata.org/w/api.php?action=wbsearchentities&search=${urlQuery}&format=json&language=en&uselang=en&origin=*&type=item&limit=5`\n );\n const searchResults = await wikidataResponse.json();\n\n for (const [keyword, facets] of Object.entries(\n WikidataHeuristic.ENTITIES\n )) {\n const keywordRegex = new RegExp('\\\\b' + keyword + '\\\\b');\n if (keywordRegex.test(searchResults.search[0]?.description)) {\n const entityName = searchResults.search[0].label;\n recommendations.push(\n ...facets.map(\n sf =>\n ({\n label: sf.label?.replace('__QUERY', entityName),\n facets: sf.facets.map(f => {\n const replaced = {\n ...f,\n bucketKey: f.bucketKey.replace('__QUERY', query),\n };\n\n if (f.displayText) {\n replaced.displayText = replaced.displayText?.replace(\n '__QUERY',\n entityName\n );\n }\n\n return replaced;\n }),\n } as SmartFacet)\n )\n );\n }\n }\n\n return recommendations;\n } catch (err) {\n return [];\n }\n }\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  import { LitElement, CSSResultGroup, nothing } from 'lit';
2
2
  import type { IaDropdown } from '@internetarchive/ia-dropdown';
3
- import type { FacetBucket, FacetOption } from '../../models';
3
+ import type { FacetRef, SmartFacet } from './models';
4
4
  export declare class SmartFacetDropdown extends LitElement {
5
- facetType?: FacetOption;
6
- buckets?: FacetBucket[];
7
- activeBucket?: FacetBucket;
5
+ facetInfo?: SmartFacet[];
6
+ labelPrefix?: string;
7
+ activeFacetRef?: FacetRef;
8
8
  dropdown?: IaDropdown;
9
9
  render(): import("lit-html").TemplateResult<1> | typeof nothing;
10
10
  private get dropdownOptions();
@@ -6,12 +6,12 @@ let SmartFacetDropdown = class SmartFacetDropdown extends LitElement {
6
6
  // COMPONENT LIFECYCLE METHODS
7
7
  //
8
8
  render() {
9
- var _a;
10
- if (!this.facetType || !this.buckets || !this.activeBucket)
9
+ var _a, _b;
10
+ if (!this.facetInfo || !this.activeFacetRef)
11
11
  return nothing;
12
- if (this.buckets.length === 0)
12
+ if (this.facetInfo.length === 0)
13
13
  return nothing;
14
- const displayText = (_a = this.activeBucket.displayText) !== null && _a !== void 0 ? _a : this.activeBucket.key;
14
+ const displayText = (_a = this.activeFacetRef.displayText) !== null && _a !== void 0 ? _a : this.activeFacetRef.bucketKey;
15
15
  if (!displayText)
16
16
  return nothing;
17
17
  return html `
@@ -27,7 +27,7 @@ let SmartFacetDropdown = class SmartFacetDropdown extends LitElement {
27
27
  @optionSelected=${this.optionSelected}
28
28
  >
29
29
  <span class="dropdown-label" slot="dropdown-label"
30
- >${displayText}</span
30
+ >${(_b = this.labelPrefix) !== null && _b !== void 0 ? _b : nothing} ${displayText}</span
31
31
  >
32
32
  </ia-dropdown>
33
33
  </div>
@@ -38,29 +38,45 @@ let SmartFacetDropdown = class SmartFacetDropdown extends LitElement {
38
38
  //
39
39
  get dropdownOptions() {
40
40
  var _a, _b;
41
- return ((_b = (_a = this.buckets) === null || _a === void 0 ? void 0 : _a.map(bucket => {
42
- var _a;
43
- return ({
44
- id: bucket.key,
45
- label: (_a = bucket.displayText) !== null && _a !== void 0 ? _a : bucket.key,
46
- });
41
+ return ((_b = (_a = this.facetInfo) === null || _a === void 0 ? void 0 : _a.map(smartFacet => {
42
+ var _a, _b;
43
+ const firstFacet = smartFacet.facets[0];
44
+ return {
45
+ id: firstFacet.bucketKey,
46
+ label: (_b = (_a = smartFacet.label) !== null && _a !== void 0 ? _a : firstFacet.displayText) !== null && _b !== void 0 ? _b : firstFacet.bucketKey,
47
+ };
47
48
  })) !== null && _b !== void 0 ? _b : []);
48
49
  }
49
50
  get activeDropdownOption() {
50
- if (!this.activeBucket)
51
+ if (!this.activeFacetRef)
51
52
  return undefined;
52
- return this.dropdownOptions.find(opt => { var _a; return opt.id === ((_a = this.activeBucket) === null || _a === void 0 ? void 0 : _a.key); });
53
+ return this.dropdownOptions.find(opt => { var _a; return opt.id === ((_a = this.activeFacetRef) === null || _a === void 0 ? void 0 : _a.bucketKey); });
53
54
  }
54
55
  optionSelected(e) {
55
- var _a;
56
+ if (!this.facetInfo || !this.activeFacetRef)
57
+ return;
58
+ let selectedSmartFacet;
59
+ for (const smartFacet of this.facetInfo) {
60
+ const selectedRef = smartFacet.facets.find(b => b.bucketKey === e.detail.option.id);
61
+ if (selectedRef) {
62
+ this.activeFacetRef = selectedRef;
63
+ selectedSmartFacet = smartFacet;
64
+ }
65
+ }
66
+ if (!selectedSmartFacet)
67
+ return;
56
68
  this.dispatchEvent(new CustomEvent('facetClick', {
57
69
  detail: {
58
- facetType: this.facetType,
59
- bucket: {
60
- ...(_a = this.buckets) === null || _a === void 0 ? void 0 : _a.find(b => b.key === e.detail.option.id),
61
- state: 'selected',
62
- },
63
- negative: false,
70
+ smartFacet: selectedSmartFacet,
71
+ details: [{
72
+ facetType: this.activeFacetRef.facetType,
73
+ bucket: {
74
+ key: this.activeFacetRef.bucketKey,
75
+ count: 0,
76
+ state: 'selected',
77
+ },
78
+ negative: false,
79
+ }],
64
80
  },
65
81
  }));
66
82
  }
@@ -93,15 +109,15 @@ let SmartFacetDropdown = class SmartFacetDropdown extends LitElement {
93
109
  `;
94
110
  }
95
111
  };
96
- __decorate([
97
- property({ type: String })
98
- ], SmartFacetDropdown.prototype, "facetType", void 0);
99
112
  __decorate([
100
113
  property({ type: Array })
101
- ], SmartFacetDropdown.prototype, "buckets", void 0);
114
+ ], SmartFacetDropdown.prototype, "facetInfo", void 0);
115
+ __decorate([
116
+ property({ type: String })
117
+ ], SmartFacetDropdown.prototype, "labelPrefix", void 0);
102
118
  __decorate([
103
119
  property({ type: Object })
104
- ], SmartFacetDropdown.prototype, "activeBucket", void 0);
120
+ ], SmartFacetDropdown.prototype, "activeFacetRef", void 0);
105
121
  __decorate([
106
122
  query('ia-dropdown')
107
123
  ], SmartFacetDropdown.prototype, "dropdown", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"smart-facet-dropdown.js","sourceRoot":"","sources":["../../../../src/collection-facets/smart-facets/smart-facet-dropdown.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAkB,OAAO,EAAE,MAAM,KAAK,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAKnE,IAAa,kBAAkB,GAA/B,MAAa,kBAAmB,SAAQ,UAAU;IAShD,EAAE;IACF,8BAA8B;IAC9B,EAAE;IAEF,MAAM;;QACJ,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO,OAAO,CAAC;QAC3E,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QAE9C,MAAM,WAAW,GAAG,MAAA,IAAI,CAAC,YAAY,CAAC,WAAW,mCAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC;QAC3E,IAAI,CAAC,WAAW;YAAE,OAAO,OAAO,CAAC;QAEjC,OAAO,IAAI,CAAA;;;;;;;;qBAQM,IAAI,CAAC,eAAe;4BACb,IAAI,CAAC,oBAAoB;4BACzB,IAAI,CAAC,cAAc;;;eAGhC,WAAW;;;;KAIrB,CAAC;IACJ,CAAC;IAED,EAAE;IACF,gBAAgB;IAChB,EAAE;IAEF,IAAY,eAAe;;QACzB,OAAO,CACL,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,GAAG,CAAC,MAAM,CAAC,EAAE;;YAAC,OAAA,CAAC;gBAC3B,EAAE,EAAE,MAAM,CAAC,GAAG;gBACd,KAAK,EAAE,MAAA,MAAM,CAAC,WAAW,mCAAI,MAAM,CAAC,GAAG;aACxC,CAAC,CAAA;SAAA,CAAC,mCAAI,EAAE,CACV,CAAC;IACJ,CAAC;IAED,IAAY,oBAAoB;QAC9B,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO,SAAS,CAAC;QACzC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,WAAC,OAAA,GAAG,CAAC,EAAE,MAAK,MAAA,IAAI,CAAC,YAAY,0CAAE,GAAG,CAAA,CAAA,EAAA,CAAC,CAAC;IAC7E,CAAC;IAEO,cAAc,CAAC,CAA2C;;QAChE,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,YAAY,EAAE;YAC5B,MAAM,EAAE;gBACN,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,MAAM,EAAE;oBACN,GAAG,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;oBACxD,KAAK,EAAE,UAAU;iBAClB;gBACD,QAAQ,EAAE,KAAK;aAChB;SACF,CAAC,CACH,CAAC;IACJ,CAAC;IAED,EAAE;IACF,SAAS;IACT,EAAE;IAEF,MAAM,KAAK,MAAM;QACf,OAAO,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;KAsBT,CAAC;IACJ,CAAC;CACF,CAAA;AArG6B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;qDAAyB;AAEzB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;mDAAyB;AAEvB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;wDAA4B;AAEjC;IAArB,KAAK,CAAC,aAAa,CAAC;oDAAuB;AAPjC,kBAAkB;IAD9B,aAAa,CAAC,sBAAsB,CAAC;GACzB,kBAAkB,CAsG9B;SAtGY,kBAAkB","sourcesContent":["import { css, html, LitElement, CSSResultGroup, nothing } from 'lit';\nimport { customElement, property, query } from 'lit/decorators.js';\nimport type { IaDropdown, optionInterface } from '@internetarchive/ia-dropdown';\nimport type { FacetBucket, FacetOption } from '../../models';\n\n@customElement('smart-facet-dropdown')\nexport class SmartFacetDropdown extends LitElement {\n @property({ type: String }) facetType?: FacetOption;\n\n @property({ type: Array }) buckets?: FacetBucket[];\n\n @property({ type: Object }) activeBucket?: FacetBucket;\n\n @query('ia-dropdown') dropdown?: IaDropdown;\n\n //\n // COMPONENT LIFECYCLE METHODS\n //\n\n render() {\n if (!this.facetType || !this.buckets || !this.activeBucket) return nothing;\n if (this.buckets.length === 0) return nothing;\n\n const displayText = this.activeBucket.displayText ?? this.activeBucket.key;\n if (!displayText) return nothing;\n\n return html`\n <div class=\"dropdown-container\">\n <ia-dropdown\n class=\"dropdown\"\n displayCaret\n openViaButton\n closeOnSelect\n includeSelectedOption\n .options=${this.dropdownOptions}\n .selectedOption=${this.activeDropdownOption}\n @optionSelected=${this.optionSelected}\n >\n <span class=\"dropdown-label\" slot=\"dropdown-label\"\n >${displayText}</span\n >\n </ia-dropdown>\n </div>\n `;\n }\n\n //\n // OTHER METHODS\n //\n\n private get dropdownOptions(): optionInterface[] {\n return (\n this.buckets?.map(bucket => ({\n id: bucket.key,\n label: bucket.displayText ?? bucket.key,\n })) ?? []\n );\n }\n\n private get activeDropdownOption(): optionInterface | undefined {\n if (!this.activeBucket) return undefined;\n return this.dropdownOptions.find(opt => opt.id === this.activeBucket?.key);\n }\n\n private optionSelected(e: CustomEvent<{ option: optionInterface }>): void {\n this.dispatchEvent(\n new CustomEvent('facetClick', {\n detail: {\n facetType: this.facetType,\n bucket: {\n ...this.buckets?.find(b => b.key === e.detail.option.id),\n state: 'selected',\n },\n negative: false,\n },\n })\n );\n }\n\n //\n // STYLES\n //\n\n static get styles(): CSSResultGroup {\n return css`\n .dropdown-container {\n padding: 5px 8px;\n border-radius: 5px;\n background: #194880;\n color: white;\n font-size: 1.6rem;\n font-family: inherit;\n box-shadow: 1px 1px rgba(0, 0, 0, 0.4);\n }\n\n .dropdown-label {\n font-size: 1.6rem;\n font-family: inherit;\n }\n\n .dropdown {\n --dropdownBorderWidth: 5px;\n --dropdownBorderColor: transparent;\n --caretWidth: 14px;\n --caretHeight: 14px;\n }\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"smart-facet-dropdown.js","sourceRoot":"","sources":["../../../../src/collection-facets/smart-facets/smart-facet-dropdown.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, CSSResultGroup, nothing } from 'lit';\nimport { customElement, property, query } from 'lit/decorators.js';\nimport type { IaDropdown, optionInterface } from '@internetarchive/ia-dropdown';\nimport type { FacetRef, SmartFacet, SmartFacetEvent } from './models';\n\n@customElement('smart-facet-dropdown')\nexport class SmartFacetDropdown extends LitElement {\n @property({ type: Array }) facetInfo?: SmartFacet[];\n\n @property({ type: String }) labelPrefix?: string;\n\n @property({ type: Object }) activeFacetRef?: FacetRef;\n\n @query('ia-dropdown') dropdown?: IaDropdown;\n\n //\n // COMPONENT LIFECYCLE METHODS\n //\n\n render() {\n if (!this.facetInfo || !this.activeFacetRef) return nothing;\n if (this.facetInfo.length === 0) return nothing;\n\n const displayText = this.activeFacetRef.displayText ?? this.activeFacetRef.bucketKey;\n if (!displayText) return nothing;\n\n return html`\n <div class=\"dropdown-container\">\n <ia-dropdown\n class=\"dropdown\"\n displayCaret\n openViaButton\n closeOnSelect\n includeSelectedOption\n .options=${this.dropdownOptions}\n .selectedOption=${this.activeDropdownOption}\n @optionSelected=${this.optionSelected}\n >\n <span class=\"dropdown-label\" slot=\"dropdown-label\"\n >${this.labelPrefix ?? nothing} ${displayText}</span\n >\n </ia-dropdown>\n </div>\n `;\n }\n\n //\n // OTHER METHODS\n //\n\n private get dropdownOptions(): optionInterface[] {\n return (\n this.facetInfo?.map(smartFacet => {\n const firstFacet = smartFacet.facets[0];\n return {\n id: firstFacet.bucketKey,\n label: smartFacet.label ?? firstFacet.displayText ?? firstFacet.bucketKey,\n };\n }) ?? []\n );\n }\n\n private get activeDropdownOption(): optionInterface | undefined {\n if (!this.activeFacetRef) return undefined;\n return this.dropdownOptions.find(opt => opt.id === this.activeFacetRef?.bucketKey);\n }\n\n private optionSelected(e: CustomEvent<{ option: optionInterface }>): void {\n if (!this.facetInfo || !this.activeFacetRef) return;\n\n let selectedSmartFacet;\n for (const smartFacet of this.facetInfo) {\n const selectedRef = smartFacet.facets.find(b => b.bucketKey === e.detail.option.id);\n if (selectedRef) {\n this.activeFacetRef = selectedRef;\n selectedSmartFacet = smartFacet;\n }\n }\n\n if (!selectedSmartFacet) return;\n\n this.dispatchEvent(\n new CustomEvent<SmartFacetEvent>('facetClick', {\n detail: {\n smartFacet: selectedSmartFacet,\n details: [{\n facetType: this.activeFacetRef.facetType,\n bucket: {\n key: this.activeFacetRef.bucketKey,\n count: 0,\n state: 'selected',\n },\n negative: false,\n }],\n },\n })\n );\n }\n\n //\n // STYLES\n //\n\n static get styles(): CSSResultGroup {\n return css`\n .dropdown-container {\n padding: 5px 8px;\n border-radius: 5px;\n background: #194880;\n color: white;\n font-size: 1.6rem;\n font-family: inherit;\n box-shadow: 1px 1px rgba(0, 0, 0, 0.4);\n }\n\n .dropdown-label {\n font-size: 1.6rem;\n font-family: inherit;\n }\n\n .dropdown {\n --dropdownBorderWidth: 5px;\n --dropdownBorderColor: transparent;\n --caretWidth: 14px;\n --caretHeight: 14px;\n }\n `;\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { SmartFacet } from "./models";
2
+ export declare function smartFacetEquals(sf1: SmartFacet, sf2: SmartFacet): boolean;
@@ -0,0 +1,15 @@
1
+ function facetRefEquals(ref1, ref2) {
2
+ if (ref1 === undefined)
3
+ return ref2 !== undefined;
4
+ if (ref2 === undefined)
5
+ return ref1 !== undefined;
6
+ return ref1.facetType === ref2.facetType &&
7
+ ref1.bucketKey === ref2.bucketKey &&
8
+ ref1.displayText === ref2.displayText;
9
+ }
10
+ export function smartFacetEquals(sf1, sf2) {
11
+ return sf1.label === sf2.label &&
12
+ sf1.facets.length === sf2.facets.length &&
13
+ sf1.facets.every((sf, i) => facetRefEquals(sf, sf2.facets[i]));
14
+ }
15
+ //# sourceMappingURL=smart-facet-equals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-facet-equals.js","sourceRoot":"","sources":["../../../../src/collection-facets/smart-facets/smart-facet-equals.ts"],"names":[],"mappings":"AAEA,SAAS,cAAc,CAAC,IAAe,EAAE,IAAe;IACtD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,KAAK,SAAS,CAAC;IAClD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,KAAK,SAAS,CAAC;IAElD,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS;QACtC,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS;QACjC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAe,EAAE,GAAe;IAC/D,OAAO,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK;QAC5B,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM;QACvC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC","sourcesContent":["import type { FacetRef, SmartFacet } from \"./models\";\n\nfunction facetRefEquals(ref1?: FacetRef, ref2?: FacetRef): boolean {\n if (ref1 === undefined) return ref2 !== undefined;\n if (ref2 === undefined) return ref1 !== undefined;\n \n return ref1.facetType === ref2.facetType &&\n ref1.bucketKey === ref2.bucketKey &&\n ref1.displayText === ref2.displayText;\n}\n\nexport function smartFacetEquals(sf1: SmartFacet, sf2: SmartFacet) {\n return sf1.label === sf2.label &&\n sf1.facets.length === sf2.facets.length &&\n sf1.facets.every((sf, i) => facetRefEquals(sf, sf2.facets[i]));\n}"]}
@@ -1,4 +1,13 @@
1
1
  import type { SmartFacet, SmartQueryHeuristic } from './models';
2
2
  export declare class SmartQueryHeuristicGroup implements SmartQueryHeuristic {
3
3
  getRecommendedFacets(query: string): Promise<SmartFacet[]>;
4
+ /**
5
+ * Removes any duplicated smart facets from the given array.
6
+ * Smart facets are equal if they have the same `label` and same
7
+ * set of facet refs. Only the first occurrence of a given smart
8
+ * facet is kept.
9
+ * @param facets The array of smart facets to deduplicate
10
+ * @returns A new array containing the deduplicated set of facets
11
+ */
12
+ private dedupe;
4
13
  }
@@ -1,11 +1,27 @@
1
1
  import { QueryKeywordsHeuristic } from './heuristics/query-keywords-heuristic';
2
- import { WikidataEntityHeuristic } from './heuristics/wikidata-heuristic';
2
+ import { WikidataHeuristic } from './heuristics/wikidata-heuristic';
3
+ import { smartFacetEquals } from './smart-facet-equals';
3
4
  export class SmartQueryHeuristicGroup {
4
5
  async getRecommendedFacets(query) {
5
- return [
6
+ return this.dedupe([
6
7
  ...(await new QueryKeywordsHeuristic().getRecommendedFacets(query)),
7
- ...(await new WikidataEntityHeuristic().getRecommendedFacets(query)),
8
- ];
8
+ ...(await new WikidataHeuristic().getRecommendedFacets(query)),
9
+ ]);
10
+ }
11
+ /**
12
+ * Removes any duplicated smart facets from the given array.
13
+ * Smart facets are equal if they have the same `label` and same
14
+ * set of facet refs. Only the first occurrence of a given smart
15
+ * facet is kept.
16
+ * @param facets The array of smart facets to deduplicate
17
+ * @returns A new array containing the deduplicated set of facets
18
+ */
19
+ dedupe(facets) {
20
+ let result = [...facets];
21
+ for (const curFacet of facets) {
22
+ result = result.filter(sf => curFacet === sf || !smartFacetEquals(curFacet, sf));
23
+ }
24
+ return result;
9
25
  }
10
26
  }
11
27
  //# sourceMappingURL=smart-facet-heuristics.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"smart-facet-heuristics.js","sourceRoot":"","sources":["../../../../src/collection-facets/smart-facets/smart-facet-heuristics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG1E,MAAM,OAAO,wBAAwB;IACnC,KAAK,CAAC,oBAAoB,CAAC,KAAa;QACtC,OAAO;YACL,GAAG,CAAC,MAAM,IAAI,sBAAsB,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACnE,GAAG,CAAC,MAAM,IAAI,uBAAuB,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;SACrE,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { QueryKeywordsHeuristic } from './heuristics/query-keywords-heuristic';\nimport { WikidataEntityHeuristic } from './heuristics/wikidata-heuristic';\nimport type { SmartFacet, SmartQueryHeuristic } from './models';\n\nexport class SmartQueryHeuristicGroup implements SmartQueryHeuristic {\n async getRecommendedFacets(query: string): Promise<SmartFacet[]> {\n return [\n ...(await new QueryKeywordsHeuristic().getRecommendedFacets(query)),\n ...(await new WikidataEntityHeuristic().getRecommendedFacets(query)),\n ];\n }\n}\n"]}
1
+ {"version":3,"file":"smart-facet-heuristics.js","sourceRoot":"","sources":["../../../../src/collection-facets/smart-facets/smart-facet-heuristics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,OAAO,wBAAwB;IACnC,KAAK,CAAC,oBAAoB,CAAC,KAAa;QACtC,OAAO,IAAI,CAAC,MAAM,CAAC;YACjB,GAAG,CAAC,MAAM,IAAI,sBAAsB,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACnE,GAAG,CAAC,MAAM,IAAI,iBAAiB,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;SAC/D,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;OAOG;IACK,MAAM,CAAC,MAAoB;QACjC,IAAI,MAAM,GAAiB,CAAC,GAAG,MAAM,CAAC,CAAC;QACvC,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE;YAC7B,MAAM,GAAG,MAAM,CAAC,MAAM,CACpB,EAAE,CAAC,EAAE,CAAC,QAAQ,KAAK,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,CACzD,CAAC;SACH;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import { QueryKeywordsHeuristic } from './heuristics/query-keywords-heuristic';\nimport { WikidataHeuristic } from './heuristics/wikidata-heuristic';\nimport type { SmartFacet, SmartQueryHeuristic } from './models';\nimport { smartFacetEquals } from './smart-facet-equals';\n\nexport class SmartQueryHeuristicGroup implements SmartQueryHeuristic {\n async getRecommendedFacets(query: string): Promise<SmartFacet[]> {\n return this.dedupe([\n ...(await new QueryKeywordsHeuristic().getRecommendedFacets(query)),\n ...(await new WikidataHeuristic().getRecommendedFacets(query)),\n ]);\n }\n\n /**\n * Removes any duplicated smart facets from the given array.\n * Smart facets are equal if they have the same `label` and same\n * set of facet refs. Only the first occurrence of a given smart\n * facet is kept.\n * @param facets The array of smart facets to deduplicate\n * @returns A new array containing the deduplicated set of facets\n */\n private dedupe(facets: SmartFacet[]): SmartFacet[] {\n let result: SmartFacet[] = [...facets];\n for (const curFacet of facets) {\n result = result.filter(\n sf => curFacet === sf || !smartFacetEquals(curFacet, sf)\n );\n }\n return result;\n }\n}\n"]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "The Internet Archive Collection Browser.",
4
4
  "license": "AGPL-3.0-only",
5
5
  "author": "Internet Archive",
6
- "version": "2.7.2-alpha.0",
6
+ "version": "2.7.2-alpha.2",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
9
9
  "scripts": {
@@ -34,7 +34,7 @@
34
34
  "@internetarchive/search-service": "^1.4.0",
35
35
  "@internetarchive/shared-resize-observer": "^0.2.0",
36
36
  "@lit/localize": "^0.11.2",
37
- "dompurify": "^2.3.6",
37
+ "dompurify": "^2.5.7",
38
38
  "eslint-plugin-lit": "^1.6.1",
39
39
  "lit": "^2.2.2",
40
40
  "typescript-cookie": "^1.0.3"
@@ -4,7 +4,7 @@ import type {
4
4
  SmartFacet,
5
5
  } from '../models';
6
6
 
7
- // If wikidata says the query is an entity of type X, recommend facet Y, e.g.:
7
+ // If wikidata describes the top query result as X, recommend facet Y, e.g.:
8
8
  // X Y
9
9
  // written work mt:texts
10
10
  // film mt:movies
@@ -12,9 +12,13 @@ import type {
12
12
  // filmmaker mt:movies and creator:<query>
13
13
  // photographer mt:image and creator:<query>
14
14
  // visual artist mt:image and creator:<query>
15
- export class WikidataEntityHeuristic implements SmartQueryHeuristic {
15
+ // etc.
16
+ export class WikidataHeuristic implements SmartQueryHeuristic {
16
17
  private static readonly ENTITIES: KeywordFacetMap = {
18
+ 'written work': [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
17
19
  literature: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
20
+ book: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
21
+ novel: [{ facets: [{ facetType: 'mediatype', bucketKey: 'texts' }] }],
18
22
  filmmaker: [
19
23
  {
20
24
  label: 'Films by __QUERY',
@@ -110,9 +114,10 @@ export class WikidataEntityHeuristic implements SmartQueryHeuristic {
110
114
  const searchResults = await wikidataResponse.json();
111
115
 
112
116
  for (const [keyword, facets] of Object.entries(
113
- WikidataEntityHeuristic.ENTITIES
117
+ WikidataHeuristic.ENTITIES
114
118
  )) {
115
- if (searchResults.search[0]?.description.includes(keyword)) {
119
+ const keywordRegex = new RegExp('\\b' + keyword + '\\b');
120
+ if (keywordRegex.test(searchResults.search[0]?.description)) {
116
121
  const entityName = searchResults.search[0].label;
117
122
  recommendations.push(
118
123
  ...facets.map(
@@ -1,15 +1,15 @@
1
1
  import { css, html, LitElement, CSSResultGroup, nothing } from 'lit';
2
2
  import { customElement, property, query } from 'lit/decorators.js';
3
3
  import type { IaDropdown, optionInterface } from '@internetarchive/ia-dropdown';
4
- import type { FacetBucket, FacetOption } from '../../models';
4
+ import type { FacetRef, SmartFacet, SmartFacetEvent } from './models';
5
5
 
6
6
  @customElement('smart-facet-dropdown')
7
7
  export class SmartFacetDropdown extends LitElement {
8
- @property({ type: String }) facetType?: FacetOption;
8
+ @property({ type: Array }) facetInfo?: SmartFacet[];
9
9
 
10
- @property({ type: Array }) buckets?: FacetBucket[];
10
+ @property({ type: String }) labelPrefix?: string;
11
11
 
12
- @property({ type: Object }) activeBucket?: FacetBucket;
12
+ @property({ type: Object }) activeFacetRef?: FacetRef;
13
13
 
14
14
  @query('ia-dropdown') dropdown?: IaDropdown;
15
15
 
@@ -18,10 +18,10 @@ export class SmartFacetDropdown extends LitElement {
18
18
  //
19
19
 
20
20
  render() {
21
- if (!this.facetType || !this.buckets || !this.activeBucket) return nothing;
22
- if (this.buckets.length === 0) return nothing;
21
+ if (!this.facetInfo || !this.activeFacetRef) return nothing;
22
+ if (this.facetInfo.length === 0) return nothing;
23
23
 
24
- const displayText = this.activeBucket.displayText ?? this.activeBucket.key;
24
+ const displayText = this.activeFacetRef.displayText ?? this.activeFacetRef.bucketKey;
25
25
  if (!displayText) return nothing;
26
26
 
27
27
  return html`
@@ -37,7 +37,7 @@ export class SmartFacetDropdown extends LitElement {
37
37
  @optionSelected=${this.optionSelected}
38
38
  >
39
39
  <span class="dropdown-label" slot="dropdown-label"
40
- >${displayText}</span
40
+ >${this.labelPrefix ?? nothing} ${displayText}</span
41
41
  >
42
42
  </ia-dropdown>
43
43
  </div>
@@ -50,28 +50,48 @@ export class SmartFacetDropdown extends LitElement {
50
50
 
51
51
  private get dropdownOptions(): optionInterface[] {
52
52
  return (
53
- this.buckets?.map(bucket => ({
54
- id: bucket.key,
55
- label: bucket.displayText ?? bucket.key,
56
- })) ?? []
53
+ this.facetInfo?.map(smartFacet => {
54
+ const firstFacet = smartFacet.facets[0];
55
+ return {
56
+ id: firstFacet.bucketKey,
57
+ label: smartFacet.label ?? firstFacet.displayText ?? firstFacet.bucketKey,
58
+ };
59
+ }) ?? []
57
60
  );
58
61
  }
59
62
 
60
63
  private get activeDropdownOption(): optionInterface | undefined {
61
- if (!this.activeBucket) return undefined;
62
- return this.dropdownOptions.find(opt => opt.id === this.activeBucket?.key);
64
+ if (!this.activeFacetRef) return undefined;
65
+ return this.dropdownOptions.find(opt => opt.id === this.activeFacetRef?.bucketKey);
63
66
  }
64
67
 
65
68
  private optionSelected(e: CustomEvent<{ option: optionInterface }>): void {
69
+ if (!this.facetInfo || !this.activeFacetRef) return;
70
+
71
+ let selectedSmartFacet;
72
+ for (const smartFacet of this.facetInfo) {
73
+ const selectedRef = smartFacet.facets.find(b => b.bucketKey === e.detail.option.id);
74
+ if (selectedRef) {
75
+ this.activeFacetRef = selectedRef;
76
+ selectedSmartFacet = smartFacet;
77
+ }
78
+ }
79
+
80
+ if (!selectedSmartFacet) return;
81
+
66
82
  this.dispatchEvent(
67
- new CustomEvent('facetClick', {
83
+ new CustomEvent<SmartFacetEvent>('facetClick', {
68
84
  detail: {
69
- facetType: this.facetType,
70
- bucket: {
71
- ...this.buckets?.find(b => b.key === e.detail.option.id),
72
- state: 'selected',
73
- },
74
- negative: false,
85
+ smartFacet: selectedSmartFacet,
86
+ details: [{
87
+ facetType: this.activeFacetRef.facetType,
88
+ bucket: {
89
+ key: this.activeFacetRef.bucketKey,
90
+ count: 0,
91
+ state: 'selected',
92
+ },
93
+ negative: false,
94
+ }],
75
95
  },
76
96
  })
77
97
  );
@@ -0,0 +1,16 @@
1
+ import type { FacetRef, SmartFacet } from "./models";
2
+
3
+ function facetRefEquals(ref1?: FacetRef, ref2?: FacetRef): boolean {
4
+ if (ref1 === undefined) return ref2 !== undefined;
5
+ if (ref2 === undefined) return ref1 !== undefined;
6
+
7
+ return ref1.facetType === ref2.facetType &&
8
+ ref1.bucketKey === ref2.bucketKey &&
9
+ ref1.displayText === ref2.displayText;
10
+ }
11
+
12
+ export function smartFacetEquals(sf1: SmartFacet, sf2: SmartFacet) {
13
+ return sf1.label === sf2.label &&
14
+ sf1.facets.length === sf2.facets.length &&
15
+ sf1.facets.every((sf, i) => facetRefEquals(sf, sf2.facets[i]));
16
+ }
@@ -1,12 +1,31 @@
1
1
  import { QueryKeywordsHeuristic } from './heuristics/query-keywords-heuristic';
2
- import { WikidataEntityHeuristic } from './heuristics/wikidata-heuristic';
2
+ import { WikidataHeuristic } from './heuristics/wikidata-heuristic';
3
3
  import type { SmartFacet, SmartQueryHeuristic } from './models';
4
+ import { smartFacetEquals } from './smart-facet-equals';
4
5
 
5
6
  export class SmartQueryHeuristicGroup implements SmartQueryHeuristic {
6
7
  async getRecommendedFacets(query: string): Promise<SmartFacet[]> {
7
- return [
8
+ return this.dedupe([
8
9
  ...(await new QueryKeywordsHeuristic().getRecommendedFacets(query)),
9
- ...(await new WikidataEntityHeuristic().getRecommendedFacets(query)),
10
- ];
10
+ ...(await new WikidataHeuristic().getRecommendedFacets(query)),
11
+ ]);
12
+ }
13
+
14
+ /**
15
+ * Removes any duplicated smart facets from the given array.
16
+ * Smart facets are equal if they have the same `label` and same
17
+ * set of facet refs. Only the first occurrence of a given smart
18
+ * facet is kept.
19
+ * @param facets The array of smart facets to deduplicate
20
+ * @returns A new array containing the deduplicated set of facets
21
+ */
22
+ private dedupe(facets: SmartFacet[]): SmartFacet[] {
23
+ let result: SmartFacet[] = [...facets];
24
+ for (const curFacet of facets) {
25
+ result = result.filter(
26
+ sf => curFacet === sf || !smartFacetEquals(curFacet, sf)
27
+ );
28
+ }
29
+ return result;
11
30
  }
12
31
  }