{"_id":"refinerdb-react","_rev":"29-e539655ac1ec9b6ac42b02b7b74d8b15","name":"refinerdb-react","dist-tags":{"latest":"2.0.2","next":"2.0.0"},"versions":{"0.2.0":{"name":"refinerdb-react","version":"0.2.0","main":"lib/index.js","scripts":{"start":"npm run watch","build:app":"react-scripts build","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","watch":"tsc --build tsconfig.lib.json --watch"},"dependencies":{"react":"^16.12.0","react-dom":"^16.12.0","react-inspector":"^4.0.0","refinerdb":"^0.1.0"},"devDependencies":{"@testing-library/jest-dom":"^4.2.4","@testing-library/react":"^9.3.2","@testing-library/user-event":"^7.1.2","@types/jest":"^24.0.0","@types/node":"^12.0.0","@types/react":"^16.9.0","@types/react-dom":"^16.9.0","react-scripts":"3.3.0","rimraf":"^3.0.0","threads-plugin":"^1.2.0","typescript":"~3.7.2"},"eslintConfig":{"extends":"react-app"},"browserslist":{"production":[">0.2%","not dead","not op_mini all"],"development":["last 1 chrome version","last 1 firefox version","last 1 safari version"]},"description":"This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).","_id":"refinerdb-react@0.2.0","_nodeVersion":"8.17.0","_npmVersion":"6.9.2","dist":{"integrity":"sha512-a+2Jv+mOLPzga2s5EheiU4KAGN1VU15knGA4HBLIR1PYjAshidcqiULGogb6JTd0aIEsvSuGnsn1/6KvqKL6Ng==","shasum":"3617f1d79131784f64b678bb51519072714bcddf","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-0.2.0.tgz","fileCount":90,"unpackedSize":345662,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeJ3d8CRA9TVsSAnZWagAAWSUQAKRUDJC6ScyaTOdoVYtf\n+dHSBuWgfDr8BTs3XDDQ6guFJn2u4WCkyysADO7sGoolkv7jbMxag4sp5j59\nFl+DO4k6OBvMpZM/BKg0Y9D7Rt0K/IZ3kaq78xiVazvbfrasreC9z+69OELj\nV+WeOL7xMJ58TUpPmLhSUrFq+jKHcLnAOMqf5f8egn+EhGL1DgP386bIojA9\nGqI5QJUfzlSjzUcvOdR3F0v96EqN22nrI6ka+njRKUmAbWujoHHBGHp0/2jP\nehvr6GtQvX7ZS3Pw6XRVAexpqMZDsgtGGjdDwl40kF5DUEAq6y1glZtLTAYB\nLA2uzDFpA1d4ZbWEwcNX2lBZv/2XPhrxQc77xO8XqGc/6dUvonOjmJG6UlGm\nDQLFGX07woIhMxIQdZJYrJMo/hHr/ecnu8OCFBGYLFpjhMEysfMjNkRlp2T8\nPuMvigximq+wGeWPNThcnfvtOT/fd5WltwuZhhZjDaRBVf4Z9wTcuG97oHKY\nwiAl9DwH12sn96x8OgmjOCeXliYKnoFI5RZIJenI1xrYOXOlIYs4Mth29uTB\nuNVxfeXIbtixH9JFaWiGbBmQhW12tTMDj1lcvTwI6cXaibOCa725viY7HxMB\nLJUvvQVXAHkOO/ClkGZ2pheaomYvoTt04KcT+bQXpq+SfDiHifNa77+XDTXd\nxn9s\r\n=jrIm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDskYtYD0hAkxYyeZkXWsHHesPLbPobjOgGM++lorS+ZQIgVhbMCPmRHJ9/gHzKc5QKAcp9F6qVDy6H83GBgwUOStc="}]},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_0.2.0_1579644795996_0.3596613791194285"},"_hasShrinkwrap":false},"1.0.2":{"name":"refinerdb-react","version":"1.0.2","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm publish --tag beta"},"dependencies":{"just-omit":"^1.4.1","react-inspector":"^5.1.1","refinerdb":"^1.0.1-2"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"readme":"# RefinerDB - React\n\n## Components\n\n### `RefinerDBProvider`\n\n## Hooks\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### `useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n### `useIndexState()`\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### `useQueryResult()`\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n### `useCriteria()`\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### `useRefinerDB()`\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n","readmeFilename":"README.md","gitHead":"73719bc3d08400ce31b60fa4ac1b6191ca731c5d","description":"## Components","_id":"refinerdb-react@1.0.2","_nodeVersion":"16.9.0","_npmVersion":"7.21.1","dist":{"integrity":"sha512-eDXyxPV8Y/oNFlyvdi9ijNawHVwaaP44vzbo4hVac5dcgkSnbZdaX94Lu7fIOSLZDTxYDtqlzm0WHuuVOzKAbA==","shasum":"86a45a56f1a40eafe9abff63b2d93dec53df6873","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.0.2.tgz","fileCount":60,"unpackedSize":1338386,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDakv0Hy1Y7p08+1h9+dNkyD5PQqnRBUe8Rh+TX+JTmNAIhAIG8pXZwbOz+dSSPbwvZdxwVREJiNlPBy5sYEuVK3Kiv"}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.0.2_1636050261514_0.7014150386908706"},"_hasShrinkwrap":false},"1.1.0-0":{"name":"refinerdb-react","version":"1.1.0-0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-omit":"^1.4.1","refinerdb":"^1.1.0-0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"747e38f4f976c2c011970569a49670257048f986","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinderdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.0-0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-d2FOJtuEFy57gBxm0WTY5samXBYR197XuJ9E7vWFjpFhxPsdig/7F1drXW0U75JI5ekDG8gJaVO+yxuUKAvEnA==","shasum":"55987da352ed870392d87fc1ec3a9b0942a3ddc4","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.0-0.tgz","fileCount":91,"unpackedSize":2662847,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCi4lAgDP+SoU59wQXLy+XpqKftlwqK4ERpbgFzPYiI1AIgdls+SPS8l63orcCmIOugx26AVU3HM67d7qMFeLxaAlU="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.0-0_1636225418767_0.0769934588509682"},"_hasShrinkwrap":false},"1.1.1-alpha.0":{"name":"refinerdb-react","version":"1.1.1-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-omit":"^1.4.1","refinerdb":"^1.1.1-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"69079860a49383b6ca8066a60d6b69a79ec70e39","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinderdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.1-alpha.0","_nodeVersion":"16.9.0","_npmVersion":"lerna/4.0.0/node@v16.9.0+arm64 (darwin)","dist":{"integrity":"sha512-PAv+86vHBJoB1Ie4B2l6ghHCnXxOkxnll7j3tuEWRZKr5sKtf1tyAlJ+GyAP6kNHmkaGjtQ9bwW9B3bu1czH+w==","shasum":"14b8e9e4196c92d0351767a2e5554e031c68ea98","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.1-alpha.0.tgz","fileCount":75,"unpackedSize":1355666,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCN/g4WqrBF98E7qPO7lWvMgzZcP0phSFrn4bCpTCKyJwIgd4YqC4e8FJWIJznIru+dtpqQXV+ZyyT9c2q+3nD2ZuE="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.1-alpha.0_1636234646372_0.8173286180208683"},"_hasShrinkwrap":false},"1.1.3-alpha.0":{"name":"refinerdb-react","version":"1.1.3-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-omit":"^1.4.1","refinerdb":"^1.1.3-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"0002f02bb2ff87efc51357e4f4a7ee2deceb029f","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinderdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.3-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-JJ/aeeF5GTWsQ3CeC/WxTewEfkpuZEo+pTADSocNiJCrcUIbpxDH181YVqEvrrRboCcmkNoBmOvRDw8XvFToLw==","shasum":"d552333154c34edb05172fe4cf2456e3532dbaa9","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.3-alpha.0.tgz","fileCount":75,"unpackedSize":1355666,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQWHcbm4V8stwPvgPQ3M9lYGTxzr70NQEyfNePF4WDwwIhAPdUiLHT6BXZnKARtg3glUgtYMtbeUWTfeM42ajPOpcx"}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.3-alpha.0_1636243662907_0.08727677949579271"},"_hasShrinkwrap":false},"1.1.4-alpha.0":{"name":"refinerdb-react","version":"1.1.4-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"refinerdb":"^1.1.4-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4","worker-loader":"^3.0.8"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"75bffe2a40d987de7d819f2b3c099c5bb3b1d204","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinderdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.4-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-A/LOt2DYtwa08QDcg67PmstQ+1JanPQChRZfc+QTmMld3dU7aYxnnF96yKF+grUxv5tzJryYSqbf2ui7dcXOpw==","shasum":"cd1ceefaaa454bb1aa5952a489865e73440a8adc","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.4-alpha.0.tgz","fileCount":75,"unpackedSize":1359055,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFBgDyInmVwd0I772cC4KwKUTzzuBEvtcwhPir2lgQXhAiBvtxLoCOi8eWNYU5Xb4q4BwzdareY+sdOin9LgZwDZkw=="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.4-alpha.0_1636326253191_0.3395108481209135"},"_hasShrinkwrap":false},"1.1.5-alpha.0":{"name":"refinerdb-react","version":"1.1.5-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.5-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"72f582abbd6a85a717de03a630bac8688c8aa2f9","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinderdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.5-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-tI1OLdXFTe3T8qtep7jS+jOBCbi/iJ2kL+mRrOysw1wtNR3KAoFEfeTfe4E4WbQa6yrY6FJ1RAETIINN8oaVeg==","shasum":"48fd9fdd107759f8bca45a7fdc25e20b34636374","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.5-alpha.0.tgz","fileCount":131,"unpackedSize":3137711,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDx2biOZvCZPGwwBn8Ylgt2RVuts1PjmeSVHLMoAD6eHAIhALhbJdyCrsNeUt8YQh0tbEcTpy04kCXRNKPa0Sdw0OsT"}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.5-alpha.0_1636681309493_0.6320258873372608"},"_hasShrinkwrap":false},"1.1.6-alpha.0":{"name":"refinerdb-react","version":"1.1.6-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.6-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"199a76f7f949c1d9ac7b539e3927b793b14abbbf","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinderdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.6-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-7ApUSpFndWaseOpKK0fxELJAR+DUUc+Wv4lZHe1ummB25LW5OIoWrLFTIijxd4NP5INa+75hwUVTZlaE2FS/VA==","shasum":"9cd6a441f512858438b71617781e5aaa0bbf8dd3","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.6-alpha.0.tgz","fileCount":131,"unpackedSize":3137619,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwz2ZD61kprVWB3xmnNt7ZIGcQr6GViKao9aajZdSuQAIgPMnzi/NkqgNhddfRvEHH8EWaqFMamlldmjvuntIqWFQ="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.6-alpha.0_1636689343140_0.029506619559442004"},"_hasShrinkwrap":false},"1.1.7-alpha.0":{"name":"refinerdb-react","version":"1.1.7-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.7-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"91c8c03c73404a18c55525c3f17ae07b2c276a8e","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinderdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.7-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-Ya5NRqMOU6oiIisnwO83JTFOd9OZZJnRH2mYCJypFO2hgxSxUpkS1jbycOvAdBSYMs1HIdcJLKG6AKppvjyrlA==","shasum":"32873a1c2f2980737309a921782b6cfb34242de8","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.7-alpha.0.tgz","fileCount":131,"unpackedSize":3137621,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDTSlrrkLhmJhSqdFJUlTy/+JflPgKA+I8sP61JqFJzjgIgE04YtyYYAj9mc2/nnAvVnUwYV/yZF8THcz5bjgrDRAg="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.7-alpha.0_1636764308518_0.0978223599042134"},"_hasShrinkwrap":false},"1.1.8-alpha.0":{"name":"refinerdb-react","version":"1.1.8-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.8-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"753a00c6a2134ef7629515a1322f3c8a59797065","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.8-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-CDiug5RG9mZjdQxDE4AC731yHOjWqBJFQ6+zXj1D5ZGckDW00I8zT7c9htwtBISrwTCKKk4xlrU8PcTnDadtKg==","shasum":"a8435acc9ba48ceefeffc5f441a4fc440c29824b","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.8-alpha.0.tgz","fileCount":131,"unpackedSize":3137620,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhnzrxCRA9TVsSAnZWagAA+9QP/je0h7xAyF2XaBbxeWC3\nOCXVMkKHvfXtnd3ZYWCqqnQoHKi1SBX5Oog4jgr+RthJNKV0KXLqpIBTxPCE\nATA8lomTQ16dAM+umNWE0O9xMtzbMOKFVtbGYQNnSJniRShyt6RObL+wTfe0\nj1BVaAwkZm0dnUof65SCXxyX8QQPNGoiZY4N7ej2sZnFbYJyrJN0guyccVrc\nZbnYKAxOx3J2FszKC3DJt3Ht74OEHJOllDl3ah6C9i7GPQFcQ9njuoIx1Mzs\ninbh7cuZqvZse910z+za46EdhPHLDN9ubBOZ0V84ZHmnQQwWdf2/wdfkbUYg\n1LIXX37XoAXl2WjaBDrcyD0u+uhX61qCpZFmv0IocKWFCMRsI1HUolkwCiEN\nCtGmCrS1R2azQK2O+zW/+GFPmBtLSUQ/893SmTKSdMEgFdrx41gQRM/AifYs\nh/5btDZ1QBo9NI7QBgk1tCJbljX3fldms6t5vzOEEesFs+4l3hKoSW8tLxJX\nTw8L2dZLpd4kZACRlK+WU688wBKqDC9sUF3PfXmkM33E5AhZBWcyMKypyuFR\nP8QvOxFhNTkqcS+ERpC+rE3VrrZqvVoqR+xwL3tYz/8ic1MR8hWD0NPlBkeP\nbLo/VYz6JINQ2qAKF0gx9eL5Dr39wRM4eI2pK2V3EuZ8m7gbdJ+RsY3cZ8+/\n2maS\r\n=1JGG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJyZryVfQF/gOyIC9RA7hvmuphLhIDGLBb5XRXKzyV9gIhAPfXNrXaWX3XJi3P53R31U4KrbdWhPLRlEuGCC3WhM2w"}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.8-alpha.0_1637825265689_0.79123013000827"},"_hasShrinkwrap":false},"1.1.9-alpha.0":{"name":"refinerdb-react","version":"1.1.9-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build-storybook":"build-storybook","prerelease":"npm run build && npm version patch","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.9-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"9c1963a37e7bf4a0ae8b20c78ae1dbd71bc75309","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.9-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-kA1i1cqWEyBNdUMwrbKHUTCwi0sO3y5lc5kzfb6YpkuD+dsAvka1PE87uE7UHpJDiLknatBm8waO/Ua5l5l3lw==","shasum":"e40661079d7c41ad674b2a41cc9e7a6c4fa0712d","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.9-alpha.0.tgz","fileCount":131,"unpackedSize":3136318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJho73JCRA9TVsSAnZWagAAFEAQAIHNlFsLYrql7EMi0oAq\nilYjZDAS35vk28pdlIU7hoXHY9jmd8f51NwYiwdOsNsViDJp/Up8TDrxwU0M\nj4j7fo1bRA/ICqbQmPOIfGqrdSaqOIOLxigFe9jIK62CxJwSyZCkLQC3nXwX\n9hVzbsjtep8Ysw3Sa654/8IX2mr6K/H/nsq97j2BYd/ksJM92TLndN+ug6s+\ntsjs2sPcE+0yHi28Cg8E3AXr6WWlN2Xz+yVKOzJk+mYwXPPGSRH66/woY5X8\nj0tG8Z+lpEj/sMvuh6OvoFSmVMiyfgP6kOX9zAZbJdSpfYSTkQSgarF9pjPg\nZevwmd0qsv8ti6OF7IsxEKbSjYW8uOBJ7chYXkbxUtYWzBeZkVzrC6AP3FPR\nqsw/jq9KdezkuJvnGQekl5tlatzBe+tlO9P60yb3GhBGWuhE+VG4gRvf1Ckz\nWaXlSK72bwCnV3mzR6EjXN299sjCh1c7rrWWaRTGOM8IxSoKnZr4brHvuP1v\n94YrpcosNhNxbQ2x0X/NaeJuljfp+a8qn1i3RnSkaC1qlSz9FGbZqBYt9y88\nOFS/jVyOL9kJo39HnCRNvXt1nU0+qF0yQVKkZnlp7Fqjtfw+0vswKCX4B6xK\nhQi3YyzMDiezKJFgTBGQIzXE10kUs7km80uKxhB/X0adZBttm5uo2vptOd5N\nax9+\r\n=FAjI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGKYbS7AI4W26LAaNF+5Fy5Eele/Y885mdxa4yFXgfF8AiEAgWxBkpxRCYH7UCAILEXoF3c/w2KzwmkloSipD3TWnJ4="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.9-alpha.0_1638120904687_0.18769182010432295"},"_hasShrinkwrap":false},"1.1.10-alpha.0":{"name":"refinerdb-react","version":"1.1.10-alpha.0","main":"lib/index.js","scripts":{"start":"npm run storybook","build":"tsc --build tsconfig.lib.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.10-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"209911c0500c103d3a9ab95152c1632a42464790","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.10-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-1FsvucHSZF6OiXeL8IS9E+FTlfStntQh8fP9SQDbyohNGePhj4PkuC7iwHoNczV04Bpg2Y7u7jJuHzBIFxVp7g==","shasum":"0ee998bb8a082b58475f566398aa68e7039419bc","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.10-alpha.0.tgz","fileCount":131,"unpackedSize":3140585,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhslWKCRA9TVsSAnZWagAADisQAJrzVsD9fe8yrdb1D6Mi\nxOgcQC82Z5MdlaNelQJkuKUTJUgGAVN2lODc98GML12lj7DMJSNgjnNFgPjf\nqQKi7Twky/9UtqDDhPkch3pydyerR1xRIQFVvaPZ+v7JfmO/TABqdq9SJdEz\nWeI3J5lUZRbdarDnTRa0sSsUavfEOoOWuUgdZdGBidlcfO9JHwYN9/q+XuoS\nbrnjQ8U/87Sil5BHrH5PMxBjW72rvzQwrObnF5XumFZzkgvKWpTSLTKSVDip\nB6hECV0dUxW0yWFKwSTgOQaX+UU6nYdRzji+/o09zRSPAG6ccTJg9HDOt5UF\n3lYUke0lNPYPzuGxbcdWYdXJcbc4u2uD1wo1DfQX4eSOAvrFgSUe2qNAlOUE\ncXWAFnKt4cVEQKL8957BU8GhpC3PC0R+H0dPte+HF/oIsoD4YESjULVjUItE\nTRsI+P++0G7j5ZDkcuHzaBH+7BJs4rSKH7fAKYKvmEAYe8053+bOOAxJI8sz\nK5hbKaXrLszJ54YPp8Rj2UXJ+fbqEUSpfz78Pz4Hkl31MBX7rqBS3v1j/Ixl\nEynPu7lkHFihJEiUHHAP//ML8OMItSM3245aA4EvvqH9XJPp2ysyyACP0vlT\nOi0Y1a5WRVuXP/J2zgocIufEvh2tDD+n56Ng3ZvtLTyhg7F5FlDbdkWWe0q5\nk/BF\r\n=mdYn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCSswAZLuyPPWBV7PCMfqnk1Oy+anhimbI0JfkPJmnaVgIgG6b6+ZBAmOviuJl3wK2y54nQ1BMfKLtcvgR5wARTC74="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.10-alpha.0_1639077258561_0.8208754634354276"},"_hasShrinkwrap":false},"1.1.11-alpha.0":{"name":"refinerdb-react","version":"1.1.11-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.10-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"cc39d7cc7c7299b84969395c883de89833df1635","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.11-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-5kwbJe8z3fakEZm418jQUTDa+ExB5/X0896Sb7n7uOs91hFxAgl5YKvT3OrHm4dWUTkZRWik72MIQ6dChbub9w==","shasum":"bcfb50c5fe43af620183e30e2ff6ca987614a7a0","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.11-alpha.0.tgz","fileCount":3,"unpackedSize":10644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhslcdCRA9TVsSAnZWagAAqHcP/A2T/f5cPeNcJ+DkNvE+\nIx+FNKRNt9tK3Glujww+c2A37sATRTBzDQRNpGwCpAHJ6hp1Q1e77nrAVfuU\nV/owXuVmuZHSmjbKnIAnMO2VNi78RZVILcWO18ev84jiNquzIFvXnzEfbZG3\nem6s/k3v3wiWun9nWMwTGmfWlUrZcwwKFQRlxjVkBx9H5h4gYFj4+9H4ACVf\n9zbB47nUDXEPA9gQh41cXw0j2wxFXPg4EnWLFDRP7vIOBqyS+3dizfDonT3T\nDzLoSFKP+v7/LBykd+MMrODjO0y+87zgkic14eOFy4hZwfuXVzV6bpVwq3Ly\n32eCJ2irFyZTArJf1R1P/KToHuWbHryCwR1FoD82aeEAEUENLF7F5IYhUFIn\nxLR8onHqZ96DLuwp0dsM6LzQTWEXwZALpqZB3ijLM2YKFrSz7cC4zrs42QU9\n4YEIpVmkh9e71udtcF8Jzzdt8Q75V+x37a7moPsVtiDWF0GkEL12zZPQjKJR\nrjdcRqIMx431VB8/p+8KSvU0fYi7PkTinWuIwmyJ0VeQHcqu6QeT/slbL6NE\n0XIxmpl1gYiL+QwoM9ht7wum7BvwoIc6BLRZJvdqTCv8Tnxku7Fk2+7J2zdt\nZ6DZ4+yYTsvysT6F4c6LNyu80TjiPWKsZ/XFCidUj8bOj/CIFUCEtRPjKe3P\nfDRX\r\n=kjvM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkjPbNcmlMRxzhRu+67sJDpqXklMyn5+8YYFIuIScEQwIgVKakXQYeGaiVeOmMtXtMIsMlG8lnCIo1Sq71+GtJxso="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.11-alpha.0_1639077661179_0.38526805184116375"},"_hasShrinkwrap":false},"1.1.12-alpha.0":{"name":"refinerdb-react","version":"1.1.12-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.10-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"8457a52ca465ac872cdb838a39e4938365819fe1","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.12-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-vKlxSyOmEBslsYdsPhZAgeEDUdGSHudskZZorgLwVWPhKuKFTx+XUDL4HltmMzAiWj/F4yXzOJ4TdklC2Lj7xw==","shasum":"c9ff9a113a0fa5b3552bfcb5c467e9a9ed844d1e","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.12-alpha.0.tgz","fileCount":173,"unpackedSize":3664962,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhslhJCRA9TVsSAnZWagAA9swQAIia8vi4UGZcIZvz0fdk\nBGxTDfjc0zenA1P7+7WUEZkTBpzs3hiZXETOr/FYU5RuUArIcgwo8sq+QqHa\n8aklYRG3KpgJU2CTZPERXEA9R1SDU+pQZEnddiQsbas0Sx+R3sViu+adCRyy\nnJYzMEQzzOsruVwYzEFZdX7vuKUymdflQ80E3AD5Q32BugBLcE09C+3M67X2\n0kwWM0ygu6CrPokIqtQM/NHr9hRoxP6OrlCM2rFCc92GLEDbU3/enYjdMkh6\nCffcLOPWB8CQC/aa/qkubXO2BYEluJbqb/qKfjwvqpfEGHezUTgfTyLgOw4V\nn9RfjaiPtlXLza/dADkb+PhPLNDAsXIvnN1yPeHTFYTE7O0b8604j2dJMcOS\n06uaXGF4tMCgmQJFcuw9H3uvE864vT9gvUNcQpNUHMzyujJ7VpCeZ0ZiNKb2\nY8EHa8W8+wdruuGW/rRADqKredw3n8uTQpFUhtUk81d0TB/SU+slPmk+cELW\n+7+9zj89JhFSYUjrrlxLMhH4VQaUqd8qopsYhsvmDnQ6wRMOz+yeeQyxsPpL\nwyow09iu896d7DIPX/2L6FXdyHlty3hpnp46V4EyTTfz3dsl2AofR2pmDg9Z\nUTZvO4VIj5ZaPTU/e3TcXGaOcJiNEffHPGxCFQiaJBFIq/6J/KScE4PXZD1g\nTQId\r\n=ccgB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCNNwH4hhN/fDEF7MeKZ3/TTUe8F7PpGZLtbRJTlajRgAIgAaY62JKG0ZrYWTljXteSXAgw3unsqlEkhf9thEwlFfI="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.12-alpha.0_1639077961553_0.28587877939612416"},"_hasShrinkwrap":false},"1.1.13-alpha.0":{"name":"refinerdb-react","version":"1.1.13-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.13-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"48aaad22510e3e64bcfbbc4190e983aa3e2865f9","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexState\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nlet { status } = useIndexState();\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.13-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-K+WDpeaBXzTL4wGDVEbgxbBQY9sHzNBzVl4VAHcHaAYIYlxR/0SGMPgYeZyqoCW/EWXeEWDatnNzz4GqauEL6w==","shasum":"d5a7fb07d06d9bed13ea57a11c225d517662547c","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.13-alpha.0.tgz","fileCount":173,"unpackedSize":3666273,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJht71xCRA9TVsSAnZWagAAY9QP/3+wRLwPhnOpbrO93Fwj\ns7batYa60z4bnAbNYjDntQeCHR9x5wbWk8ey/3HHdjClBC/RPggaR5cHtDwU\ncB6Z2y+C1UolM+VkEUv7cJI6VU6MxBgu/QQgem58ADRhVcmJrFn2bafeEFVG\nk2XMf3S7PzHyBbH22Ny7iE6lWqQ3pA+KgjDtg4C6/rgz8zIQF/cKD16UxC7F\nUVJt2i1tvq58zTgl4NjlSYP5MvzP9sA9sOKmrR2SzdzkLMNP0Om6I9J8kGO3\nN4tT+oMEtisKflBrQ71yFIYPmU7pC63OnEEeZz+lzP8S0svac1xm8bUXpk9d\nGRUercCE/DD6Ps1rTFdVUzqlPervCWmv8nS49Ss3FNlEkgpzyaUF/7WOVSq2\nmbUhmhIZccEHf6unLtAH23TIYfkS6MqmAcoX+Aox1l+qazHOYyzx4RBhCDA6\nrS+WkLwtE2pZZhQsEdGzJS0ehSrCciDD6j4dlF5isfzba8H39UDKI3ZHvrBi\nlJED0d1rt6do8aQRs9RrJ9gNg8Homvaho5rFLXJXsqRh8kFTJxqonVRzm6Nm\ndieiEmrFwmDau3fwHFxhYuoJ0YoAZBw4fIxvUQBd3zZxedYltCtbzzFCQUv6\njt9HB7Slb4Tf8sxMfrJCjgjLHlODhqjzzeiK54UkSqx4AcMOcx9Nx1B5HGMI\n5UwA\r\n=1XeA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC1FPv0R8Ee1owXiKq0x2udk+UfuU4N14bKcd6KXrVbagIgWN8iPdiOCq51AGKakYRACdoum2q+83W8pXK5CZ4a04c="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.13-alpha.0_1639431537116_0.1419969829197616"},"_hasShrinkwrap":false},"1.1.14-alpha.0":{"name":"refinerdb-react","version":"1.1.14-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.14-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"f94aca66122cc3967db50b3d94ab04c51a6f7efd","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.14-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-sxWwM6yXDU7ag9rD+uEuGuBaxnuD1oen5FYoLEsJ2yLkLYLjG7ufuPf3K5H5JmJ79McznKb0dvAm38+6UuB+aQ==","shasum":"29589085bed65ea071ec0bef602c7bb6123d7114","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.14-alpha.0.tgz","fileCount":177,"unpackedSize":3667494,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJht9SYCRA9TVsSAnZWagAA57EQAJZ7TxqwZlSXX+AVbHTE\nu/4M3gYdoxcQdJsws++m43V1loA1+HWVlG1/jeiYfMM/tXLaRstz0BR3DA2/\nqxwgbXKSyZFRdQfutVc24uNfoscM+yhsul64zRvoi4cURiWj7+omYCqLF2vB\n1IYY4+hevTPTWiRjFNlsWezaqcY0w5LU9+QSPNoxW4bP9Xkoc9O94m15MTvv\nCAo25+qlVg9bKwnz2PnFezEkL0cbQ+YaUFui52gV4q83GfORBBTlh7d2AcXw\nlmSPMy6HsqBUzsrxae2fVmU5rEylSQbUpzrPVLXg0vw8Lb9AG7ci/O8xGFZP\nIItfHWSo8Xt0v6lLxE+AgHa0GOFVvikXFKjPyew2j1qpQV6rALFBahIazd12\nsl8dthftuDdn/E/Yugprw4j0VUFafEr2hzZ3CJmILsoP8qs6M7G0iUCTvWfv\npfUzEhL8jy8gd/6p4rVhnw0EqLiKAqYBqL9fXScdxIRTfLmBJaw9CxfeF9mf\nBI80l/S9bpwtzIZNumqZzgVf0CbfeFEsYAO9tULpF0DeNxK8YN1my9RSbd0Y\nRzY/AQjjBzb2T3gSAW+DHKQgon6mnj5fUKkMYbcloTlIkeqSlEzk84/tyBq2\nZ1RTdp8Q7O08AH+vvgxrk/q015gU/nTLddpSJRdznXs7lHSqOP0kqu44zh3r\nNsPq\r\n=U8Ny\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGIHKPmdQD9k6xGr+z2iLnD0wU818eYGwW+qAnIghY43AiB2t17KZIpFB32A6uloxCT7zh6ahxZG2pQa8F9NWVq6nA=="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.14-alpha.0_1639437464407_0.38038883956697656"},"_hasShrinkwrap":false},"1.1.15-alpha.0":{"name":"refinerdb-react","version":"1.1.15-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.15-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"6226262c9f125c7430fff05b221cf20e38c1fd85","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.15-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-L71PQ4DQ4WQ2h7N1kZ993FEpKATLqT/oaLfvLAAbbrT/D1drN68+fps5pHr84LF27HMZ/TlyjPqu+uUzDfG0eQ==","shasum":"4622ef82caab0bb28fe455004040c8212eb0250c","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.15-alpha.0.tgz","fileCount":177,"unpackedSize":3667494,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhu33mCRA9TVsSAnZWagAAfxgP/2bDuxdmTwPLUyJZkEjo\nyefeEtsnTGY+vUPS8IdApLrxDCdhQYpbIVke0GY3NdxDsbAS/eVJ28uY4s9X\n/UEwxXlW9UPJEzxSH0+1bHksDiovtTlmN/HmNw1lGAL0MJo8Iyuip8tiF1J5\n7GsC/yJQjSJqyybkYeefpxbHLdy62DInrbZo7nuerQOmHlXKIhye2b3pF7h8\noEHqPQ71iwWT4xRcmv5i8Ga5YoDfn5Y6vLXyRjYtTvNHKOkVq7dRUS0TYoi+\nlouMIRHIQCLi8FMU3uQ4M+w1h/7tjrY3Jh/HVI9R6yHP2Yym9k2S82suH1rn\n9VsQ7W31UBFYDmMghloCQ/2rsAMSED8PBsIrX+EuPOKPOpPa4kqwNRANEeGH\nxJb8af5QZe2vR/kitIhaLl6veBpZN9zPdSeSs2adz4OPgNEtnm6RnKm5n3d8\nfdo8vbe/owwK3tlWmo720m9irKrVWqxY12zvEGqR4Kgtcg0rtYskZEhGD/fB\nRGZ1IjuCOyWhhz2fzgJFPcKjmcISX5fpbf+iHuIXBOLEIk7zPlIF8S5fFO0R\n5D2raPD9m+SCueP/tnL4laGk3H5kajNnX2VvBz8fqLO+nRB4xZ64oy+Um1Mp\nd0xrfT3VeMQBqxHF8NHiG68UXCp94VOFQmgnEuGkE6CDVNaXQnNTO65BdPzJ\nrfkS\r\n=6WWb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDVhhv6mCE9pZjV7Cxo9C44YofsIcaC1aM+KL9zailXAwIgM4F0RGZYLaK9+tejG4hL3z8dH6PhgVBmi51W8XHob60="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.15-alpha.0_1639677413939_0.14325730274161885"},"_hasShrinkwrap":false},"1.1.16-alpha.0":{"name":"refinerdb-react","version":"1.1.16-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.1.15-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"aace1ddf895f51c4d8d9ed294b361cea79d582d6","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.1.16-alpha.0","_nodeVersion":"16.13.0","_npmVersion":"lerna/4.0.0/node@v16.13.0+arm64 (darwin)","dist":{"integrity":"sha512-uKHAF2ShWAuoJ0VhnN7mVdKyLKMgxo/rQJ/+Bk5Q8/MoFGal+LKaVg9h3apVqC1xDtLszFTBW2f7fAlbJV7NXg==","shasum":"db506de90d770209a1e75c86e446f0c153c52633","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.1.16-alpha.0.tgz","fileCount":181,"unpackedSize":3671698,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhvO2QCRA9TVsSAnZWagAACvMP/REi2Kt0CtQvtxkUgR26\nV75nh9gKczWTmUTSmbPrOcUZsWePpn29xf2j8puGKlbpVfQdgbQ3237MgNS5\n5RacMWlx+uSDL9BKsgIiqvHGyf69Wh2O8XXtBo6Qb2ixMnmDfMaw8xMH+/6s\nymc2cl48SwFH92iHN1vBx2kZhnWAUnyUpMfa+7ZlWPimMxOcsmns2BV02gS/\nqlY0/0J7daXUhou4rhujdI7D6vERbvmIDsOC4brkoMxfenTheWcPN97mZX8v\n4i8xCUHmsL+s0wPSrxYUSjpWLEOyrXsDQLsFIvy3CboPY904or7NR3uDZvs6\nDIOdqYW3AG4VpoOhv6DswpKZybopUILt99WxdNUR/0SJYd96wfEwQIkysYAj\nKizdbAFbaZ4QY0ACyskNVM1Iz4datCc1Fy6LP7hggt+1nI0xRSebvqyRAhQi\nv+yjPQM2/DDL7+nnCrxXCswuraQmEY0rX4twU2nAZ1vmpA4OkKdfzCL8wlGX\nZSXyljVpPqAAi19N99HbIZVgBCVRVTiCaQz9GEMA3YwrI1WRacmqnB5aNzSi\nP2pZKQQCiOf8pLP+LJzu452kQQhktatExbX3GdgUxoYVSAoIBnA5mAGJnaXJ\nAKMUsdbU+VaXy6HqzvVQuq4AkxjmKJd5vEDXlr7H/bvcNlHN8KUoKdXM0DN+\n3DpO\r\n=95DB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbcL3vzMykVt5monz94CQ7V9M3nT3jiPJDku6OIZqZQwIhAKziJRjQjG7p3Gsyp/XmXh/62VeV3qd5VcO1NI8K2Cd5"}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.1.16-alpha.0_1639771536421_0.6240504934954099"},"_hasShrinkwrap":false},"1.2.1-alpha.0":{"name":"refinerdb-react","version":"1.2.1-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.2.1-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"c1974b80733f28a2065ed9d85743dee5d6f0c6e0","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.2.1-alpha.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-GJ6NXL05B3MDS9thBkTr0aFzFr/6vILkcSsg8zR00P467U0MEK8bwc3qhePQSZCUdbeJ+UzgjHGI6caw2vrz6w==","shasum":"e93ff619d582c11d5eed89d575453b020497fac0","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.2.1-alpha.0.tgz","fileCount":177,"unpackedSize":3671213,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiGTd1ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrw4A/+PKBXA2EnkuXz0/wZYRCBRHTTL5/KcyxnPOsGOTaaQTkK1PR7\r\nsnUw1vuRpsFyJmt+c+dZlt7zTQos/pknVQ9eYuKo2MWfCpyuV7TBMGwtppou\r\nQzQnYpe6AsExGZTb2Q29Urixvsm0TGYCRn2svK086X/qIhkvXVDB+JbATY84\r\nrHp8I9rJUvkm9DibJ2ehCSFKG6FYZder2UUxBcgBmuEL3JDXKAMa/5TtmRBf\r\nyScUJo95rf6CnDFILs6haLVGMVrM3pnQd0GCgyJWoTbN7rVR0ddtk+0ei64Y\r\nQ8mm0msiQ3o9lMjbbjkrDKM4qoSuev9Cqxp4NJoSf6OW/r7Z+g51jvDuEyDF\r\nmZPQN9YhVTZyjEkiUaNiJ/uTfxOim8yRBvAx2GH52eTA/hHk4iKtL+Sa6cwP\r\n0RFwNxeE7G5eHeVln0rixqC0Koq5Tketq9SdaTJecP+Cqf5IDJyz/F/H1XBQ\r\nDN3htFUkM7xrrdaBJtq4HLTtJwKrVIT/V7fcBmwpuVVkIXhsC6IQKhSy+gE5\r\nv9GHZD+Z9+02RlfdXJbcIvuDtkgzBdpcGxPf8nVDgFo9KySeUooykEGSemwe\r\nmbMVxeZn+KkGFshGq+MJ1AXcabv5M7TGpTdHzMkB8CG8V4WNrNHnT/gsULpM\r\nNlYCkUfV5eyuWjQX9lIzS1BkhVGs1blwk3U=\r\n=xRcs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFmsRBRwfDjVUye5ngEw3QqSFAcPLBhvWdEP60ROGOTbAiAnB851dT/tUqfwpf1L9BRyNi5vQQn66m24p1hZ0AHIMA=="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.2.1-alpha.0_1645819765092_0.49429604479631406"},"_hasShrinkwrap":false},"1.2.2-alpha.0":{"name":"refinerdb-react","version":"1.2.2-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.2.2-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"49f848c3f336567d3db795ecb14395cec05235dd","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.2.2-alpha.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-JHYBnvagFfa8ohY3077QIOonm2QjtWB2KfA/vrpGEF9rvENmCVZCk3Bjjx2xIUuMe0bfQP3NNlYf5lrTu64jjg==","shasum":"9e094b9be7f6bdb32779de5cc5ba82622097ace9","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.2.2-alpha.0.tgz","fileCount":185,"unpackedSize":3674548,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIpDnACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqNsQ//Qjd6Q0FuWPBrwfgcaTlzwfRalk8T4vA3hGTgXLbORc1s+/mE\r\n6dClLL7Hw0r8jloh3gzH3hfpOQmvJKVPM/GZNYhvEFWiJU1esUYlGJuDNnBS\r\npgeTUr9lda6j44+LcROnHdFv5S1TQ8R/aDvVig/VJKjBYgenA8kFbsGvr0sf\r\n9weVM14TQ7OEgLbwPl/JhHHExkweH/iNRCvEco3xDzL4j3HPd3KtADifkT0J\r\nfk5O+gqIM4Y9xBbUVzGMutHEM2xsaVFxwabqfKfLdcphCHvnjSLGjooqpauu\r\nEhpdvq2oa23FcrRz0g06MeudcbhDyAfsZ8yjuPV1oz3LTSO+nkZ2tLZrivHK\r\nwsQzfwOgmnOnSi4w/haQNiQLbuZzxFjxc5R77XySHMmSx2F9ijKh3Z6IQxcS\r\nHkmrNqaLv4ofpNGGPMGEJoM0Ccj26OHZCwCHC0oCC0Y4tE0izNZJY5knLl5L\r\nsLqvuKwFuwGSZ0ve6KczSnnrDjua6oz2CKwp6Qz1p//YrnC1sPGhOYN4e78J\r\n+5qlq/Y40v5MANh8Eay3H6h0OShJuRtLhrNGJLBuimoVJfIkc/oXy9b1x90c\r\nm/TAHQxHzBDhrjJovfgRkjkUv/SxdBsRNRez/kLL2v5IUEXDD6gygD/ZnFR3\r\nd3MgPZ3QSMA5wycA+Rx6YBK0m4asGd23JrE=\r\n=dzpm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH57Zj7nX/6MbOI96J+DJ43Cz7eKJPSaVnomubYqS2TDAiEA1IsCNCLOl3ZA2Vuw7ER1KyqxXNqHaEYHSuoVJRrvess="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.2.2-alpha.0_1646432486979_0.5229778418558961"},"_hasShrinkwrap":false},"1.2.3-alpha.0":{"name":"refinerdb-react","version":"1.2.3-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.2.2-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"196d248ee1cb7cd7a4965821afed9cbdcd5a62d7","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.2.3-alpha.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-A1gBGA9ntbM0enMUxq9TsZ6g2LSej07fhO7JroMTQLkFJzrKvjFvNU67m9ksTJK/cVOBX3YHkyemI2lHbDdlkA==","shasum":"99ffce80340527b751ec3496dd42e13641d8e8ae","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.2.3-alpha.0.tgz","fileCount":185,"unpackedSize":3674812,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIpQAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmofLRAAmL4rweVxaNtJy/AEIb578D/GGxSYha46GwMMheEFxmvKYutQ\r\n6K5pLdPhZVFquVdSuFBz9W7qRCPFSEf8PmD3tH2G06mvNrQuc4pSBs09xhWZ\r\nr8R+DtaQ2Ty4w95cOL1HQ/DMwkvhm7f1Jf18RQBkV2ovX0O4We1Z66RYtEn8\r\nntxX8pJvhz6lG7Br3TL2vDQpqKkq9uaeMYx2XJZOnZ4C8cqzKRqY+x2+8GMG\r\nrc8Xj5mpEq51EfT0FUYlw2ZzIBoIwTN0+nldpxda8LIgD6993XNZCJRhLPFY\r\ndgj8LbX0n5NxGGd3bp7CQXXTk+bjSJZ9TSg/qsj03UuCKvmQWHVm+iY7KyOw\r\nxBrSqriClRnl6axxhwgK1Ou9l+Ze7cFNZgsxYRZtcXRE3+rnTNk3uA2mKRrb\r\nJsBzS1JAm5OqN4AjRxZktc1KJ8vpqvSVLPCjTGsnDdKgMVZVUF4PDy2vbj+/\r\nROqb3v8VDE1RfZVndZxBFQhOIkwhw/szHoe5glne0b0nTjuyUCeNKEXkFMUS\r\n29BvxtHkQNLHPTBQCKPcsftNWYWY43nJbtd+8Vf88lJ/FT7vtlnZ31w/zYsd\r\nH0GHjYDc39af7AqKnL92+T2ca3GeH7LUdP6iGyFIShButkDmBEvRvvVxRAZC\r\nSGZpuuFAg9aoOpRRz6uzrUXa9jOk+yDpJ1o=\r\n=aIwL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICzQyeHArVVfoNQ5hBotdHdYbCUAr5wPuh3e/H8TXuVnAiASMJy/wvntxd3rsIj++g9PoZNP6DiLu1soezgi3srjOA=="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.2.3-alpha.0_1646433280118_0.2113070908593333"},"_hasShrinkwrap":false},"1.2.4-alpha.0":{"name":"refinerdb-react","version":"1.2.4-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.2.4-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"66eb1b7aaddef345d4bc34ae4b39c8cfce6e03be","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.2.4-alpha.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-TGxBl51Vu7WuQ6hXKLB7oJ2uCdEx2C+1UB0KwEu/Sd9iV6kOijuxsVklcg+ZRWDXEKm4PjLpkFflvHesdCzLag==","shasum":"e413702cdac85580436be993d8c5938bf1453b5f","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.2.4-alpha.0.tgz","fileCount":185,"unpackedSize":3674122,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiIpzQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpsxw/7BgDTvMuMFrnF8NiLAjXVCYanfmNoPypO7ONo6HqLos1gB41Q\r\nauxDJ8BdaPD2GC76XL+VMmPDZpe0YBFtGVk3L91annMUPUJqq4JmPHWLluVJ\r\nTnxpiPdT3s7A3AVE+OZY39GU36RMTD6fsaBADk6U2NObTEWwMfQlbOKYQH5q\r\n9jH3azTk9UMEcUlE/dPu/fkAy/vBCalgyuVAdj74h0ikwBJK8OwYXi+FaUw4\r\njiSIrOrvs9UkuCVzNaxhw12YiiaPZSKv6z6AWoPa5xOfG8I45lmR7xAjkuJB\r\nAAK11f5P3v04DCLKK00PFlMpzgMn/OLmdx6iAWh4+YokTskc1MpunZvuKQ3G\r\nHg5zT10VywHXgdf71KtmPXgNVpU4s71cbdJ7nsZydYqZElGyeeYUtOgbkt58\r\nAiqmvna+B8ulZfoN8fcMeVh2tvCtde6m4pYEixLkuHsQ77A5a8LsmbXgAZ1n\r\nYJ4EzHjnRmPCIEaf6EL2dbdFoemmys2Z3uvEHrlHZR88iXtDKa79SvQ1ll6b\r\n20MYHZnU7FzJEdACKCdmjVyHWfOI6VMrcX/IanFhSdPh39uuM3nwmaIV82zn\r\n1F6AdR1ItDWOET1KKhFgNdYTjubMXDGSeD3nOpPwQjvBoCG/gnCJcVuidqC7\r\njo1S8bCtTIFXqkYQ8dklvojZj+dMGeL9Dyo=\r\n=dAH+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFzsQQAswUTuiP21M3q3Lrz/4+BW21RvbwjYJdEjdFspAiABdpjcMj6sAT0m8BMZlWXQpWYuimAhLGn26v5kIZYRkg=="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.2.4-alpha.0_1646435536074_0.5911745443952747"},"_hasShrinkwrap":false},"1.2.5-alpha.0":{"name":"refinerdb-react","version":"1.2.5-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.2.5-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"fb4f3bea3b6c1c9a6584266d7b5bc3e2a69fd48d","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.2.5-alpha.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-DhLRAnZwkdbUXAsXtNuYKMLBYMAiyN2psfG8e/UkpPJJjeDnqAKq40E2yY12RnbYY9R5yJ1efcXj/R9W4l4oWg==","shasum":"a29dc22022460f4561ab91ae3c741ac08607975d","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.2.5-alpha.0.tgz","fileCount":185,"unpackedSize":3673958,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiI7QLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqoKBAAhgy6rfgcp+qxal49ubSRfgFykFFoxt1i7+JQjYZCqN0CwlI3\r\nJkC5+t6hk5pM7x0d9ZzTRklPNHxCaNrvX12lKJ0Bg6lpFvUqV7X0zzigOmft\r\n1lVi+Pp/ccPqlKhUrQglg91tqjybhOY+GcohkikAzzvhAUIO4HCcJoOMYDLd\r\nPaVhSuJf84PWAuY0HonQ8BINbs9zMl8nTrTLqUe26hHUlTHF2SHVWFNNaZzI\r\nBKpknqX4/e4A+4qBPXeXZS7LlvXQrnpTOdgGLnrnmA73/uT6ub0JywyccXAY\r\nQ+2jem+P70uDuKOkxz2nTDDMQgf4T/rpzcOUs7aXz2I4YofGTfHLtGY7wl2j\r\nlfUXVG//86LJmANEkHScXQjfQhHnUWlEyqhwZmUToWL3UucpTfswTHrNlPCJ\r\n63DuEp/95Y5QVj7F0YIMiiQkXsDINStxg/nouJlzOzRSIBK94jxSpWj8870N\r\nM7uct9wqN0sf9v1nAxeHPUzLAA7PUPy82KjJMWgiB05mdAXWXTRIuGSf5Wk5\r\nBQY9whz4Zh6wWK/mr6o4sVQ2FFPEhL2wlMwoxDa4xIzh2Y3VP+o67AvHWOOl\r\npQvnt/tkVEuBFH5p1hZvsgdlOrO43BIvN4M82J0JRXYIKDrgHj3sKfHo0TMd\r\nU63IbRbEXf0mqOloRfLX4Dkp1HCPbMYOGMM=\r\n=ZNrs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFZywKxiOhN944kAQ47SPCSzUVhbImP4/UsYADWfjFpPAiEAqCy8bkQ0pS7aSQaS1ladLUJKmC0HCL8ONzemA4cEY4A="}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.2.5-alpha.0_1646507019222_0.2793766170040175"},"_hasShrinkwrap":false},"1.2.6-alpha.0":{"name":"refinerdb-react","version":"1.2.6-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.2.6-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"80e337c3f59ba6a8a421184647ef0b996ce65ef9","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.2.6-alpha.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-mQezrYZ3guYBiHS6KKv27Z2UZrzuBq11YYkyszo3z3ceR/axNpM4qsC5Svgz1k+tCDSEYCM/6epqgRr3woFZQg==","shasum":"4e1956033f1bee12ed287a7b6f61f3a123abd447","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.2.6-alpha.0.tgz","fileCount":185,"unpackedSize":3674210,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiJk9sACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqeYA/7BKODuKU6RRJX3gq+G9kPMgsm2hWRgHJyP5qjrwGneNS0D0FZ\r\nFnATWO8UaiTZAz0RI5sbRRkvQ/8aD/bQvltmAokqrRgNbdrWSy9mVhuLODXg\r\nY0YTzrUGEbXII1p/9tMQKJD5EWvX/bx2u9i1thLvTrjHfWA1K9HMBVqKX+jO\r\nGvq8gUz4RTJgz9JE62jxcOryC5YvkjHc6YWlb/NpN/+KwhIyb2cc0XQWFVPa\r\n+g1brsPbYhdGNC4+Npkan7l8s081BAHJypxt/kMquQjv1PIpJuhIcMZ65X3e\r\nI67RTtxlO9KAcU4W9FLAFOWbvJyILPo7PmTqUbPUkKbxuSHMCGeC9xW+AzBX\r\n3qYKKz670h6V87NFwLsrA4IXaFruEyK4atvX5JwzB2bnCM5JDm4/LZNGiH55\r\nmQgdp3+pIwEEselYr/lhM2OUniQ+ZYvRGGfWbkixR1WBkiIvL55GUT1f5LLp\r\ncPjPG1YtIdinm/jk6XYIiMa34ZX2TT9yDDVtAVFEE3RdR3nId4iOkuaOUyqq\r\nHXqC/0TJTNz7/vyR5JLDqhffmaIK0OalXG2LwHazhJ4iXwwlJK9ILNNLfwZ3\r\nCx/z5v9axQx41nAU5edZZ9/4pSy6zwUf3FApt3wOkseBi7vlddsr7JrwzNQk\r\nDEtco9aGaGclDTZhwmqzc6D4C3KJEWT3ajo=\r\n=38gf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7MGv26PAa4XpjDNr+NOESBiylDm3JsW3gUiG0ZY7DJgIhAI/mYYdTpWrWpkxPbAr036EGr3Bn//xrV4hy5/HOn0Kn"}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.2.6-alpha.0_1646677868437_0.8691183652934467"},"_hasShrinkwrap":false},"1.2.7-alpha.0":{"name":"refinerdb-react","version":"1.2.7-alpha.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^1.2.7-alpha.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"140cdfbffb256071ce1818614b740bb0acb53e06","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@1.2.7-alpha.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-VjIuz/x2CwquEJzRXTYkORhfrzRcCjiqglWUAx3hKbGrf76ZwR7IZcTHJZlDUsOqiEx9UVgRgn3H0UAR6s25YA==","shasum":"6069d9ca9d513307055e93387435757969c9a455","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-1.2.7-alpha.0.tgz","fileCount":185,"unpackedSize":3674076,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiJobMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqfaQ/8Clyag3COPa2vmjpubHyTPXfvAcxUnF6jBUMgNLzxuPLJJ9/o\r\nJLhqgjAPBHniedmL6GrV5dWxLzLS8SsL09z8R+m7QfUL/ZMBVNSoqAc3mdMo\r\nOqZl94Sof4VB01mbkiZC8O4oOe86lq9khk+SGVWwa0YuYweH585MfWku4+om\r\nwpABZBePlbjsQuZ+dk4vhn6cz9OB/6oJOpx10PVRN0fvk0jRsrO0APp7kQ+P\r\ni8oN67Ryado/GSVupERFYAxCFbGa6na0E4smL8//JnBtthO91XOmQ0la3G+A\r\nIFX/cJQ7JbZfGJRVIEsVOy+ZyprGG0jtYEElqP3Rqcd6PEToNuMVdQLMlSuL\r\nsHPSqBbufa3JVQVhj7we/k6FqTL1twxURhHH2h+m09XtVVkt9AnRmhnPS9CH\r\nk1zBHKbc3t3jlXyqafKiEvJFIHRsHpsl9RyTYfeaQFsq3Am7VFC6JkQ9vXOd\r\ntqyIH3WGZtCXK/fIlznbmuFDcQlIO/s7/I+c217/eHvOJxUlrlyOy+kBFhyb\r\nDj1/XyfSVGyDR/jO77k6oSzthL1fFeRYKjecFXaAmLKxlKeRmMtNX4PwGcNB\r\nZvPiTfmhbqtnb1CbCDhMXSG0g5B1HG2ZItJ92eVfV1+5iuriPS+AwUn5tD+1\r\n9miQIX9draaYNjI0Qa2kEp9GTbHyJ6ZJjbc=\r\n=Vn9G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCpLwOW3RyY8s//J871CS1569hPKvClnOgYj/zowezYjgIhANbuFuc4gXrOwe2FiAr+lFg2KGb3EFHFWTuh2gEXFbu3"}]},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_1.2.7-alpha.0_1646692044497_0.6893506839582348"},"_hasShrinkwrap":false},"2.0.0":{"name":"refinerdb-react","version":"2.0.0","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^2.0.0"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"02453064cb3ae22257d96b6d5cf6c03426b5ace5","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md","description":"## Install","_id":"refinerdb-react@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-lSNlczAFv/mPch09XZmjD1m7L71xk/9hIRBNkaoEotr6k/xeosdo+pda5Dl6ri9Ra1f1u2PYycTl1W4RSSoZpA==","shasum":"129177bbdeedd733008ad625acea2c0508a6c7ab","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-2.0.0.tgz","fileCount":185,"unpackedSize":3678880,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBbZNIWPYdpb24FISkmFvOJMG+C1U8V9MLxfrACkFn9QIhAOk/M9RrPNkJjFGhGRV+Vh+WH76MBGeXvAqKwNkAyXmb"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJihTc+ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqFLg/+OZRw+anlJY0vt4oWj1nlC0qx/rqJ/ASfk/An4XbuD80YSkSq\r\nZI211ZNbcRGsPy6PLCUN0OOTzBFHDUnZevg85j3f7ZZKCsy2RIj9SFuGkUOn\r\nIvpugRhtRe7/zreH1pziVS8L4kLnqgofDNZyQMpaaPrzRquCt+ngmAwPChBv\r\nQ8ltarjTzlS5mu6ZmRq3naF7pZ3QUYGhRWl/n0X+ImuZMYC3BWgHTvwSeEJU\r\nl6vVqOvTMrHSdLRIa/XjnGWZxHbw9hVmkm1ABCUp3Or+vod60/mDeGsI1kgC\r\noOIq6TH4syjzapetwXL2/vFhcQ626k3xZtzLGDmnmt57Ah9mJGnWI/krx3cO\r\nySQ8s2Hz/oXSZ6V1kD8xtWHa0iJHL4uBeQ7pvdyKXW1mr9cpLBqTZYgC35er\r\n48MUO/8wjmYphB/wqa7aT5+XI2XFD1wS5B3NiTUPKTjJhJk2Z9q9eWZf5rQn\r\nx9uB6otJoViqEA1y7BLCk1/vULi534+rFmU4gEQcnpL17NbMmLC69vXT/Bri\r\nrn0z3bx8Vc/le3tO8xu7+VoaQVGRhoZFOtq8akRl7kIpLlK2k1YR7xt+JnJo\r\nNXzuznh9gyndwwxKafkj8+1v76BZcvIbDwgOrraQ16DSW007yEfPvSZR05W5\r\n3peUP02aj8z0oSvhpKEReGW5ehUwGJ0yimQ=\r\n=+inH\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_2.0.0_1652897598578_0.9396115123967883"},"_hasShrinkwrap":false},"2.0.1":{"name":"refinerdb-react","version":"2.0.1","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^2.0.1"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"dc50d8521ffedddff8cf2218d75e6e3a285b9135","description":"## Install","_id":"refinerdb-react@2.0.1","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-6GGqf6gAvwa6jrU24F9Jgvs4U9Wz3wArKNEDgp2ZSKX3dqfkJSv+n0ZtUNes3GDKhJd33ji02/CXH99WbE3MOQ==","shasum":"2fe4aa8c0cc366d8821a6f9db9053fecc6460218","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-2.0.1.tgz","fileCount":185,"unpackedSize":3679610,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTBdZoM6qHg66NXEnGIsqkdcC0QT2Bno80Zo+/q8dDlAIgBmD3/rV0GuFepAcTu+j/yT/UBk92XBia4VgFqYCnvYk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJihU0eACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp1KA//d6OgkStXKPmKfunhsyjAnRuZxO0x+Ap7VanSx6h+U6NUrNgo\r\nv75nVbRPPnfegfyOxQVqlOGwrTZioixsad2+Uz/77QNoTkdqEOQ0GD15cpFs\r\nSLJbUzau5GeEd+OnLxVQfBqZ23/czMLk8/Si03L3fLqT/gR0SljXBGxIFj56\r\nrnuYfm1i0Zl4jXjpojG8qFJb2acY8jBge875J0cv8vHbOZMp1F/G9Mo5RJ2o\r\ntUIy0OhuJG7zdnsACbKmd9j1BSPlw2TTK8gOGtFKop4vl6b9bGemoR4WfcoO\r\nFg/dN/oDggUvOYsn20kKbUTJIMBQe29GcHGMtwd2Kza+QPgXGqFfauLPjYA8\r\nM66/xeL2YMA+Tahpkc7OUgd358c2lgnEG1q850Ch4ETpy6rbDZA0fikOCQU8\r\n8YFq/oDBE3AIqseeQhPP9h9GrGUJ8owYTVlXg4z/HM/vJOylhINoS7B1IFNe\r\nCr6wa48rXaCV2byH4go0JqKIJZVtA0hg4IsSj6Fb4pez4Lx4LuaPt12Pls97\r\n+IwFThC/qd1v7TAgrnfDHduLupTc0Z0hWO+4bWL4l4QOvZv6OemDSJwgWhbf\r\nXwhIlDOjb9VyfprJkt2svuw0yqW+1JvB4AtHsrLC9asFrLX1ce5CC3RWlUQP\r\nysjIQJ4tP46KYxZ8mjgMAhMuN1B6H581ZGY=\r\n=PZEx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_2.0.1_1652903198593_0.48228377787213805"},"_hasShrinkwrap":false},"2.0.2":{"name":"refinerdb-react","version":"2.0.2","main":"./lib/cjs/index.js","module":"./lib/esm/index.js","scripts":{"start":"npm run storybook","build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rimraf lib","tsc:watch":"tsc --build tsconfig.lib.json --watch","storybook":"start-storybook -p 6006","build:storybook":"rimraf ../../web/public/_storybook && build-storybook -o ../../web/public/_storybook","prerelease":"npm run build && npm version patch","generateTypes":"node tasks/generateTypes.js","release":"npm version prerelease && npm publish --tag beta"},"dependencies":{"just-compare":"^2.0.0","refinerdb":"^2.0.2"},"devDependencies":{"@babel/core":"^7.16.0","@documentalist/compiler":"^3.0.0","@storybook/addon-actions":"^6.3.12","@storybook/addon-essentials":"^6.3.12","@storybook/addon-links":"^6.3.12","@storybook/react":"^6.3.12","@types/node":"^16.11.6","@types/react":"^17.0.34","@types/react-dom":"^17.0.11","babel-loader":"^8.2.3","react":"^17.0.2","react-dom":"^17.0.2","react-inspector":"^5.1.1","rimraf":"^3.0.2","typescript":"^4.4.4"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"gitHead":"bed5d534d959cf80b345db64670c01acf54b9bd1","description":"## Install","_id":"refinerdb-react@2.0.2","_nodeVersion":"16.13.1","_npmVersion":"lerna/4.0.0/node@v16.13.1+arm64 (darwin)","dist":{"integrity":"sha512-P4BulyFfb3ajcBSH9NLtf9CoISSpGsTF7ks24LWHeU7ceGpk0YmohIPtd8ES4WmyXQ7R9nai/o0rFmTblv5zDg==","shasum":"d86e19ca3471709ef2ff356ffcadc8f366826bbe","tarball":"https://registry.npmjs.org/refinerdb-react/-/refinerdb-react-2.0.2.tgz","fileCount":185,"unpackedSize":3679610,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDa1VWqEXQhni2lwr3RvCxA1eBvG220zHdjQUSgGB3jeAIhAPRiXBVJgv5tjaVx9Vojsd7pSbYUS/8XTULiG0p4Q8gk"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiir3xACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqDLA/9He45BVM1YrZuyHcySN2Xx1IIyGXvldmtw/IUQCAKXIXO6XkQ\r\nLvkCsDNcOv0VeI8MbJt61uJtSdqfc5dIR9ye3VSgcos1+YQ/sPmkRe9NDJ44\r\nu/wyjPhNDqVHz1gy/3U/LT1+89hUNuc6HG1MX0KDBE+9TFkEgMyCE7p1G8Rw\r\ns1rzyaA/4+UXPOc3FaRBo06y+Zr2fc0vNrIIz6hB7YNn98tlZK1/WMVgPknA\r\nS0csVS9iBM0b7vw3HD9ehZlrKkniCdlkwRVdR5H1WBsuAwijPVmb/FbavDIG\r\n0tcMBSf2kuy0TJQmKRBnJDHU+6UqtVXflP3h4l3xglcz5151igQ2z0xtG6MM\r\nxpgdnyNEOULzIyyifB+I/h66YHz2jhmf/1gGtWR86DjFSKwiTu7OQXR/7QLz\r\nidtIs5hbZ1NIuRm7X/w9TH0u7EIxkDtAet6+M/3lkOmMteJ1q0KtUjxXZfa3\r\nvDE+wvVRksCUSPby1ZzZPVfCyMCcnwsR0NClQh/rPv7lrkscRCuye8QOKHLf\r\nf9IP/2LZYW98ULluGWcZlKeC5by8Xb9EfYi7Rbly//aXSyLswjUVj5bAz/70\r\nFGZinWtZOVhaqk3CDwCROKNKoQS0yEjUEdCI01W0tpMB/l6zFXYEq/ErCcI7\r\nfPgPfowpe+4Hkf98krTQxqpa2GzPtFuptuc=\r\n=QaKW\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"droopytersen","email":"andrew.petersen15@gmail.com"},"directories":{},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/refinerdb-react_2.0.2_1653259761630_0.15497998869061025"},"_hasShrinkwrap":false}},"time":{"created":"2020-01-21T22:13:15.965Z","0.2.0":"2020-01-21T22:13:16.143Z","modified":"2022-05-22T22:49:21.955Z","1.0.2":"2021-11-04T18:24:21.736Z","1.1.0-0":"2021-11-06T19:03:38.992Z","1.1.1-alpha.0":"2021-11-06T21:37:26.600Z","1.1.3-alpha.0":"2021-11-07T00:07:43.083Z","1.1.4-alpha.0":"2021-11-07T23:04:13.392Z","1.1.5-alpha.0":"2021-11-12T01:41:49.727Z","1.1.6-alpha.0":"2021-11-12T03:55:43.374Z","1.1.7-alpha.0":"2021-11-13T00:45:08.839Z","1.1.8-alpha.0":"2021-11-25T07:27:45.897Z","1.1.9-alpha.0":"2021-11-28T17:35:05.109Z","1.1.10-alpha.0":"2021-12-09T19:14:18.756Z","1.1.11-alpha.0":"2021-12-09T19:21:01.313Z","1.1.12-alpha.0":"2021-12-09T19:26:01.712Z","1.1.13-alpha.0":"2021-12-13T21:38:57.378Z","1.1.14-alpha.0":"2021-12-13T23:17:44.602Z","1.1.15-alpha.0":"2021-12-16T17:56:54.091Z","1.1.16-alpha.0":"2021-12-17T20:05:36.756Z","1.2.1-alpha.0":"2022-02-25T20:09:25.322Z","1.2.2-alpha.0":"2022-03-04T22:21:27.232Z","1.2.3-alpha.0":"2022-03-04T22:34:40.536Z","1.2.4-alpha.0":"2022-03-04T23:12:16.300Z","1.2.5-alpha.0":"2022-03-05T19:03:39.459Z","1.2.6-alpha.0":"2022-03-07T18:31:08.858Z","1.2.7-alpha.0":"2022-03-07T22:27:24.721Z","2.0.0":"2022-05-18T18:13:18.764Z","2.0.1":"2022-05-18T19:46:38.878Z","2.0.2":"2022-05-22T22:49:21.874Z"},"maintainers":[{"name":"droopytersen","email":"andrew.petersen15@gmail.com"}],"description":"## Install","readme":"# RefinerDB - React\n\n## Install\n\n```\nnpm install refinerdb-react\n```\n\n## Quick start\n\n1. Define your `getData` async function\n2. Define your `indexes`\n3. Wrap your app in a `RefinerDBProvider`\n4. Create a results view component\n5. Create a refiner panel\n\n## Refiners\n\n**! IMPORTANT !** - To use these hooks you need to wrap your components in a `RefinerDBProvider` component.\n\n### Text Refiner\n\n- Useful for textbox refiner controls\n- Performs a \"contains\" query\n\n```tsx\n// Setup a refiner on the 'title' index\nlet [value, setValue] = useTextRefiner(\"title\")\n// Use it in JSX\n<input value={value} onChange={(e) => setValue(e.currentTarget.value)}>\n```\n\n### Multiselect Refiner\n\n- Useful for OR queries\n- Ex: Filter movies by genre \"Action\" or \"Comedy\"\n- Uses a `string[]`\n- Returns a tuple: `[values, setValues, options, events ]`\n  - `values` is the `string[]` of active filter values\n  - `setValues` is the setter for the `string[]` of filter values\n  - `options` is the list of available `RefinerOption`\n    - In the shape of `{ key, count}`\n    - E.g. `{ key: \"Drama\", count: 42 }`\n  - `events` Is a bonus that provider dom event handlers for working with common UI scenarios\n    - `selectOnChange` can be applied to the `onChange` of a `<select>`\n      - `<select value={values} onChange={events.selectOnChange}>`\n    - `checkboxOnChange` can be applied to the `onChange` of an `<input type='checkbox'>`\n\n> Don't forget, because it is a tuple you can rename any of these destructured items\n\n```tsx\nlet [values, setValues, options, { selectOnChange, checkboxOnChange }] = useMultiselectRefiner(\n  indexKey,\n  (debounce = 0)\n);\n```\n\n_Here is an example \"CheckboxRefiner\" component_\n\n```tsx\nfunction CheckboxRefiner({ indexKey, label = \"\" }: Props) {\n  let [values = [], , options = [], events] = useMultiselectRefiner(indexKey);\n\n  return (\n    <Fieldset label={label || indexKey}>\n      {options.map((option) => (\n        <div key={option.key}>\n          <label>\n            <input\n              type=\"checkbox\"\n              value={option.key}\n              checked={values.includes(option.key)}\n              onChange={events.checkboxOnChange}\n            />\n            {option.key} - {option.count}\n          </label>\n        </div>\n      ))}\n    </Fieldset>\n  );\n}\n```\n\n### Date Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n- The `min` and `max` are formatted date strings, `YYYY-MM-dd`, so you can easily pop them on a `<input type='date' />`\n\n```tsx\nfunction CreatedDateRefiner() {\n  // Reference the 'created_at' index\n  let range = useDateRangeRefiner(\"created_at\");\n  // range is { min, max, setMin, setMax }\n  return (\n    <Fieldset label={label || indexKey}>\n      <div className=\"\">\n        <input\n          placeholder=\"Min\"\n          value={range.min}\n          onChange={(e) => range.setMin(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n        <div>to</div>\n        <input\n          placeholder=\"Max\"\n          value={range.max}\n          onChange={(e) => range.setMax(e.currentTarget.valueAsDate)}\n          type=\"date\"\n        />\n      </div>\n    </Fieldset>\n  );\n}\n```\n\n### Number Range Refiner\n\n- Provides `{ min, max, setMin, setMax }`\n\n```tsx\nfunction RatingRefiner() {\n  // Reference the 'rating' index which is IndexType.Number\n  let range = useNumberRangeRefiner(\"rating\");\n\n  return (\n    <label>\n      Rating\n      <br />\n      <input\n        placeholder=\"Min\"\n        value={range.min + \"\"}\n        onChange={(e) => range.setMin(e.target.value)}\n        type=\"number\"\n      />\n      <input placeholder=\"Max\" value={range.max + \"\"} onChange={(e) => range.setMax(e.target.value)} type=\"number\" />\n    </label>\n  );\n}\n```\n\n### Generic useRefiner\n\n`useRefiner(indexKey)`\n\nYou pass in the index key and this hook will provide the filter `value`, and `setValue` method for changing the filter value, and the refiner `options`.\n\nFor multi-select\n\n```typescript\n// A mutli-value string refiner, with the refiner options\nlet { value, setValue, options } = useRefiner<string[]>(\"genre\");\n```\n\nFor Dates and Numbers it is common to use a `MinMaxFilterValue` refiner. This will re\n\n```typescript\n// A { min, max } refiner\nlet { value, setValue } = useRefiner<MinMaxFilterValue>(\"score\");\n```\n\nYou can also pass an optional `RefinerConfig` as a second param to the hook.\n\nIn this example we disable the `debounce`.\n\n```typescript\nlet { value = \"\", setValue, options = [] } = useRefiner<string>(indexKey, { debounce: 0 });\n```\n\n## Results\n\n### useQueryResult\n\nKeeps you up to date with the latest `QueryResult`. As soon as a query completes (after a filter change or a reindex), the useQueryResult state will update and you will re-render.\n\n```tsx\nlet { items, totalCount } = useQueryResult();\n```\n\n_Example component that takes a `renderItem` prop to call for each result item_\n\n```typescript\nfunction ItemResults({ renderItem }) {\n  let result = useQueryResult();\n  if (!result || !result.items) return null;\n  if (!result.items.length) return <>No Results</>;\n  return <div>{result.items.map(renderItem)}</div>;\n}\n```\n\n```typescript\nexport interface QueryResult {\n  key: string;\n  items: any[];\n  refiners: {\n    [key: string]: RefinerOption[];\n  };\n  totalCount: number;\n}\n```\n\n## Sorting\n\n### useSort\n\n```tsx\ntype Sort = {\n  sortKey: string;\n  sortDir: \"asc\" | \"desc\";\n  setSortKey: (indexKey: string) => void;\n  setSortDir: (dir: \"asc\" | \"desc\") => void;\n  toggleDir: () => void;\n  /** Each registered index can be sorted */\n  options: {\n    /** index key */\n    value: string;\n    /** Uses the index label */\n    text: string;\n  };\n};\n```\n\n```tsx\nexport default function SortControl() {\n  let sort = useSort();\n  return (\n    <div className=\"rdb-sort\" style={{ display: \"flex\" }}>\n      <select value={sort.sortKey} onChange={(e) => sort.setSortKey(e.currentTarget.value)}>\n        {sort.options.map((o) => (\n          <option key={o.value} value={o.value}>\n            {o.text}\n          </option>\n        ))}\n      </select>\n      <button\n        style={{ marginLeft: \"5px\" }}\n        className=\"button-outline\"\n        title=\"Toggle sort direction\"\n        onClick={(e) => sort.toggleDir()}\n      >\n        {sort.sortDir}\n      </button>\n    </div>\n  );\n}\n```\n\n## Lower Level\n\n### useIndexStatus\n\nGives you the current `IndexState` (idle, stale, pending, etc...) of your database.\n\n```typescript\nuseIndexStatus((status) => {\n  if (status === \"idle\") {\n    // do work\n  }\n});\n```\n\n### useCriteria\n\nYour `RefinerDB` instance keeps track of things like the filtering, sorting and paging in the `criteria`.\n\nUsing this hook will give your back the `QueryCriteria` value on your `RefinerDB` instance, as well as an `updateCriteria` function that takes an updates object and merges in your changes (vs completely replacing the criteria).\n\n```typescript\nlet [criteria, updateCriteria] = useCriteria();\n```\n\n```typescript\nexport interface QueryCriteria {\n  filter?: Filter;\n  sort?: string;\n  sortDir?: \"asc\" | \"desc\";\n  limit?: number;\n  skip?: number;\n}\n```\n\n### useIndexes\n\n```tsx\nlet [indexes, setIndexes] = useIndexes();\n```\n\n### useRefinerDB\n\nYou maybe don't need this one for most situations, but it is an escape hatch if you want to get at the actual `RefinerDB` instance. Most other hooks are built on top of this one.\n\n```typescript\nlet refinerDB = useRefinerDB();\n```\n\n## Tutorial\n\nFirst, consider your data.\n\n- Which properties do you want to refine by?\n- What kind of refiner control would each have?\n  - Multi value checkbox?\n  - Dropdown?\n  - Number Range (min and max values)?\n  - Textbox w/ a \"contains\" search?\n  - etc...\n\nPretend we are dealing an array of `Movie` objects:\n\n```js\n{\n  title: \"A Star Is Born\",\n  id: 332562,\n  released: \"2018-10-03\",\n  score: 7.5,\n  genres: [\"Drama\", \"Music\", \"Romance\"],\n}\n```\n\nWe might want...\n\n- A textbox refiner that does a contains filter on `title`\n- A date range refiner that filters the `released` date by a `min` and/or a `max`\n- A multiselect refiner that filters the `genre`\n- A number range refiner that filters by the `score` by a `min` and/or a `max`\n\nTODO: Finish tutorial\n","readmeFilename":"README.md"}