npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

zim-fuzzysearch

v1.0.0

Published

Client side fuzzy search combined with seperate configurable result highlighting.

Downloads

71

Readme

ZIMFuzzySearch (+highlighting)

Short Description

Client side fuzzy search combined with seperate configurable result highlighting.

Files from src/lib are compiled to lib folder on root lvl (which is then published to npm)

Uses create-react-app setup for display demo application

published code on npmjs

Demos & Examples

Code Sandbox

Corema Fuzzy Search

DERLA Fuzzy Search

  • take a look at src/demo/index.tsx: via npm run start you may start the dev setup from create-react-app (but there need to be two changes made to the tsconfig.json) react-scripts will print necessary adaptions to console. (change baseUrl + noEmit to true)

Quickstart

  • create a component and copy code. (first install ;-) )
  • you also need bs4 + fontawesome as external dependencies.

// import component and optionally select type
import { ZIMFuzzySearch, ZIMSearchAble } from "zim-fuzzysearch"

const Demo = () => {
  return (
    <ZIMFuzzySearch.Comp
      data={[
        {
          label: "Receipe 01",
          value: "/link/to/category",
          //txt and tags are optional properties
          txt: "Nym weichsel vnd thu sy In ein haff",
          tags: ["suess", "bitter"]
        },
        {
          label: "Receipe02",
          value: "link/to/category",
          txt: "Tua rein wos longt und mer",
          tags: ["sauer", "bitter"]
        },
        {
          label: "Receipe03",
          value: "link/to/somewhere",
          txt:
            "An fisch tua sulzen rein in spegg, ayn ay dazua tue no drauf amou wist honig dran mach. mit schmalczen reib ein.",
          tags: ["sauer", "bitter"]
        }
      ]}
      // set to true will demand click on search button (fuzzy search is decoupled from highlight)
      // searchOnClick={true}
      // results will be renderedto bs4 card grid
      // showResultGrid={true}
      // etc.
    />
  )
}

More elaborate Demo


// import component and optionally select type
import { ZIMFuzzySearch, ZIMSearchAble } from "zim-fuzzysearch"

// call component and pass in data
const Demo = () => {
  return 
    (<ZIMFuzzySearch.Comp
      // optionally try with test data
      // as long as no data is defined -> will display loader 
      // data={testData.coremaData}
      data={[
        {
          label: "Receipe 01",
          value: "/link/to/category",
          //txt and tags are optional properties
          txt:"Nym weichsel vnd thu sy In ein haff",
          tags: ["suess", "bitter"],
        },
        {
          label:"Receipe02",
          value:"link/to/category",
          txt:"Tua rein wos longt und mer",
          tags:["sauer", "bitter"]
        },
        {
          label:"Receipe03",
          value:"link/to/somewhere",
          txt:"An fisch tua sulzen rein in spegg, ayn ay dazua tue no drauf amou wist honig dran mach. mit schmalczen reib ein.",
          tags: ["sauer", "bitter"]
        }
      ]}

      // tagConceptMap leads user to a link when clicked on tag badge 
      // not all tags have to be stated.
      // also activates the filtering for one specific tag
      tagConceptMap={{
        sauer: {url: "https://google.com"},
        bitter: {url: "https://google.com"},
        suess: {url: "https://google.com"},
        "This value doesn't exist": {url:"https://google.com"},
        "not assigned01": {url:"https://google.com"},
        "not assigned02": {url:"https://google.com"},
        "not assigned03": {url:"https://google.com"}
      }}

      // suggestions will be applied if given. 
      // allows to suggest interesting search values to the user without being required to select one.
      suggestions={{
        "weichsel": {label:"search-hint: Weichsel means cherry in German", value:"weichsel"},
        "sulzen": {label:"search-hint: Try to find most relevant receipes meantioning sulzen", value:"sulzen"},
        "spegg": {label:"Bacon", value:"spegg"}
      }}
      
      // specify labeling of result count
      resultCountLabel="Got results: "

      // starts fuse search on input change (with short delay) when set to false
      // set to true will demand click on search button (fuzzy search is decoupled from highlight)
      searchOnClick={true}

      // placeholder on input field 
      placeHolder={"Suche eingeben..."}

      // 
      onSelect={
        // what should happen when result link is clicked
        // callback gets passed in selected ZimSearchable object
        (selected) => console.log(selected)
      } 

      // define what should be happen when a tag is clicked
      // works only if the tagmap is defined with the specific value.
      onTagSelect = {
        (tagObj) => {
          console.log(tagObj);
        }
      }

      // display search alert via using the onSearch prop
      onSearch={(searchVal) => alert("Searching now for: " + searchVal + " (this message may be customized)")}

    />)
}

