diff --git a/babel.config.js b/babel.config.js index facc811fe7..970ed58edc 100644 --- a/babel.config.js +++ b/babel.config.js @@ -79,6 +79,9 @@ module.exports = (api) => { // false positive (babel doesn't know types) // this is actually only called on arrays 'String.prototype.includes', + + // just for the PoC + 'Object.assign', ]; if (defaultShouldInject && !exclude.includes(name)) { throw new Error( diff --git a/packages/instantsearch-jsx/package.json b/packages/instantsearch-jsx/package.json new file mode 100644 index 0000000000..26fee334b6 --- /dev/null +++ b/packages/instantsearch-jsx/package.json @@ -0,0 +1,51 @@ +{ + "name": "instantsearch-jsx", + "version": "1.0.0", + "description": "Common JSX components for InstantSearch flavors", + "types": "dist/es/index.d.ts", + "main": "dist/cjs/index.js", + "module": "dist/es/index.js", + "type": "module", + "exports": { + ".": { + "import": "./dist/es/index.js", + "require": "./dist/cjs/index.js" + } + }, + "sideEffects": false, + "license": "MIT", + "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/", + "repository": { + "type": "git", + "url": "https://github.com/algolia/instantsearch" + }, + "author": { + "name": "Algolia, Inc.", + "url": "https://www.algolia.com" + }, + "keywords": [ + "algolia", + "components", + "fast", + "instantsearch", + "react", + "search" + ], + "files": [ + "README.md", + "dist" + ], + "scripts": { + "clean": "rm -rf dist", + "watch": "yarn build:cjs --watch", + "build": "yarn build:cjs && yarn build:es && yarn build:types", + "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet && ../../scripts/prepare-cjs.sh", + "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet", + "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es", + "test:exports": "node ./test/module/is-es-module.mjs && node ./test/module/is-cjs-module.cjs", + "version": "./scripts/version.cjs" + }, + "dependencies": { + "@babel/runtime": "^7.1.2" + } +} diff --git a/packages/instantsearch-jsx/src/Hits.tsx b/packages/instantsearch-jsx/src/Hits.tsx new file mode 100644 index 0000000000..ffa8015a4c --- /dev/null +++ b/packages/instantsearch-jsx/src/Hits.tsx @@ -0,0 +1,100 @@ +/** @jsx createElement */ + +export type HitsClassNames = { + /** + * Class names to apply to the root element + */ + root: string; + /** + * Class names to apply to the root element without results + */ + emptyRoot: string; + /** + * Class names to apply to the list element + */ + list: string; + /** + * Class names to apply to each item element + */ + item: string; +}; + +type BaseHit = { + objectID: string; +}; + +export type HitsProps = { + hitComponent?: (props: { + hit: T; + item: T; + sendEvent: (eventName: string, hit: T, event: string) => void; + }) => JSX.Element; + itemComponent?: (props: { hit: T; index: number }) => JSX.Element; + hits: T[]; + className?: string; + classNames?: Partial; + sendEvent: (eventName: string, hit: T, event: string) => void; +}; + +export function cx( + ...classNames: Array +) { + return classNames.filter(Boolean).join(' '); +} + +export function createHits({ createElement }: any) { + function DefaultHitComponent({ hit }: { hit: BaseHit }) { + return ( +
+ {JSON.stringify(hit).slice(0, 100)}… +
+ ); + } + + return function Hits({ + hitComponent: HitComponent, + itemComponent: ItemComponent, + classNames = {}, + hits, + sendEvent, + ...props + }: HitsProps) { + return ( +
+
    + {hits.map((hit, index) => + ItemComponent ? ( + + ) : ( +
  1. { + sendEvent('click:internal', hit, 'Hit Clicked'); + }} + onAuxClick={() => { + sendEvent('click:internal', hit, 'Hit Clicked'); + }} + > + {HitComponent ? ( + // Vue uses `item` and React uses `hit` + + ) : ( + + )} +
  2. + ) + )} +
+
+ ); + }; +} diff --git a/packages/instantsearch-jsx/src/index.ts b/packages/instantsearch-jsx/src/index.ts new file mode 100644 index 0000000000..c01658da47 --- /dev/null +++ b/packages/instantsearch-jsx/src/index.ts @@ -0,0 +1 @@ +export * from './Hits'; diff --git a/packages/instantsearch-jsx/tsconfig.declaration.json b/packages/instantsearch-jsx/tsconfig.declaration.json new file mode 100644 index 0000000000..1e0c6449f8 --- /dev/null +++ b/packages/instantsearch-jsx/tsconfig.declaration.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.declaration" +} diff --git a/packages/instantsearch.js/package.json b/packages/instantsearch.js/package.json index 071c9e63b2..71626c1893 100644 --- a/packages/instantsearch.js/package.json +++ b/packages/instantsearch.js/package.json @@ -34,6 +34,7 @@ "@types/hogan.js": "^3.0.0", "@types/qs": "^6.5.3", "algoliasearch-helper": "3.15.0", + "instantsearch-jsx": "1.0.0", "hogan.js": "^3.0.2", "htm": "^3.0.0", "preact": "^10.10.0", diff --git a/packages/instantsearch.js/src/components/Hits/Hits.tsx b/packages/instantsearch.js/src/components/Hits/Hits.tsx index 414757f00b..b3cedc7259 100644 --- a/packages/instantsearch.js/src/components/Hits/Hits.tsx +++ b/packages/instantsearch.js/src/components/Hits/Hits.tsx @@ -1,6 +1,7 @@ /** @jsx h */ import { cx } from '@algolia/ui-components-shared'; +import { createHits } from 'instantsearch-jsx'; import { h } from 'preact'; import { createInsightsEventHandler } from '../../lib/insights/listener'; @@ -26,13 +27,15 @@ export type HitsProps = { templateProps: PreparedTemplateProps; }; +const UiHits = createHits({ createElement: h }); + export default function Hits({ results, hits, insights, - bindEvent, sendEvent, cssClasses, + bindEvent, templateProps, }: HitsProps) { const handleInsightsClick = createInsightsEventHandler({ @@ -55,40 +58,41 @@ export default function Hits({ } return ( -
-
    - {hits.map((hit, index) => ( -