@datawheel/bespoke 0.3.7 → 0.3.8

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/index.js CHANGED
@@ -3888,13 +3888,14 @@ function BespokeSearch({
3888
3888
  };
3889
3889
  const inputRef = useRef();
3890
3890
  const containerRef = useClickOutside(doClose);
3891
+ const searchLimit = autocompleteProps?.searchConfig?.searchLimit ?? 10;
3891
3892
  const doSearch = () => {
3892
3893
  setLoading(true);
3893
3894
  const params = {
3894
3895
  query: debouncedQuery,
3895
3896
  format: "profiles",
3896
3897
  locale,
3897
- limit: 10,
3898
+ limit: searchLimit,
3898
3899
  visible: true,
3899
3900
  includes: true,
3900
3901
  noImage: false,
@@ -3936,10 +3937,10 @@ function BespokeSearch({
3936
3937
  });
3937
3938
  };
3938
3939
  useEffect(() => {
3939
- if (initialized && debouncedQuery.length > 0) {
3940
+ if (initialized && debouncedQuery.length >= (autocompleteProps.searchConfig?.queryThreshold ?? 0)) {
3940
3941
  doSearch();
3941
3942
  }
3942
- }, [debouncedQuery]);
3943
+ }, [initialized, debouncedQuery, autocompleteProps.searchConfig?.queryThreshold]);
3943
3944
  useEffect(() => {
3944
3945
  cancel();
3945
3946
  if (inputRef.current && visible) {
@@ -3952,13 +3953,6 @@ function BespokeSearch({
3952
3953
  setInitialized(true);
3953
3954
  }
3954
3955
  }, [visible]);
3955
- const acConfig = {
3956
- placeholder: "Search",
3957
- size: "md",
3958
- icon: /* @__PURE__ */ jsx(IconSearch, {}),
3959
- ...autocompleteProps,
3960
- data: results
3961
- };
3962
3956
  const onItemSubmit = (item) => {
3963
3957
  setVisible(false);
3964
3958
  if (callback) {
@@ -3973,6 +3967,14 @@ function BespokeSearch({
3973
3967
  setQuery(searchTerm);
3974
3968
  }
3975
3969
  };
3970
+ const { searchConfig: _, ...finalAutocompleteProps } = autocompleteProps;
3971
+ const acConfig = {
3972
+ placeholder: "Search",
3973
+ size: "md",
3974
+ icon: /* @__PURE__ */ jsx(IconSearch, {}),
3975
+ ...finalAutocompleteProps,
3976
+ data: results
3977
+ };
3976
3978
  const autocompleteElement = /* @__PURE__ */ jsx(
3977
3979
  Autocomplete,
3978
3980
  {
@@ -3982,7 +3984,7 @@ function BespokeSearch({
3982
3984
  onChange,
3983
3985
  filter: () => true,
3984
3986
  disabled: (loading || redirecting) && !initialized,
3985
- limit: 10,
3987
+ limit: searchLimit,
3986
3988
  maxDropdownHeight: 250,
3987
3989
  rightSection: /* @__PURE__ */ jsx(Group, { spacing: 0, position: "right", children: loading ? /* @__PURE__ */ jsx(Loader, { size: 20 }) : /* @__PURE__ */ jsx(Fragment, {}) }),
3988
3990
  rightSectionWidth: 50,
package/dist/server.js CHANGED
@@ -1707,6 +1707,12 @@ function readMemberImageFactory(db) {
1707
1707
  }
1708
1708
  }
1709
1709
  }
1710
+ function sanitizeQuery(str) {
1711
+ if (!str)
1712
+ return "";
1713
+ return strip(str.toLowerCase(), " ").replace(/\s\s+/g, " ");
1714
+ }
1715
+ var sanitizeQuery_default = sanitizeQuery;
1710
1716
  lunrStemmer(lunr);
1711
1717
  var whitelist = [
1712
1718
  "ar",
@@ -1787,21 +1793,23 @@ async function newSearchIndex(db) {
1787
1793
  if (locale && locale !== "en")
1788
1794
  this.use(lunr[locale]);
1789
1795
  this.ref("content_id");
1790
- this.field("id");
1791
- this.field("name");
1792
- this.field("keywords");
1796
+ this.field("id", { boost: 4 });
1797
+ this.field("keywords", { boost: 3 });
1798
+ this.field("searchTerms", { boost: 2 });
1793
1799
  this.field("attributes");
1794
1800
  this.pipeline.reset();
1795
1801
  this.searchPipeline.reset();
1796
1802
  results.forEach((result) => {
1797
1803
  const content = result.contentByLocale.find((d) => d.locale === locale);
1798
1804
  if (content) {
1805
+ const searchTerms = sanitizeQuery_default(content.name);
1799
1806
  const payload = {
1800
1807
  id: result.id,
1801
1808
  content_id: result.content_id,
1802
1809
  name: content.name,
1803
1810
  keywords: content.keywords,
1804
- attributes: content.attributes
1811
+ attributes: content.attributes,
1812
+ searchTerms
1805
1813
  };
1806
1814
  this.add(payload, { boost: result.zvalue });
1807
1815
  }
@@ -1825,6 +1833,7 @@ function dbSearchMemberFactory(db) {
1825
1833
  async function searchMember2(params) {
1826
1834
  const searchIndexByLocale = await search_default2(db);
1827
1835
  let resultsIds = [];
1836
+ let resultsScores = [];
1828
1837
  const {
1829
1838
  query,
1830
1839
  locale,
@@ -1841,11 +1850,10 @@ function dbSearchMemberFactory(db) {
1841
1850
  direction = "ASC"
1842
1851
  } = params;
1843
1852
  if (query && query !== "" && searchIndexByLocale[locale]) {
1844
- const terms = query.split(" ").filter((d) => d.trim() !== "").map((d) => {
1845
- return `+${d}~2*`;
1846
- }).join(" ");
1853
+ const terms = sanitizeQuery_default(query).replace(/([A-z]{2,})/g, (txt) => `+${txt}`).replace(/(.)$/g, (txt) => `${txt}*`);
1847
1854
  const lunrResults = searchIndexByLocale[locale].index.search(terms);
1848
1855
  resultsIds = lunrResults.map((d) => parseInt(d.ref, 10));
1856
+ resultsScores = lunrResults.reduce((obj, d) => (obj[d.ref] = d.score, obj), {});
1849
1857
  }
1850
1858
  const whereClause = {
1851
1859
  ...visible ? { visible } : {},
@@ -1910,6 +1918,29 @@ function dbSearchMemberFactory(db) {
1910
1918
  });
1911
1919
  }
1912
1920
  const resultFormatter = format === "nested" ? nestedFormatter : plainFormatter;
1921
+ const formattedResults = results.map(resultFormatter);
1922
+ if (format !== "nested") {
1923
+ formattedResults.forEach((d) => {
1924
+ const { name, keywords, score = 0, zvalue } = d;
1925
+ if (name) {
1926
+ const cleanName = sanitizeQuery_default(name);
1927
+ let scoreMod = score;
1928
+ const diffMod = query.length / cleanName.length;
1929
+ const matchingStartChars = cleanName.split("").reduce((str, c, i) => {
1930
+ if (str === query.slice(0, i), query.charAt(i) === c)
1931
+ str += c;
1932
+ return str;
1933
+ }, "").length;
1934
+ if (cleanName === query || keywords && keywords.includes(query))
1935
+ scoreMod = 1e6;
1936
+ else if (cleanName.startsWith(query))
1937
+ scoreMod *= 20 * diffMod;
1938
+ else if (matchingStartChars)
1939
+ scoreMod *= matchingStartChars * diffMod;
1940
+ d.zvalue = scoreMod * 2 + zvalue;
1941
+ }
1942
+ });
1943
+ }
1913
1944
  return {
1914
1945
  meta: {
1915
1946
  origin: "lunr",
@@ -1917,7 +1948,7 @@ function dbSearchMemberFactory(db) {
1917
1948
  Object.entries(params).map((entry) => [entry[0], stripHTML(entry[1])])
1918
1949
  )
1919
1950
  },
1920
- results: results.map(resultFormatter)
1951
+ results: formattedResults
1921
1952
  };
1922
1953
  function nestedFormatter(item) {
1923
1954
  return item.toJSON();
@@ -1929,7 +1960,8 @@ function dbSearchMemberFactory(db) {
1929
1960
  locale: localizedItem.locale,
1930
1961
  name: localizedItem.name,
1931
1962
  attributes: localizedItem.attributes,
1932
- keywords: localizedItem.keywords
1963
+ keywords: localizedItem.keywords,
1964
+ score: resultsScores[item.content_id]
1933
1965
  };
1934
1966
  }
1935
1967
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datawheel/bespoke",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Content management system for creating automated data reports",
5
5
  "exports": {
6
6
  ".": {
@@ -73,7 +73,7 @@
73
73
  "d3plus-export": "^1.3.0",
74
74
  "d3plus-format": "^1.2.4",
75
75
  "d3plus-react": "^1.3.3",
76
- "d3plus-text": "^1.2.3",
76
+ "d3plus-text": "^1.2.4",
77
77
  "d3plus-viz": "^1.3.3",
78
78
  "dom-parser": "^0.1.6",
79
79
  "fast-glob": "^3.2.12",