External Dependencies

  • Component has bootstrap4 and font-awesome6 classnames assigned BUT NOT bundled into. Apply external dependencies if needed.

Advanced


// ... return for rendering: 
// (how to import see quickstart)

    <ZIMFuzzySearch.Comp 
      data={testData.coremaData}
      fuseOptions={{
        keys: ["label","tags"]        // keys is required! adjusts where fuse.js should look for data to index
                               // other properties are directly from fuse.js
                               // defaults against label, txt and tags properties. 
        //ignoreLocation: true, 
        //ignoreFieldNorm: true,
        //includeScore: false,
      }}

      // maps given fuseOption.keys to tab-view below input.
      // KEY array MUST HAVE same values and properties below
      fuseKeyMap= {
        {
          label: {label:"Titel"}, 
          tags: {label:"Schlagwörter"}
        }
      }

      // options to adjust mark.js behavior
      markOptions={{
        accuracy: "exactly",        // complementary will mark whole word 
        exclude: ["h5"],            // exclude elems in result from highlighting 
      }}

      // optionally display result as bootstrap grid
      showResultGrid={true}
      
    />

Custom GUI

  • see demo in src/demo/CustomGUI

import React from "react";

// import from "zim-fuzzysearch" -- when installed via npm install 
import { ZIMFuzzySearch } from "../../lib"

/**
 * Example for how to build a CustomGui on top of the 
 * fuzzy search, using the fuzzyResult and fuzzyInput hook.
 */
const CustomGui: React.FC = () => {
  // result of fuzzy search
  const { fuzzyResult } = ZIMFuzzySearch.hooks.useFuzzyResult();

  // handleInput will init fuzzySearch. In inputVal is the inputVal stored. 
  const { handleInput, inputVal } = ZIMFuzzySearch.hooks.useFuzzyInput();

  return (
    <>
      <input
        value={inputVal}
        // init search on input of component
        onChange={(evt) => handleInput(evt.currentTarget.value)}
        type="text"
      ></input>
      {fuzzyResult
        ? Object.keys(fuzzyResult).map((key) => <p key={key}>{key}</p>)
        : null}
    </>
  );
};

export default CustomGui;

Fetch Data and use in Fuzzy Search

  • see Demo04 ind src/demo

  // fetch data in component and assign to state
  const [fetchResult, setFetchResult] =  React.useState<any | null>(null);
  React.useEffect(() => {
    if(fetchResult)return;
    fetch(
      "DEMO_DATA.json" //add entire url from server to file here
    ).then((resp) => {
      resp.json().then((data) => {
        setFetchResult(data);
      });
    });
  }, [fetchResult]);

  // assign fetched data to fuzzy result component
  return (
    <ZIMFuzzySearch.Comp 
      // fetchResult can be null or undefined -> shows loader by default.
      data={fetchResult}
      fuseOptions={{
        keys: ["txt"] 
      }}
      markOptions={{
        accuracy: "complementary",        
        exclude: ["h5"],  
        wildcards: "enabled"         
      }}
    />
  )

Two-way binding


// returned React component

return <ZIMFuzzySearch.Comp
      data={fetchResult}
      searchInput="Search value given as prop"
      placeHolder="Etwas suchen..."
      searchOnClick={true}
      
      // use onSearch to get search input outside 
      onSearch={(searchVal) => alert("Search for: " + searchVal)}

      //use onInput to get search input outside 
      //onInput={(val) => alert("Input: " + val)}
    ></ZIMFuzzySearch.Comp>

Example for changing mark.js highlighting reactively



  // ... additional component code
  // adapt highlighting to selected fuseKeyMap (without fuseKeymap won't work!)
  const [markOps, setMarkops] = React.useState<Object>({
      element:"b",
      accuracy: "complementary",
      exclude: []
    });

  return 
      <ZIMFuzzySearch.Comp
        data={testData.coremaData}
        // mark options controled via state to allow control
        markOptions={markOps}
        // tell fuse which keys to listen to
        fuseOptions={{
          keys: ["label","tags", "txt"]
        }}
        // maps given fuseOption.keys to tab-view below input.
        // KEY array MUST HAVE same values and properties below
        fuseKeyMap= {
          {
            label: {label:"Titel"}, 
            tags: {label:"Schlagwörter"},
            txt: {label:"Volltext"}
          }
        }

        // set all label
        fuseKeyAllLabel={"Alle Felder"}
        
        // change mark options onFuseKeySelect
        onFuseKeySelect={(fuseKeys) => {
          let markExclude = [];
          if(fuseKeys.length === 1){
            // onFuseKey select gets array of selected fuseKeys as parameter.
            // exclude different elements 
            if(fuseKeys.includes("tags"))markExclude = [".zim-fuzzy--result-txt", ".zim-fuzzy--result-heading", ".zim-fuzzy--result-crumb"]
            if(fuseKeys.includes("label"))markExclude = [".zim-fuzzy--result-txt", "a", ".zim-fuzzy--result-tag", ".zim-fuzzy--result-crumb"]
            if(fuseKeys.includes("txt"))markExclude = [".zim-fuzzy--result-tag", ".zim-fuzzy--result-heading", ".zim-fuzzy--result-crumb"]
          } else {
            markExclude = []
          }
          let markCopy = JSON.parse(JSON.stringify(markOps));
          markCopy.exclude = markExclude;
          setMarkops(markCopy);
        }}
    ></ZIMFuzzySearch.Comp>

External styling via css classes

Provided classes for external styling.

  • Main container: zim-fuzzy--main

  • Input form: zim-fuzzy--input-form

  • Input group: zim-fuzzy--input-group

  • Every result in a div: zim-fuzzy--result-div

  • Result container: zim-fuzzy--result-main

  • Pagination bar: zim-fuzzy--result-pagination

  • Individual result text: zim-fuzzy--result-txt

  • Individual result heading: zim-fuzzy--result-heading

  • Individual result bread crumb: zim-fuzzy--result-crumb

  • Individual result tag: zim-fuzzy--result-tag

Assigned classes according to Fuse.js scoring

  • you may use this classes
  • score must be provided by fuse.js options!

.zim-fuzzy-score-highest .zim-fuzzy-score-higher .zim-fuzzy-score-high .zim-fuzzy-score-medium .zim-fuzzy-score-low .zim-fuzzy-score-lowest

Manipulate css classes according to fuse.js scoring

  <ZIMFuzzySearch.Comp
    data={testData.coremaData}
    // e.g. assign method to calculate different css classes according 
    // to calculated fuse.js score
    // then use css to style the result
    // the css classname will be assigned to the css result-div 
    onFuseScoreToCssClass={(score) => {
      if (score < .1){
        return "bg-success"
      } else {
        return "bg-danger"
      }
    }}
  ></ZIMFuzzySearch.Comp>

Contribute

Useful Resources

  • https://medium.com/@jchiam/publishing-a-typescript-react-component-to-npm-d3cc15b8d0a2
  • https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c

Create react app and useful stuff:

  • https://github.com/wmonk/create-react-app-typescript/blob/master/template/README.md#publishing-components-to-npm

Important Scripts

# locally build publishable lib folder
# this folder will be published by npm publish later on
npm run npm:build

# login to npm before
npm login

# apply new version
npm version x.y.z

# then publish to npmjs 
npm publish