├── .nvmrc ├── .storybook ├── styles.css ├── preview-head.html ├── main.js ├── manager.js ├── webpack.config.js └── preview.js ├── stories ├── reactivemaps │ ├── placeholder.png │ ├── Img.js │ ├── ReactiveGoogleMap.stories.js │ ├── ReactiveOpenStreetMapDefault.stories.js │ ├── GeoDistanceDropDownOpenStreetMap.stories.js │ ├── GeoDistanceDropdownGoogleMap.stories.js │ ├── GeoDistanceSliderGoogleMap.stories.js │ └── GeoDistanceSliderOpenStreetMap.stories.js ├── reactivesearch │ ├── ReactiveElement │ │ ├── index.js │ │ ├── helper.js │ │ ├── Basic.js │ │ ├── WithStream.js │ │ └── WithTheme.js │ ├── CustomRecentIcon.js │ ├── CustomPopularIcon.js │ ├── Img.js │ ├── SingleList.stories.js │ ├── MultiDropdownList.stories.js │ ├── SingleDropdownList.stories.js │ ├── ResponsiveStory.js │ ├── MultiList.stories.js │ ├── ReactiveList.stories.js │ ├── NumberBox.stories.js │ ├── DynamicRangeSlider.stories.js │ ├── ErrorBoundary.stories.js │ ├── Dark.stories.js │ ├── RangeSlider.stories.js │ ├── RangeInput.stories.js │ ├── TagCloud.stories.js │ ├── ResultCard.stories.js │ ├── MultiRange.stories.js │ ├── SingleRange.stories.js │ ├── ResultList.stories.js │ ├── MultiDropdownRange.stories.js │ ├── SearchBox.stories.js │ ├── SingleDropdownRange.stories.js │ ├── MultiDataList.stories.js │ ├── SingleDataList.stories.js │ ├── ToggleButton.stories.js │ ├── RatingsFilter.stories.js │ ├── SearchBoxControlledUsage.stories.js │ ├── AIAnswer.stories.js │ ├── TreeList.stories.js │ ├── MultiListWithIndexProp.js │ ├── SearchBoxWithIndexProp.js │ ├── ReactiveComponentWithDistinctFieldProp.js │ ├── DarkCard.stories.js │ ├── ReactiveComponent.stories.js │ ├── TagCloudDark.stories.js │ ├── TabDataList.stories.js │ ├── DatePickerDark.stories.js │ ├── DatePicker.stories.js │ ├── DateRange.stories.js │ ├── DateRangeDark.stories.js │ ├── list.css │ ├── resultViews │ │ └── index.js │ ├── TreeListCustomRenderer.stories.js │ ├── TreeListCustomSelectedFilters.stories.js │ ├── KNNSearch.stories.js │ └── SearchBoxWithCustomAIRender.stories.js ├── utils │ └── renderItem.js └── styles.css ├── .gitignore ├── README.md ├── package.json ├── examples └── KNNSearch │ └── index.js └── LICENSE /.nvmrc: -------------------------------------------------------------------------------- 1 | v18 2 | -------------------------------------------------------------------------------- /.storybook/styles.css: -------------------------------------------------------------------------------- 1 | .hljs { 2 | display: inline; 3 | } 4 | -------------------------------------------------------------------------------- /stories/reactivemaps/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appbaseio/playground/HEAD/stories/reactivemaps/placeholder.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log.* 3 | .DS_Store 4 | storybook-static/ 5 | .yarn-error.log 6 | npm-debug.log* 7 | yarn-error.log 8 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveElement/index.js: -------------------------------------------------------------------------------- 1 | import Basic from "./Basic"; 2 | import WithStream from "./WithStream"; 3 | import WithTheme from "./WithTheme"; 4 | 5 | module.exports = { 6 | Basic, 7 | WithStream, 8 | WithTheme 9 | }; 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # playground 2 | 3 | A storybook playground for ReactiveMaps and ReactiveSearch projects. 4 | 5 | # Running Locally 6 | 7 | ``` 8 | npm run storybook 9 | ``` 10 | 11 | # Deploying 12 | 13 | ``` 14 | npm run deploy-storybook 15 | ``` 16 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ["../stories/index.js"], 3 | addons: [ 4 | "@storybook/addon-knobs", 5 | "storybook-readme", 6 | "@storybook/addon-actions", 7 | "@storybook/addon-a11y", 8 | ], 9 | reactOptions: { legacyRootApi: false }, 10 | }; 11 | -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import "./styles.css"; 2 | import { addons } from "@storybook/addons"; 3 | import { create } from "@storybook/theming/create"; 4 | 5 | const theme = create({ 6 | base: "light", 7 | brandTitle: "ReactiveSearch", 8 | brandUrl: "https://github.com/appbaseio/playground", 9 | }); 10 | 11 | addons.setConfig({ 12 | panelPosition: "bottom", 13 | theme, 14 | }); 15 | -------------------------------------------------------------------------------- /stories/reactivesearch/CustomRecentIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CustomRecentIcon = () => ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default CustomRecentIcon; 16 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | module: { 3 | rules: [ 4 | { 5 | test: /\.css$/, 6 | use: ["style-loader", "css-loader"] 7 | }, 8 | { 9 | test: /\.(ttf|eot|png|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, 10 | use : "file-loader" 11 | }, 12 | { 13 | test: /\.m?js$/, 14 | exclude: /(node_modules|bower_components)/, 15 | use: { 16 | loader: 'babel-loader', 17 | options: { 18 | presets: ["@babel/preset-env", "@babel/preset-react"], 19 | } 20 | } 21 | } 22 | ] 23 | }, 24 | externals: ["ws"] 25 | } 26 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveElement/helper.js: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | 3 | export var GetTopTopics = function (data) { 4 | const store = {}; 5 | let topics = []; 6 | data.forEach((singleData) => { 7 | singleData._source.group.group_topics.forEach((topic) => { 8 | store[topic.topic_name] = store[topic.topic_name] ? store[topic.topic_name] + 1 : 1; 9 | }); 10 | }); 11 | for (const topic in store) { 12 | const obj = { 13 | name: topic, 14 | value: store[topic] 15 | }; 16 | topics.push(obj); 17 | } 18 | topics = _.sortBy(topics, "value").reverse(); 19 | return topics; 20 | }; 21 | -------------------------------------------------------------------------------- /stories/reactivesearch/CustomPopularIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CustomPopularIcon = () => ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default CustomPopularIcon; 16 | -------------------------------------------------------------------------------- /stories/reactivemaps/Img.js: -------------------------------------------------------------------------------- 1 | import { default as React, Component } from "react"; 2 | 3 | export class Img extends Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | src: this.props.src 8 | }; 9 | this.stopImg = false; 10 | } 11 | 12 | componentDidMount() { 13 | const self = this; 14 | const defaultSrc = "https://s-media-cache-ak0.pinimg.com/216x146/27/b2/da/27b2da4789262e3b828a8ec6587dd8aa.jpg"; 15 | 16 | this.img = new Image(); 17 | 18 | this.img.onerror = function() { 19 | self.setState({ 20 | src: defaultSrc 21 | }); 22 | }; 23 | 24 | this.img.src = this.state.src; 25 | } 26 | 27 | componentWillUnmount() { 28 | this.stopImg = true; 29 | } 30 | 31 | render() { 32 | return ; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /stories/reactivesearch/Img.js: -------------------------------------------------------------------------------- 1 | import { default as React, Component } from "react"; 2 | 3 | export class Img extends Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | src: this.props.src 8 | }; 9 | this.stopImg = false; 10 | } 11 | 12 | componentDidMount() { 13 | const self = this; 14 | this.img = new Image(); 15 | const defaultSrc = "https://s-media-cache-ak0.pinimg.com/216x146/27/b2/da/27b2da4789262e3b828a8ec6587dd8aa.jpg"; 16 | this.img.onerror = function () { 17 | self.setState({ 18 | src: defaultSrc 19 | }, () => { 20 | 21 | }); 22 | }; 23 | this.img.src = this.state.src; 24 | } 25 | 26 | componentWillUnmount() { 27 | this.stopImg = true; 28 | } 29 | 30 | render() { 31 | return ; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /stories/utils/renderItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const renderBookItemWithDate = (suggestion) => { 4 | if (suggestion._suggestion_type === 'document') { 5 | return ( 6 |
7 | {suggestion._source.original_title} 8 | 16 | {new Date(Number(suggestion._source._timestamp) * 1000).toDateString() || ''} 17 | 18 |
19 | ); 20 | } 21 | return null; 22 | }; 23 | 24 | export const renderBookItemWithLink = (suggestion) => { 25 | return ( 26 | 38 | {suggestion._source.original_title} 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /stories/reactivesearch/SingleList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SelectedFilters, SingleList, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksReactiveList } from "./resultViews"; 5 | 6 | export default class SingleListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 22 |
23 | 24 |
25 | 26 | 37 |
38 |
39 |
40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /stories/reactivesearch/MultiDropdownList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SelectedFilters, MultiDropdownList, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksReactiveList } from "./resultViews"; 5 | 6 | export default class MultiDropdownListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 22 |
23 | 24 |
25 | 26 | 37 |
38 |
39 |
40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /stories/reactivesearch/SingleDropdownList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SelectedFilters, SingleDropdownList, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksReactiveList } from "./resultViews"; 5 | 6 | export default class SingleDropdownListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 22 |
23 | 24 |
25 | 26 | 37 |
38 |
39 |
40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import { addDecorator } from "@storybook/react"; 2 | import { withKnobs } from "@storybook/addon-knobs"; 3 | 4 | import { addReadme } from "storybook-readme"; 5 | 6 | addDecorator(withKnobs); 7 | addDecorator(addReadme); 8 | export const parameters = { 9 | a11y: { 10 | element: "#storybook-root", 11 | config: {}, 12 | options: {}, 13 | manual: true, 14 | }, 15 | }; 16 | 17 | 18 | /** 19 | * Workaround for knobs not updating. Look at github issue for more details. 20 | * https://github.com/storybookjs/addon-knobs/issues/19 21 | * 22 | */ 23 | let currentPath; 24 | let timeout = 500; 25 | 26 | if (window.parent) { 27 | const parentWindow = window.parent; 28 | parentWindow.setInterval(function () { 29 | const urlParams = new URLSearchParams(parentWindow.location.search); 30 | const path = urlParams.get('path'); 31 | 32 | if (path && path !== currentPath) { 33 | currentPath = path; 34 | 35 | const knobButtons = parentWindow.document.querySelectorAll( 36 | '#panel-tab-content button', 37 | ); 38 | 39 | if (knobButtons.length) { 40 | const resetButton = knobButtons[knobButtons.length - 1]; 41 | resetButton.click(); 42 | }else{ 43 | currentPath = "" 44 | } 45 | } 46 | }, timeout); 47 | } 48 | -------------------------------------------------------------------------------- /stories/reactivesearch/ResponsiveStory.js: -------------------------------------------------------------------------------- 1 | var $ = require("jquery"); 2 | 3 | var ResponsiveStory = function ResponsiveStory() { 4 | var paginationHeight = function paginationHeight() { 5 | return $(".rbc-pagination").length * 85; 6 | }; 7 | 8 | var getHeight = function getHeight(item) { 9 | return item.height() ? item.height() : 0; 10 | }; 11 | 12 | var handleResponsive = function handleResponsive() { 13 | var height = $(window).height(); 14 | var resultHeight = height - 15; 15 | $(".rbc.rbc-reactivelist, .rbc.rbc-reactiveelement").css({ 16 | maxHeight: resultHeight 17 | }); 18 | var $component = [$(".rbc.rbc-singlelist"), $(".rbc.rbc-multilist"), $(".rbc.rbc-nestedlist"), $(".rbc.rbc-tagcloud")]; 19 | $component.forEach(function (item) { 20 | if (item.length) { 21 | var itemHeader = getHeight(item.find(".rbc-title")) + getHeight(item.find(".rbc-search-container")); 22 | item.find(".rbc-list-container").css({ maxHeight: height - itemHeader - 35 }); 23 | } 24 | }); 25 | $(".rbc-base > .row").css({ 26 | "margin-bottom": 0 27 | }); 28 | $(".rbc-reactivemap .rbc-container").css({ 29 | maxHeight: height 30 | }); 31 | }; 32 | 33 | handleResponsive(); 34 | 35 | $(window).resize(function () { 36 | handleResponsive(); 37 | }); 38 | }; 39 | 40 | export default ResponsiveStory; -------------------------------------------------------------------------------- /stories/reactivesearch/MultiList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SelectedFilters, MultiList, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksReactiveList } from "./resultViews"; 5 | 6 | export default class MultiListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 25 |
26 | 27 |
28 | 29 | 40 |
41 |
42 |
43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, MultiDropdownList, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksReactiveList } from "./resultViews"; 5 | 6 | export default class ReactiveListDefault extends Component { 7 | render() { 8 | const {scrollTargetStory, ...otherProps} = this.props; 9 | const scrollStyle = scrollTargetStory ? ({ 10 | overflow : 'scroll', 11 | height: '80vh' 12 | }) : {}; 13 | return ( 14 | 19 |
20 |
21 | 27 |
28 | 29 |
30 | 42 |
43 |
44 |
45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /stories/reactivesearch/NumberBox.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, NumberBox, ReactiveList, SelectedFilters } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default class NumberBoxDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 21 |
22 |
23 | 24 | 35 | {({ data }) => ( 36 | 37 | { 38 | data.map(item => ) 39 | } 40 | 41 | )} 42 | 43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /stories/reactivesearch/DynamicRangeSlider.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | DynamicRangeSlider, 5 | SelectedFilters, 6 | ReactiveList 7 | } from "@appbaseio/reactivesearch"; 8 | 9 | import { booksList as BooksList } from "./resultViews"; 10 | 11 | export default class DynamicRangeSliderDefault extends Component { 12 | render() { 13 | return ( 14 | 19 |
20 |
21 | 26 |
27 | 28 |
29 | 30 | 41 | {({ data }) => ( 42 | 43 | { 44 | data.map(item => ) 45 | } 46 | 47 | )} 48 | 49 |
50 |
51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /stories/reactivesearch/ErrorBoundary.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | DynamicRangeSlider, 5 | SelectedFilters, 6 | ReactiveList, 7 | ErrorBoundary 8 | } from "@appbaseio/reactivesearch"; 9 | 10 | import { booksList as BooksList } from "./resultViews"; 11 | 12 | export default class ErrorBoundaryDefault extends Component { 13 | render() { 14 | return ( 15 | 20 |
21 |
22 | 23 | 27 | 28 |
29 | 30 |
31 | 32 | 43 | {({ data }) => ( 44 | 45 | { 46 | data.map(item => ) 47 | } 48 | 49 | )} 50 | 51 |
52 |
53 |
54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /stories/reactivesearch/Dark.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, ReactiveList, SelectedFilters, SearchBox } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default class DarkStoriesComponent extends Component { 7 | render() { 8 | return ( 9 | 15 |
16 |
17 | 18 |
19 | 24 |
25 |
26 | 27 |
28 | 40 | {({ data }) => ( 41 | 42 | { 43 | data.map(item => ) 44 | } 45 | 46 | )} 47 | 48 |
49 |
50 |
51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stories/reactivesearch/RangeSlider.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | RangeSlider, 5 | SelectedFilters, 6 | ReactiveList 7 | } from "@appbaseio/reactivesearch"; 8 | 9 | import { booksList as BooksList } from "./resultViews"; 10 | 11 | export default class RangeSliderDefault extends Component { 12 | render() { 13 | return ( 14 | 19 |
20 |
21 | 34 |
35 | 36 |
37 | 38 | 49 | {({ data }) => ( 50 | 51 | { 52 | data.map(item => ) 53 | } 54 | 55 | )} 56 | 57 |
58 |
59 |
60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /stories/reactivesearch/RangeInput.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | RangeInput, 5 | SelectedFilters, 6 | ReactiveList 7 | } from "@appbaseio/reactivesearch"; 8 | 9 | import { booksList as BooksList } from "./resultViews"; 10 | 11 | export default class RangeInputDefault extends Component { 12 | render() { 13 | return ( 14 | 19 |
20 |
21 | 34 |
35 | 36 |
37 | 38 | 50 | {({ data }) => ( 51 | 52 | { 53 | data.map(item => ) 54 | } 55 | 56 | )} 57 | 58 |
59 |
60 |
61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /stories/reactivesearch/TagCloud.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, TagCloud, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { meetupList as MeetupList } from "./resultViews"; 5 | 6 | export default class TagCloudDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 22 |
23 |
24 | 25 | 42 | {({ data }) => ( 43 | 44 | { 45 | data.map(item => ) 46 | } 47 | 48 | )} 49 | 50 |
51 |
52 |
53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /stories/reactivesearch/ResultCard.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SingleDropdownRange, ResultCard, ReactiveList } from "@appbaseio/reactivesearch"; 3 | import { booksCard as BooksCard } from "./resultViews"; 4 | 5 | export default class ResultCardDefault extends Component { 6 | render() { 7 | return ( 8 | 13 |
14 |
15 | 4" }] 23 | } 24 | /> 25 |
26 |
27 | 37 | { 38 | ({ data }) => ( 39 | 40 | { 41 | data.map(item => ) 42 | } 43 | 44 | ) 45 | } 46 | 47 |
48 |
49 |
50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stories/reactivesearch/MultiRange.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, MultiRange, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksCard as BooksCard } from "./resultViews"; 5 | 6 | export default class MultiRangeDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 4" }] 23 | } 24 | {...this.props} 25 | /> 26 |
27 |
28 | 29 | 39 | { 40 | ({ data }) => ( 41 | 42 | { 43 | data.map(item => ) 44 | } 45 | 46 | ) 47 | } 48 | 49 |
50 |
51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /stories/reactivesearch/SingleRange.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SingleRange, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksCard as BooksCard } from "./resultViews"; 5 | 6 | export default class SingleRangeDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 4" }] 23 | } 24 | {...this.props} 25 | /> 26 |
27 |
28 | 29 | 39 | { 40 | ({ data }) => ( 41 | 42 | { 43 | data.map(item => ) 44 | } 45 | 46 | ) 47 | } 48 | 49 |
50 |
51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /stories/reactivesearch/ResultList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SingleDropdownRange, ResultList, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default class ResultListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 4" }] 24 | } 25 | /> 26 |
27 |
28 | 40 | {({ data }) => ( 41 | 42 | { 43 | data.map(item => ) 44 | } 45 | 46 | )} 47 | 48 |
49 |
50 |
51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stories/reactivesearch/MultiDropdownRange.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, MultiDropdownRange, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksCard as BooksCard } from "./resultViews"; 5 | 6 | export default class MultiDropdownRangeDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 4" }] 23 | } 24 | {...this.props} 25 | /> 26 |
27 |
28 | 29 | 39 | { 40 | ({ data }) => ( 41 | 42 | { 43 | data.map(item => ) 44 | } 45 | 46 | ) 47 | } 48 | 49 |
50 |
51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /stories/reactivesearch/SearchBox.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SearchBox, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default class SearchBoxDefault extends Component { 7 | render() { 8 | return ( 9 | 19 |
20 |
21 | 27 | 28 |
29 |
30 | 41 | {({ data }) => ( 42 | 43 | { 44 | data.map(item => ) 45 | } 46 | 47 | )} 48 | 49 |
50 |
51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /stories/reactivesearch/SingleDropdownRange.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SingleDropdownRange, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | import { booksCard as BooksCard } from "./resultViews"; 4 | 5 | export default class SingleDropdownRangeDefault extends Component { 6 | render() { 7 | return ( 8 | 13 |
14 |
15 | 4" }] 22 | } 23 | {...this.props} 24 | /> 25 |
26 |
27 | 28 | 38 | { 39 | ({ data }) => ( 40 | 41 | { 42 | data.map(item => ) 43 | } 44 | 45 | ) 46 | } 47 | 48 |
49 |
50 |
51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stories/reactivesearch/MultiDataList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, MultiDataList, ReactiveList, SelectedFilters } from "@appbaseio/reactivesearch"; 3 | 4 | import { meetupList as MeetupList } from "./resultViews"; 5 | 6 | export default class MultiDataListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 27 |
28 |
29 | 30 | 44 | {({ data }) => ( 45 | 46 | { 47 | data.map(item => ) 48 | } 49 | 50 | )} 51 | 52 |
53 |
54 |
55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /stories/reactivesearch/SingleDataList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SingleDataList, ReactiveList, SelectedFilters } from "@appbaseio/reactivesearch"; 3 | 4 | import { meetupList as MeetupList } from "./resultViews"; 5 | 6 | export default class SingleDataListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 27 |
28 |
29 | 30 | 44 | {({ data }) => ( 45 | 46 | { 47 | data.map(item => ) 48 | } 49 | 50 | )} 51 | 52 |
53 |
54 |
55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /stories/reactivesearch/ToggleButton.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, ToggleButton, ReactiveList, SelectedFilters } from "@appbaseio/reactivesearch"; 3 | 4 | import { meetupList as MeetupList } from "./resultViews"; 5 | 6 | export default class ToggleButtonDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 | 26 |
27 |
28 | 29 | 46 | {({ data }) => ( 47 | 48 | { 49 | data.map(item => ) 50 | } 51 | 52 | )} 53 | 54 |
55 |
56 |
57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /stories/reactivesearch/RatingsFilter.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, RatingsFilter, ReactiveList, SelectedFilters, ResultCard } from "@appbaseio/reactivesearch"; 3 | import ResponsiveStory from "./ResponsiveStory"; 4 | import { booksCard as BooksCard } from './resultViews'; 5 | 6 | export default class RatingsFilterDefault extends Component { 7 | 8 | componentDidMount() { 9 | ResponsiveStory(); 10 | } 11 | 12 | render() { 13 | return ( 14 | 19 |
20 |
21 | 22 | 1 stars' }, 31 | ]} 32 | {...this.props} 33 | /> 34 |
35 | 36 |
37 | 48 | { 49 | ({ data }) => ( 50 | 51 | { 52 | data.map(item => ( 53 | 54 | )) 55 | } 56 | 57 | ) 58 | } 59 | 60 |
61 |
62 |
63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveElement/Basic.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, MultiList, ReactiveElement } from "@appbaseio/reactivesearch"; 3 | import ResponsiveStory from "../ResponsiveStory"; 4 | import { Img } from "../Img.js"; 5 | 6 | require("../list.css"); 7 | 8 | export default class Basic extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.cityQuery = this.cityQuery.bind(this); 12 | this.DEFAULT_IMAGE = "http://www.avidog.com/wp-content/uploads/2015/01/BellaHead082712_11-50x65.jpg"; 13 | } 14 | 15 | cityQuery(value) { 16 | if (value) { 17 | const field = "group.group_city.group_city_simple"; 18 | const query = JSON.parse(`{"${field}":${JSON.stringify(value)}}`); 19 | return { terms: query }; 20 | } return null; 21 | } 22 | 23 | componentDidMount() { 24 | ResponsiveStory(); 25 | } 26 | 27 | render() { 28 | return ( 29 | 34 |
35 |
36 | 47 |
48 | 49 |
50 | 61 |
62 |
63 |
64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "version": "1.0.0", 4 | "description": "Reactive storybook playground", 5 | "main": "index.js", 6 | "scripts": { 7 | "storybook": "start-storybook -p 9009", 8 | "build-storybook": "build-storybook", 9 | "deploy-storybook": "storybook-to-ghpages" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/appbaseio/playground.git" 14 | }, 15 | "author": "metagrover", 16 | "license": "Apache-2.0", 17 | "bugs": { 18 | "url": "https://github.com/appbaseio/reactive-playground/issues" 19 | }, 20 | "homepage": "https://github.com/appbaseio/reactive-playground#readme", 21 | "dependencies": { 22 | "@appbaseio/reactivemaps": "4.0.0", 23 | "@appbaseio/reactivesearch": "4.3.0", 24 | "docs": "appbaseio/Docs#dev", 25 | "moment": "^2.29.1", 26 | "react": "^18.2.0", 27 | "react-dom": "^18.2.0", 28 | "remarkable": "^2.0.1" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "^7.19.6", 32 | "@babel/preset-env": "^7.19.4", 33 | "@babel/preset-react": "^7.18.6", 34 | "@storybook/addon-a11y": "^6.5.16", 35 | "@storybook/addon-actions": "^6.5.16", 36 | "@storybook/addon-info": "^6.0.0-alpha.2", 37 | "@storybook/addon-knobs": "^6.4.0", 38 | "@storybook/addon-options": "^6.0.0-alpha.29", 39 | "@storybook/react": "^6.5.16", 40 | "@storybook/storybook-deployer": "^2.8.16", 41 | "babel-loader": "^8.2.2", 42 | "css-loader": "^0.27.3", 43 | "file-loader": "^4.0.0", 44 | "jquery": "^3.4.1", 45 | "json-loader": "^0.5.4", 46 | "materialize-css": "^0.98.1", 47 | "raw-loader": "^0.5.1", 48 | "storybook-readme": "^5.0.9", 49 | "sumoselect": "^3.0.3" 50 | }, 51 | "engines": { 52 | "node": ">=14.17.1" 53 | } 54 | } -------------------------------------------------------------------------------- /stories/reactivesearch/SearchBoxControlledUsage.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useState } from "react"; 2 | import { ReactiveBase, SearchBox, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default function SearchBoxControlledUsage(props) { 7 | const [value, setValue] = useState("") 8 | const {enableRecentSuggestions} = props 9 | return ( 10 | 19 |
20 |
21 | { 26 | v?setValue(v): setValue("") 27 | if(props.shouldTriggerQueryWhileTyping){ 28 | triggerQuery() 29 | } 30 | }} 31 | searchboxId={props.enableFAQSuggestions?"rs_docs":undefined} 32 | {...props} 33 | /> 34 | 35 |
36 |
37 | 48 | {({ data }) => ( 49 | 50 | { 51 | data.map(item => ) 52 | } 53 | 54 | )} 55 | 56 |
57 |
58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /stories/reactivesearch/AIAnswer.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SearchBox, SelectedFilters, ReactiveList, AIAnswer } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default class AIAnswerxDefault extends Component { 7 | render() { 8 | return ( 9 | 20 | 25 | 26 |
27 | 28 |
29 | 40 | {({ data }) => ( 41 | 42 | { 43 | data.map(item => ) 44 | } 45 | 46 | )} 47 | 48 |
49 |
50 | 56 |
57 |
58 |
59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /stories/reactivemaps/ReactiveGoogleMap.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | SingleList, 5 | } from "@appbaseio/reactivesearch"; 6 | 7 | import { ReactiveGoogleMap } from '@appbaseio/reactivemaps'; 8 | 9 | import historyPin from "./placeholder.png"; 10 | 11 | export default class ReactiveGoogleMapDefault extends Component { 12 | constructor(props) { 13 | super(props); 14 | this.onPopoverClick = this.onPopoverClick.bind(this); 15 | } 16 | 17 | onPopoverClick(marker) { 18 | return (
19 |

20 | Earthquake (at) {marker.place}  21 | of maginutde: {marker.mag} in the year {marker.time}. 22 |

23 |
); 24 | } 25 | 26 | render() { 27 | return ( 28 | 34 |
35 |
36 |
37 | 44 |
45 |
46 |
47 | ({ 56 | label: result.mag, 57 | // icon: 'https://i.imgur.com/NHR2tYL.png', 58 | // custom: (
{result.mag}
), 59 | })} 60 | defaultCenter={{ 61 | lat: -23.944, 62 | lng: -70.093, 63 | }} 64 | react={{ 65 | and: ["CitySensor", "VenueSensor"] 66 | }} 67 | {...this.props} 68 | /> 69 |
70 |
71 |
72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /stories/reactivemaps/ReactiveOpenStreetMapDefault.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | SingleList, 5 | } from "@appbaseio/reactivesearch"; 6 | 7 | import { ReactiveOpenStreetMap } from '@appbaseio/reactivemaps'; 8 | 9 | import { Img } from "./Img.js"; 10 | import historyPin from "./placeholder.png"; 11 | 12 | export default class ReactiveOpenStreetMapDefault extends Component { 13 | constructor(props) { 14 | super(props); 15 | this.onPopoverClick = this.onPopoverClick.bind(this); 16 | } 17 | 18 | onPopoverClick(marker) { 19 | return (
20 |

21 | Earthquake (at) {marker.place}  22 | of maginutde: {marker.mag} in the year {marker.time}. 23 |

24 |
); 25 | } 26 | 27 | render() { 28 | return ( 29 | 35 |
36 |
37 |
38 | 45 |
46 |
47 |
48 | ({ 57 | label: result.mag, 58 | // icon: 'https://i.imgur.com/NHR2tYL.png', 59 | // custom: (
{result.mag}
), 60 | })} 61 | defaultCenter={{ 62 | lat: -23.944, 63 | lng: -70.093, 64 | }} 65 | react={{ 66 | and: ["CitySensor", "VenueSensor"] 67 | }} 68 | {...this.props} 69 | /> 70 |
71 |
72 |
73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /stories/reactivesearch/TreeList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, TreeList, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default class TreeListDefault extends Component { 7 | booksReactiveList(data) { 8 | return ( 9 |
10 | Book Cover 11 |
12 |
{data.name}
13 |
14 |
15 |
16 | {data.class} > {data.subclass} 17 |
18 |
19 | Sale price: {data.salePrice} 20 |
21 |
22 |
23 |
24 |
25 | ); 26 | } 27 | 28 | render() { 29 | return ( 30 | 35 |
36 |
37 | 38 | 50 |
51 |
52 | 71 |
72 |
73 |
74 | ); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /stories/reactivesearch/MultiListWithIndexProp.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SelectedFilters, MultiList, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksReactiveList } from "./resultViews"; 5 | 6 | export default class MultiListDefault extends Component { 7 | render() { 8 | return ( 9 | 14 |
15 |
16 |
17 | {/* all queries triggereing from this component will be tragetted to the 'good-books-clone' index in the BE as it is specified 18 | through the index prop */} 19 | 32 |
33 |
34 | {/* as no index is specified in this component, by default all the queries made will be targetted to the 35 | index provided in seachbase component */} 36 | 48 |
49 |
50 | 51 |
52 | 53 | 64 |
65 |
66 |
67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /stories/reactivemaps/GeoDistanceDropDownOpenStreetMap.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | SelectedFilters, 5 | } from '@appbaseio/reactivesearch'; 6 | import { 7 | ReactiveOpenStreetMap, 8 | GeoDistanceDropdown, 9 | } from "@appbaseio/reactivemaps"; 10 | 11 | import { Img } from "./Img.js"; 12 | import historyPin from "./placeholder.png"; 13 | 14 | export default class GeoDistanceDropdownDefault extends Component { 15 | constructor(props) { 16 | super(props); 17 | // this.onPopoverTrigger = this.onPopoverTrigger.bind(this); 18 | } 19 | 20 | onPopoverTrigger(marker) { 21 | return (
22 | 23 | 24 | 25 |
26 |
27 | {marker._source.member.member_name} 28 |
29 |
30 |

is going to  31 | 32 | {marker._source.event.event_name} 33 | 34 |

35 |
36 |
37 |
); 38 | } 39 | 40 | render() { 41 | return ( 42 | 49 |
50 |
51 | 52 | 57 |
58 |
59 | 71 |
72 |
73 |
74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /stories/reactivemaps/GeoDistanceDropdownGoogleMap.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | SelectedFilters, 5 | } from '@appbaseio/reactivesearch'; 6 | import { 7 | ReactiveGoogleMap, 8 | GeoDistanceDropdown, 9 | } from "@appbaseio/reactivemaps"; 10 | 11 | import { Img } from "./Img.js"; 12 | import historyPin from "./placeholder.png"; 13 | 14 | export default class GeoDistanceDropdownDefault extends Component { 15 | constructor(props) { 16 | super(props); 17 | // this.onPopoverTrigger = this.onPopoverTrigger.bind(this); 18 | } 19 | 20 | onPopoverTrigger(marker) { 21 | return (
22 | 23 | 24 | 25 |
26 |
27 | {marker._source.member.member_name} 28 |
29 |
30 |

is going to  31 | 32 | {marker._source.event.event_name} 33 | 34 |

35 |
36 |
37 |
); 38 | } 39 | 40 | render() { 41 | return ( 42 | 49 |
50 |
51 | 52 | 57 |
58 |
59 | 73 |
74 |
75 |
76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /stories/reactivesearch/SearchBoxWithIndexProp.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SearchBox, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | export default class SearchBoxDefault extends Component { 7 | render() { 8 | return ( 9 | 17 |
18 |
19 |
20 | {/* as no index is specified in this component, by default all the queries made will be targetted to the 21 | index provided in seachbase component */} 22 | 32 |
33 |
34 | {/* all queries triggereing from this component will be tragetted to the 'good-books-clone' index in the BE as it is specified 35 | through the index prop */} 36 | 47 |
48 |
49 |
50 | 61 | {({ data }) => ( 62 | 63 | { 64 | data.map(item => ) 65 | } 66 | 67 | )} 68 | 69 |
70 |
71 |
72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /stories/reactivemaps/GeoDistanceSliderGoogleMap.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | SelectedFilters, 5 | } from '@appbaseio/reactivesearch'; 6 | import { 7 | ReactiveGoogleMap, 8 | GeoDistanceSlider, 9 | } from "@appbaseio/reactivemaps"; 10 | 11 | import { Img } from "./Img.js"; 12 | import historyPin from "./placeholder.png"; 13 | 14 | export default class GeoDistanceSliderDefault extends Component { 15 | constructor(props) { 16 | super(props); 17 | // this.onPopoverTrigger = this.onPopoverTrigger.bind(this); 18 | } 19 | 20 | onPopoverTrigger(marker) { 21 | return (
22 | 23 | 24 | 25 |
26 |
27 | {marker._source.member.member_name} 28 |
29 |
30 |

is going to  31 | 32 | {marker._source.event.event_name} 33 | 34 |

35 |
36 |
37 |
); 38 | } 39 | 40 | render() { 41 | return ( 42 | 49 |
50 |
51 | 52 | 61 |
62 |
63 | 77 |
78 |
79 |
80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /stories/reactivemaps/GeoDistanceSliderOpenStreetMap.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ReactiveBase, 4 | SelectedFilters, 5 | } from '@appbaseio/reactivesearch'; 6 | import { 7 | ReactiveOpenStreetMap, 8 | GeoDistanceSlider, 9 | } from "@appbaseio/reactivemaps"; 10 | 11 | import { Img } from "./Img.js"; 12 | import historyPin from "./placeholder.png"; 13 | 14 | export default class GeoDistanceSliderDefault extends Component { 15 | constructor(props) { 16 | super(props); 17 | // this.onPopoverTrigger = this.onPopoverTrigger.bind(this); 18 | } 19 | 20 | onPopoverTrigger(marker) { 21 | return (
22 | 23 | 24 | 25 |
26 |
27 | {marker._source.member.member_name} 28 |
29 |
30 |

is going to  31 | 32 | {marker._source.event.event_name} 33 | 34 |

35 |
36 |
37 |
); 38 | } 39 | 40 | render() { 41 | return ( 42 | 49 |
50 |
51 | 52 | 61 |
62 |
63 | 77 |
78 |
79 |
80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveComponentWithDistinctFieldProp.js: -------------------------------------------------------------------------------- 1 | /* eslint react/prop-types: 0 */ 2 | import React, { Component } from 'react'; 3 | 4 | import { 5 | ReactiveBase, 6 | ReactiveComponent, 7 | ReactiveList, 8 | SelectedFilters, 9 | } from '@appbaseio/reactivesearch'; 10 | 11 | export default class ReactiveComponentDefault extends Component { 12 | renderItem(data) { 13 | return ( 14 |
15 |

{data.name}

16 |

{data.price} - {data.rating} stars rated

17 |
18 | ); 19 | } 20 | render() { 21 | return ( 22 | 27 |
28 |
29 | 30 | ({ 33 | aggs: { 34 | 'brand.keyword': { 35 | terms: { 36 | field: 'brand.keyword', 37 | order: { 38 | _count: 'desc', 39 | }, 40 | size: 1, 41 | }, 42 | }, 43 | }, 44 | })} 45 | distinctField="brand.keyword" 46 | distinctFieldConfig={{ 47 | inner_hits: { 48 | name: 'most_recent', 49 | size: 5, 50 | sort: [{ timestamp: 'asc' }], 51 | }, 52 | max_concurrent_group_searches: 4, 53 | }} 54 | size={10} 55 | {...this.props} 56 | > 57 | {({ data, setQuery }) => } 58 | 59 |
60 | 61 |
62 | 74 |
75 |
76 |
77 | ); 78 | } 79 | } 80 | 81 | class CustomComponent extends Component { 82 | setValue(value) { 83 | this.props.setQuery({ 84 | query: { 85 | term: { 86 | "brand.keyword": value, 87 | }, 88 | }, 89 | value, 90 | }); 91 | } 92 | 93 | render() { 94 | if (this.props.data) { 95 | return this.props.data.map(item => ( 96 |
this.setValue(item.brand)}>{item.brand}
97 | )); 98 | } 99 | return null; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /stories/styles.css: -------------------------------------------------------------------------------- 1 | .row { 2 | display: flex; 3 | flex-direction: row; 4 | width: 100%; 5 | } 6 | 7 | .row.dark { 8 | background: #303030; 9 | } 10 | 11 | .col { 12 | width: calc(100% - 400px); 13 | padding: 15px; 14 | } 15 | .container{ 16 | padding: 1.5rem; 17 | } 18 | .row > .col:first-child { 19 | border-right: 1px solid #ccc; 20 | max-width: 400px; 21 | } 22 | 23 | .result-list-container { 24 | max-width: 800px; 25 | } 26 | 27 | .row > .col:last-child { 28 | background: #fafafa; 29 | } 30 | 31 | .row.dark > .col:last-child { 32 | background: #303030; 33 | } 34 | 35 | .flex { 36 | display: flex; 37 | } 38 | 39 | .wrap { 40 | flex-wrap: wrap; 41 | } 42 | 43 | .column { 44 | flex-direction: column; 45 | } 46 | 47 | .authors-list { 48 | color: #9d9d9d; 49 | font-weight: bold; 50 | } 51 | 52 | .dark .authors-list { 53 | color: #fafafa; 54 | } 55 | 56 | .ratings-list { 57 | padding: 10px 0; 58 | } 59 | 60 | .avg-rating { 61 | color: #6b6b6b; 62 | margin-left: 5px; 63 | } 64 | 65 | .dark .avg-rating { 66 | color: #fafafa; 67 | } 68 | 69 | .align-center { 70 | align-items: center; 71 | } 72 | 73 | .justify-center { 74 | justify-content: center; 75 | } 76 | 77 | .justify-space-between { 78 | justify-content: space-between; 79 | } 80 | 81 | .stars { 82 | color: gold; 83 | } 84 | 85 | .location { 86 | color: salmon; 87 | margin-right: 5px; 88 | } 89 | 90 | .meetup-location { 91 | margin: 4px 0; 92 | } 93 | 94 | .book-title { 95 | white-space: normal; 96 | margin-top: 4px; 97 | } 98 | 99 | .book-title-card { 100 | white-space: normal; 101 | margin-top: 4px; 102 | max-height: 45px; 103 | } 104 | 105 | .book-image { 106 | height: 150px; 107 | width: 110px; 108 | background-size: cover; 109 | } 110 | 111 | .book-header { 112 | font-weight: bold; 113 | margin-bottom: 5px; 114 | } 115 | 116 | .book-content { 117 | background: white; 118 | margin: 10px 0; 119 | box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); 120 | } 121 | 122 | .meetup-title { 123 | white-space: normal; 124 | } 125 | 126 | .meetup-topics { 127 | height: 35px; 128 | overflow: hidden; 129 | } 130 | 131 | .text-center { 132 | text-align: center; 133 | } 134 | 135 | .meetup-topic { 136 | background-color: #dedede; 137 | color: #555; 138 | padding: 5px 10px; 139 | margin: 5px; 140 | border-radius: 4px; 141 | } 142 | 143 | .meetup-topic:first-child { 144 | margin-left: 0; 145 | } 146 | 147 | .col .meetup-list-image { 148 | background-size: cover; 149 | } 150 | 151 | .recent-icon { 152 | height: 20px; 153 | width: 20px; 154 | fill: royalblue; 155 | } 156 | .popular-icon { 157 | height: 20px; 158 | width: 20px; 159 | fill: rgb(253, 44, 16); 160 | } 161 | -------------------------------------------------------------------------------- /stories/reactivesearch/DarkCard.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, NumberBox, RangeInput, SelectedFilters, SingleDropdownRange, MultiDropdownRange, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksCard as BooksCard } from "./resultViews"; 5 | 6 | export default class ResultCardDefault extends Component { 7 | render() { 8 | return ( 9 | 15 |
16 |
17 | 18 |
19 | 29 |
30 | 43 |
44 | 4" }] 52 | } 53 | /> 54 |
55 | 4" }] 63 | } 64 | /> 65 |
66 |
72 | 83 | { 84 | ({ data }) => ( 85 | 86 | { 87 | data.map(item => ) 88 | } 89 | 90 | ) 91 | } 92 | 93 |
94 |
95 |
96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveComponent.stories.js: -------------------------------------------------------------------------------- 1 | /* eslint react/prop-types: 0 */ 2 | import React, { Component } from 'react'; 3 | 4 | import { 5 | ReactiveBase, 6 | ReactiveComponent, 7 | ReactiveList, 8 | SelectedFilters, 9 | } from '@appbaseio/reactivesearch'; 10 | 11 | export default class ReactiveComponentDefault extends Component { 12 | renderItem(data) { 13 | return ( 14 |
15 |

{data.name}

16 |

{data.price} - {data.rating} stars rated

17 |
18 | ); 19 | } 20 | triggerRef = React.createRef(null); 21 | render() { 22 | return ( 23 | 28 | {this.test} 29 |
30 |
31 | { 32 | if (props === 'CarSensor' && this.triggerRef.current) { 33 | this.triggerRef.current({ 34 | query: { 35 | "match_all": {} 36 | }, 37 | value: null 38 | }); 39 | } 40 | ref={testRef} // testRef.current would hold the ref of SelectedFitlers 41 | 42 | }} /> 43 | ({ 46 | aggs: { 47 | 'brand.keyword': { 48 | terms: { 49 | field: 'brand.keyword', 50 | order: { 51 | _count: 'desc', 52 | }, 53 | size: 10, 54 | }, 55 | }, 56 | }, 57 | })} 58 | {...this.props} 59 | > 60 | {({ aggregations, setQuery }) => { 61 | if (!this.triggerRef.current) { 62 | this.triggerRef.current = setQuery; 63 | } 64 | return 65 | }} 66 | 67 |
68 | 69 |
70 | 82 |
83 |
84 |
85 | ); 86 | } 87 | } 88 | 89 | class CustomComponent extends Component { 90 | setValue(value) { 91 | this.props.setQuery({ 92 | query: { 93 | term: { 94 | "brand.keyword": value, 95 | }, 96 | }, 97 | value, 98 | }); 99 | } 100 | 101 | render() { 102 | if (this.props.aggregations) { 103 | return this.props.aggregations['brand.keyword'].buckets.map(item => ( 104 |
this.setValue(item.key)}>{item.key}
105 | )); 106 | } 107 | return null; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /stories/reactivesearch/TagCloudDark.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, TagCloud, SingleDataList, MultiDataList, ToggleButton, ReactiveList, SelectedFilters } from "@appbaseio/reactivesearch"; 3 | 4 | import { meetupList as MeetupList } from "./resultViews"; 5 | 6 | export default class TagCloudDefault extends Component { 7 | render() { 8 | return ( 9 | 15 |
16 |
17 | 27 |
28 | 39 |
40 | 51 |
52 | 58 |
59 |
60 | 61 | 78 | {({ data }) => ( 79 | 80 | { 81 | data.map(item => ) 82 | } 83 | 84 | )} 85 | 86 |
87 |
88 |
89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /stories/reactivesearch/TabDataList.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, ReactiveList, SelectedFilters, TabDataList } from "@appbaseio/reactivesearch"; 3 | 4 | import { meetupList as MeetupList } from "./resultViews"; 5 | 6 | const HorizontalLayout = (props) =>( 7 |
8 | 9 | 20 | 34 | {({ data }) => ( 35 | 36 | { 37 | data.map(item => ) 38 | } 39 | 40 | )} 41 | 42 |
43 | ) 44 | 45 | const VerticalLayout = (props)=>( 46 | <> 47 | 48 |
49 |
50 | 61 |
62 |
63 | 77 | {({ data }) => ( 78 | 79 | { 80 | data.map(item => ) 81 | } 82 | 83 | )} 84 | 85 |
86 |
87 | 88 | ) 89 | 90 | export default class TabDataListDefault extends Component { 91 | render() { 92 | return ( 93 | 98 | {this.props.displayAsVertical ? : } 99 | 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /stories/reactivesearch/DatePickerDark.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import moment from "moment"; 3 | import { 4 | ReactiveBase, 5 | DatePicker, 6 | ReactiveList, 7 | SelectedFilters, 8 | ResultCard, 9 | } from "@appbaseio/reactivesearch"; 10 | import ResponsiveStory from "./ResponsiveStory"; 11 | 12 | export default class DatePickerDefault extends Component { 13 | componentDidMount() { 14 | ResponsiveStory(); 15 | } 16 | 17 | dateQuery(value, props) { 18 | let query = null; 19 | if (value) { 20 | query = [ 21 | { 22 | range: { 23 | [props.dataField]: { 24 | lte: moment(value).valueOf(), 25 | }, 26 | }, 27 | }, 28 | ]; 29 | } 30 | return query ? { query: { bool: { must: query } } } : null; 31 | } 32 | 33 | render() { 34 | return ( 35 | 41 |
42 |
43 | 51 |
52 | 53 |
54 | 55 | 66 | {({ data }) => ( 67 | 68 | {data.map((item) => ( 69 | 70 | 71 | 74 | 75 |
76 |
${item.price}
77 | 82 |

83 | {item.room_type} · {item.accommodates} guests 84 |

85 |
86 |
87 |
88 | ))} 89 |
90 | )} 91 |
92 |
93 |
94 |
95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /stories/reactivesearch/DatePicker.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import moment from "moment"; 3 | import { 4 | ReactiveBase, 5 | DatePicker, 6 | ReactiveList, 7 | SelectedFilters, 8 | ResultCard, 9 | } from "@appbaseio/reactivesearch"; 10 | import ResponsiveStory from "./ResponsiveStory"; 11 | 12 | export default class DatePickerDefault extends Component { 13 | componentDidMount() { 14 | ResponsiveStory(); 15 | } 16 | 17 | dateQuery(value, props) { 18 | let query = null; 19 | if (value) { 20 | query = [ 21 | { 22 | range: { 23 | [props.dataField]: { 24 | lte: moment(value).valueOf(), 25 | }, 26 | }, 27 | }, 28 | ]; 29 | } 30 | return query ? { query: { bool: { must: query } } } : null; 31 | } 32 | 33 | render() { 34 | return ( 35 | 41 |
42 |
43 | 52 |
53 | 54 |
55 | 56 | 66 | {({ data }) => ( 67 | 68 | {data.map((item) => ( 69 | 70 | 71 | 76 | 77 |
78 |
${item.price}
79 | 84 |

85 | {item.room_type} · {item.accommodates} guests 86 |

87 |
88 |
89 |
90 | ))} 91 |
92 | )} 93 |
94 |
95 |
96 |
97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveElement/WithStream.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, MultiList, ReactiveElement } from "@appbaseio/reactivesearch"; 3 | import { AppbaseSensorHelper as helper } from "@appbaseio/reactivesearch"; 4 | import ResponsiveStory from "../ResponsiveStory"; 5 | import { GetTopTopics } from "./helper"; 6 | import { Img } from "../Img.js"; 7 | 8 | require("../list.css"); 9 | 10 | export default class WithStream extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.cityQuery = this.cityQuery.bind(this); 14 | this.onAllData = this.onAllData.bind(this); 15 | this.DEFAULT_IMAGE = "http://www.avidog.com/wp-content/uploads/2015/01/BellaHead082712_11-50x65.jpg"; 16 | } 17 | 18 | cityQuery(value) { 19 | if (value) { 20 | const field = "group.group_city.group_city_simple"; 21 | const query = JSON.parse(`{"${field}":${JSON.stringify(value)}}`); 22 | return { terms: query }; 23 | } return null; 24 | } 25 | 26 | componentDidMount() { 27 | ResponsiveStory(); 28 | } 29 | 30 | onAllData(res, err) { 31 | let result = null; 32 | if (res && res.appliedQuery) { 33 | let combineData = res.currentData; 34 | if (res.mode === "historic") { 35 | combineData = res.currentData.concat(res.newData); 36 | } else if (res.mode === "streaming") { 37 | combineData = helper.combineStreamData(res.currentData, res.newData); 38 | } 39 | if (combineData) { 40 | combineData = GetTopTopics(combineData); 41 | const resultMarkup = combineData.map((data, index) => { 42 | if (index < 5) { 43 | return this.itemMarkup(data, index); 44 | } 45 | }); 46 | result = ( 47 |
48 | 49 | 50 | {resultMarkup} 51 | 52 |
53 |
54 | ); 55 | } 56 | } 57 | return result; 58 | } 59 | 60 | itemMarkup(data, index) { 61 | return ( 62 | 63 | {data.name} 64 | {data.value} 65 | 66 | ); 67 | } 68 | 69 | 70 | render() { 71 | return ( 72 | 77 |
78 |
79 | 92 |
93 | 94 |
95 | 107 |
108 |
109 |
110 | ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /stories/reactivesearch/ReactiveElement/WithTheme.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, MultiList, ReactiveElement } from "@appbaseio/reactivesearch"; 3 | import { AppbaseSensorHelper as helper } from "@appbaseio/reactivesearch"; 4 | import ResponsiveStory from "../ResponsiveStory"; 5 | import { GetTopTopics } from "./helper"; 6 | import { Img } from "../Img.js"; 7 | 8 | require("../list.css"); 9 | 10 | export default class WithTheme extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.cityQuery = this.cityQuery.bind(this); 14 | this.onAllData = this.onAllData.bind(this); 15 | this.DEFAULT_IMAGE = "http://www.avidog.com/wp-content/uploads/2015/01/BellaHead082712_11-50x65.jpg"; 16 | } 17 | 18 | cityQuery(value) { 19 | if (value) { 20 | const field = "group.group_city.group_city_simple"; 21 | const query = JSON.parse(`{"${field}":${JSON.stringify(value)}}`); 22 | return { terms: query }; 23 | } return null; 24 | } 25 | 26 | componentDidMount() { 27 | ResponsiveStory(); 28 | } 29 | 30 | onAllData(res, err) { 31 | let result = null; 32 | if (res && res.appliedQuery) { 33 | let combineData = res.currentData; 34 | if (res.mode === "historic") { 35 | combineData = res.currentData.concat(res.newData); 36 | } else if (res.mode === "streaming") { 37 | combineData = helper.combineStreamData(res.currentData, res.newData); 38 | } 39 | if (combineData) { 40 | combineData = GetTopTopics(combineData); 41 | const resultMarkup = combineData.map((data, index) => { 42 | if (index < 5) { 43 | return this.itemMarkup(data, index); 44 | } 45 | }); 46 | result = ( 47 |
48 | 49 | 50 | {resultMarkup} 51 | 52 |
53 |
54 | ); 55 | } 56 | } 57 | return result; 58 | } 59 | 60 | itemMarkup(data, index) { 61 | return ( 62 | 63 | {data.name} 64 | {data.value} 65 | 66 | ); 67 | } 68 | 69 | 70 | render() { 71 | return ( 72 | 78 |
79 |
80 | 92 |
93 | 94 |
95 | 107 |
108 |
109 |
110 | ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /stories/reactivesearch/DateRange.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import moment from "moment"; 3 | import { 4 | ReactiveBase, 5 | DateRange, 6 | ResultCard, 7 | ReactiveList, 8 | SelectedFilters, 9 | } from "@appbaseio/reactivesearch"; 10 | import ResponsiveStory from "./ResponsiveStory"; 11 | 12 | export default class DateRangeDefault extends Component { 13 | componentDidMount() { 14 | ResponsiveStory(); 15 | } 16 | 17 | dateQuery(value) { 18 | let query = null; 19 | if (value) { 20 | query = [ 21 | { 22 | range: { 23 | available_from: { 24 | gte: moment(value.start).valueOf(), 25 | }, 26 | }, 27 | }, 28 | { 29 | range: { 30 | available_to: { 31 | lte: moment(value.end).valueOf(), 32 | }, 33 | }, 34 | }, 35 | ]; 36 | } 37 | return query ? { query: { bool: { must: query } } } : null; 38 | } 39 | 40 | render() { 41 | return ( 42 | 47 |
48 |
49 | 58 |
59 | 60 |
61 | 62 | 72 | {({ data }) => ( 73 | 74 | {data.map((item) => ( 75 | 76 | 77 | 82 | 83 |
84 |
${item.price}
85 | 90 |

91 | {item.room_type} · {item.accommodates} guests 92 |

93 |
94 |
95 |
96 | ))} 97 |
98 | )} 99 |
100 |
101 |
102 |
103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /stories/reactivesearch/DateRangeDark.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import moment from "moment"; 3 | import { 4 | ReactiveBase, 5 | DateRange, 6 | ResultCard, 7 | SelectedFilters, 8 | ReactiveList, 9 | } from "@appbaseio/reactivesearch"; 10 | import ResponsiveStory from "./ResponsiveStory"; 11 | 12 | export default class DateRangeDefault extends Component { 13 | componentDidMount() { 14 | ResponsiveStory(); 15 | } 16 | 17 | dateQuery(value) { 18 | let query = null; 19 | if (value) { 20 | query = [ 21 | { 22 | range: { 23 | available_from: { 24 | gte: moment(value.start).valueOf(), 25 | }, 26 | }, 27 | }, 28 | { 29 | range: { 30 | available_to: { 31 | lte: moment(value.end).valueOf(), 32 | }, 33 | }, 34 | }, 35 | ]; 36 | } 37 | return query ? { query: { bool: { must: query } } } : null; 38 | } 39 | 40 | render() { 41 | return ( 42 | 48 |
49 |
50 | 58 |
59 | 60 |
61 | 62 | 73 | {({ data }) => ( 74 | 75 | {data.map((item) => ( 76 | 77 | 78 | 83 | 84 |
85 |
${item.price}
86 | 91 |

92 | {item.room_type} · {item.accommodates} guests 93 |

94 |
95 |
96 |
97 | ))} 98 |
99 | )} 100 |
101 |
102 |
103 |
104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /stories/reactivesearch/list.css: -------------------------------------------------------------------------------- 1 | #map, 2 | .h-100, 3 | body, 4 | html { 5 | height: 100%; 6 | } 7 | 8 | .rbc-base > .row { 9 | position: absolute; 10 | width: 100%; 11 | height: 100%; 12 | } 13 | 14 | .rbc-base > .row > .col.s6 { 15 | position: relative; 16 | height: 100%; 17 | } 18 | 19 | .rbc-base > .row > .col.s6:first-child { 20 | border-right: 1px solid #cdcdcd; 21 | } 22 | 23 | .rbc-base > .row > .col.s6:first-child::after, 24 | .rbc-base > .row > .col.s6:last-child::after { 25 | content: 'Actuator'; 26 | display: block; 27 | position: absolute; 28 | text-align: center; 29 | color: #fff; 30 | font-size: 12px; 31 | letter-spacing: 0.04em; 32 | top: 0; 33 | right: 0; 34 | padding: 2px 10px; 35 | text-transform: uppercase; 36 | background-color: #bcbcbc; 37 | border-bottom-left-radius: 5px; 38 | } 39 | 40 | .rbc-base > .row > .col.s6:first-child::after { 41 | content: 'Sensor'; 42 | } 43 | 44 | .rbc-base > .row.reverse-labels > .col.s6:first-child::after { 45 | content: 'Actuator'; 46 | } 47 | 48 | .rbc-base > .row.reverse-labels > .col.s6:last-child::after { 49 | content: 'Sensor'; 50 | } 51 | 52 | .listResult { 53 | height: 100%; 54 | position: relative; 55 | padding: 0; 56 | margin: 0; 57 | overflow: auto; 58 | } 59 | 60 | .makerInfo { 61 | margin-bottom: 15px; 62 | font-size: 12px; 63 | } 64 | 65 | .full_row { 66 | display: block; 67 | width: 100%; 68 | position: relative; 69 | } 70 | 71 | .single-record { 72 | height: 70px; 73 | } 74 | 75 | .single-record .img-container { 76 | position: absolute; 77 | left: 10px; 78 | top: 10px; 79 | width: 50px; 80 | height: 50px; 81 | overflow: hidden; 82 | border-radius: 8px; 83 | } 84 | 85 | .img-container img { 86 | width: 100%; 87 | } 88 | 89 | .single-record .text-container { 90 | padding-left: 70px; 91 | padding-top: 10px; 92 | font-size: 16px; 93 | line-height: 25px 94 | } 95 | 96 | .single-record .text-description { 97 | color: #aaa 98 | } 99 | 100 | .text-overflow { 101 | overflow: hidden; 102 | text-overflow: ellipsis; 103 | white-space: nowrap; 104 | } 105 | 106 | .highlight_tags { 107 | display: block; 108 | float: left; 109 | padding-left: 0; 110 | list-style-position: inside; 111 | overflow: hidden; 112 | max-height: 24px; 113 | margin: 0; 114 | } 115 | 116 | .highlight_tags li { 117 | float: left; 118 | padding-right: 10px; 119 | } 120 | 121 | .text-head-city { 122 | position: absolute; 123 | right: 10px; 124 | top: 0; 125 | font-size: 12px; 126 | line-height: 25px; 127 | color: #aaa; 128 | font-weight: bold; 129 | } 130 | 131 | .text-head-info { 132 | display: block; 133 | width: 100%; 134 | padding-right: 100px; 135 | position: relative; 136 | } 137 | 138 | .single-record .text-description { 139 | position: relative; 140 | padding-right: 100px; 141 | } 142 | .single-record .sort-info { 143 | position: absolute; 144 | right: 5px; 145 | font-size: 10px; 146 | } 147 | 148 | .rbc-highlight-table th, .rbc-highlight-table td{ 149 | padding: 5px; 150 | vertical-align: top; 151 | } 152 | 153 | .rbc-highlight-table th { 154 | font-weight: lighter; 155 | color: #aaa; 156 | } 157 | 158 | .animate { 159 | -webkit-animation: fadein 2s; /* Safari, Chrome and Opera > 12.1 */ 160 | -moz-animation: fadein 2s; /* Firefox < 16 */ 161 | -ms-animation: fadein 2s; /* Internet Explorer */ 162 | -o-animation: fadein 2s; /* Opera < 12.1 */ 163 | animation: fadein 2s; 164 | } 165 | 166 | @keyframes fadein { 167 | from { opacity: 0; } 168 | to { opacity: 1; } 169 | } 170 | 171 | /* Firefox < 16 */ 172 | @-moz-keyframes fadein { 173 | from { opacity: 0; } 174 | to { opacity: 1; } 175 | } 176 | 177 | /* Safari, Chrome and Opera > 12.1 */ 178 | @-webkit-keyframes fadein { 179 | from { opacity: 0; } 180 | to { opacity: 1; } 181 | } 182 | 183 | /* Internet Explorer */ 184 | @-ms-keyframes fadein { 185 | from { opacity: 0; } 186 | to { opacity: 1; } 187 | } 188 | 189 | /* Opera < 12.1 */ 190 | @-o-keyframes fadein { 191 | from { opacity: 0; } 192 | to { opacity: 1; } 193 | } 194 | -------------------------------------------------------------------------------- /stories/reactivesearch/resultViews/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ResultCard, ResultList } from '@appbaseio/reactivesearch'; 3 | 4 | export const booksList = (data) => ( 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 |
14 |
by {data.authors}
15 |
16 | 17 | { 18 | Array(data.average_rating_rounded).fill('x') 19 | .map((item, index) => ) 20 | } 21 | 22 | ({data.average_rating} avg) 23 |
24 |
25 | Pub {data.original_publication_year} 26 |
27 |
28 | 29 | 30 | ); 31 | 32 | export const booksCard = (data) => ( 33 | 34 | 35 |
36 | 37 |
38 |
39 |
by {data.authors}
40 |
41 | 42 | { 43 | Array(data.average_rating_rounded).fill('x') 44 | .map((item, index) => ) // eslint-disable-line 45 | } 46 | 47 | ({data.average_rating} avg) 48 |
49 |
50 | Pub {data.original_publication_year} 51 |
52 |
53 | 54 | ); 55 | 56 | export const booksReactiveList = (data) => ( 57 |
58 | Book Cover 59 |
60 |
{data.original_title}
61 |
62 |
63 |
by {data.authors}
64 |
65 | 66 | { 67 | Array(data.average_rating_rounded).fill('x') 68 | .map((item, index) => ) // eslint-disable-line 69 | } 70 | 71 | ({data.average_rating} avg) 72 |
73 |
74 | Pub {data.original_publication_year} 75 |
76 |
77 |
78 | ); 79 | 80 | export const meetupList = (data) => ( 81 | 82 | 83 | 84 | 85 |
86 | {data.member ? data.member.member_name : ""} is going to ${data.event ? data.event.event_name : ""} 87 |
88 |
89 | 90 |
91 |
92 | 93 | {data.group ? data.group.group_city : ""} 94 |
95 |
96 | { 97 | data.group.group_topics.slice(0, 4).map(tag => ( 98 |
{tag.topic_name}
99 | )) 100 | } 101 |
102 |
103 |
104 |
105 |
106 | ) 107 | -------------------------------------------------------------------------------- /stories/reactivesearch/TreeListCustomRenderer.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, TreeList, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | 4 | import { booksList as BooksList } from "./resultViews"; 5 | const recLookup = (obj, path) => { 6 | try { 7 | const parts = path.split('.'); 8 | if (parts.length === 1) { 9 | return obj[parts[0]]; 10 | } 11 | return recLookup(obj[parts[0]], parts.slice(1).join('.')); 12 | } catch (e) { 13 | return false; 14 | } 15 | }; 16 | export default class TreeListCustomRenderer extends Component { 17 | booksReactiveList(data) { 18 | return ( 19 |
20 | Book Cover 21 |
22 |
{data.name}
23 |
24 |
25 |
26 | {data.class} > {data.subclass} 27 |
28 |
29 | Sale price: {data.salePrice} 30 |
31 |
32 |
33 |
34 |
35 | ); 36 | } 37 | 38 | renderListItems(listItem, parentPath, selectedValues, handleListItemClick) { 39 | if (!(listItem instanceof Object) || Object.keys(listItem).length === 0) { 40 | return null; 41 | } 42 | const listItemLabel = listItem.key; 43 | const listItemCount = listItem.count; 44 | const isLeafNode = !(Array.isArray(listItem.list) && listItem.list.length > 0); 45 | 46 | let newParentPath = listItemLabel; 47 | if (parentPath) { 48 | newParentPath = `${parentPath}.${listItemLabel}`; 49 | } 50 | const isSelected = recLookup(selectedValues, newParentPath); 51 | 52 | return ( 53 |
  • 54 | {/* eslint-disable jsx-a11y/click-events-have-key-events */} 55 | {/* eslint-disable jsx-a11y/no-static-element-interactions */} 56 | handleListItemClick(listItemLabel, parentPath)} 59 | > 60 | 61 | {listItemLabel} 62 | 63 | {listItemCount} 64 | 65 | 66 | {isLeafNode === false && ( 67 |
    68 | {/* eslint-disable-next-line no-use-before-define */} 69 | {this.renderLists( 70 | listItem.list, 71 | newParentPath, 72 | isSelected, 73 | selectedValues, 74 | handleListItemClick, 75 | )} 76 |
    77 | )} 78 |
  • 79 | ); 80 | } 81 | 82 | renderLists(transformedData, parentPath, isExpanded, selectedValues, handleClick) { 83 | return ( 84 |
      85 | {transformedData.map(listItem => 86 | this.renderListItems(listItem, parentPath, selectedValues, handleClick), 87 | )} 88 |
    89 | ); 90 | } 91 | 92 | render() { 93 | return ( 94 | 99 |
    100 |
    101 | 102 | { 113 | const { 114 | /* eslint-disable no-unused-vars */ 115 | data, 116 | rawData, 117 | error, 118 | handleClick, 119 | value, 120 | loading, 121 | } = propData; 122 | return this.renderLists(data, '', true, value, handleClick); 123 | }} 124 | {...this.props} 125 | /> 126 |
    127 |
    128 | 147 |
    148 |
    149 |
    150 | ); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /stories/reactivesearch/TreeListCustomSelectedFilters.stories.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | import React, { Component } from 'react'; 4 | 5 | import { 6 | ReactiveBase, 7 | TreeList, 8 | ReactiveList, 9 | SelectedFilters, 10 | componentTypes, 11 | } from '@appbaseio/reactivesearch'; 12 | 13 | 14 | class TreeListCustomSelectedFilters extends Component { 15 | render() { 16 | return ( 17 | 22 |
    23 |
    24 | 37 |
    38 | 39 |
    40 | { 42 | const { selectedValues, setValue } = props; 43 | const clearFilter = (component) => { 44 | setValue(component, null); 45 | }; 46 | 47 | const filters = Object.keys(selectedValues).map((component) => { 48 | if (!selectedValues[component].value) return null; 49 | 50 | if ( 51 | selectedValues[component].componentType 52 | === componentTypes.treeList 53 | ) { 54 | const { value } = selectedValues[component]; 55 | const valueArray = value; 56 | 57 | return ( 58 |
    65 | {valueArray.map((valueItem, index) => { 66 | const pathParts = valueItem.split(' > '); 67 | return ( 68 |
    74 | {pathParts.map( 75 | (pathItem, pathItemIndex) => ( 76 | { 78 | const newValueArray = [ 79 | ...valueArray, 80 | ]; 81 | newValueArray[index] 82 | = pathParts.length 83 | === 1 84 | ? '' 85 | : pathParts 86 | .slice( 87 | 0, 88 | pathItemIndex 89 | + 1, 90 | ) 91 | .join( 92 | ' > ', 93 | ); 94 | if (JSON.stringify(newValueArray) === JSON.stringify(valueArray)) { 95 | return; 96 | } 97 | setValue( 98 | component, 99 | newValueArray.filter( 100 | item => 101 | !!item, 102 | ), 103 | ); 104 | }} 105 | > 106 | {pathItem}{' '} 107 | {pathItemIndex 108 | !== pathParts.length - 1 ? ( 109 | 114 | ➤ 115 | 116 | ) : ( 117 | '' 118 | )} 119 | 120 | ), 121 | )} 122 |
    123 | ); 124 | })} 125 |
    126 | ); 127 | } 128 | 129 | return ( 130 | 136 | ); 137 | }); 138 | 139 | return filters; 140 | }} 141 | /> 142 | ({ 153 | track_total_hits: true, 154 | })} 155 | includeFields={['class', 'subclass', 'name', 'image', 'salePrice']} 156 | /> 157 |
    158 |
    159 |
    160 | ); 161 | } 162 | 163 | reactiveList(data) { 164 | return ( 165 |
    166 | Book Cover 167 |
    168 |
    {data.name}
    169 |
    170 |
    171 |
    172 | 173 | {data.class} > {data.subclass} 174 | 175 |
    176 |
    177 | Sale Price 💰 {data.salePrice} 178 |
    179 |
    180 |
    181 |
    182 |
    183 | ); 184 | } 185 | } 186 | 187 | 188 | export default TreeListCustomSelectedFilters; 189 | -------------------------------------------------------------------------------- /examples/KNNSearch/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | ReactiveBase, 4 | SearchBox, 5 | MultiList, 6 | ReactiveList, 7 | } from "@appbaseio/reactivesearch"; 8 | import styled from "@emotion/styled"; 9 | import { FaUsers, FaBuilding } from "react-icons/fa"; 10 | 11 | const Container = styled.div` 12 | max-width: 1000px; 13 | margin: 0 auto; 14 | padding: 2rem; 15 | font-family: Arial, sans-serif; 16 | `; 17 | 18 | const Layout = styled.div` 19 | display: flex; 20 | gap: 2rem; 21 | `; 22 | 23 | const FacetContainer = styled.div` 24 | width: 250px; 25 | `; 26 | 27 | const ResultsContainer = styled.div` 28 | flex: 1; 29 | `; 30 | 31 | const ResultItem = styled.div` 32 | display: flex; 33 | align-items: flex-start; 34 | padding: 1rem 0; 35 | border-bottom: 1px solid #ddd; 36 | `; 37 | 38 | const Logo = styled.img` 39 | width: 50px; 40 | height: 50px; 41 | object-fit: contain; 42 | margin-right: 1rem; 43 | `; 44 | 45 | const Info = styled.div` 46 | flex: 1; 47 | `; 48 | 49 | const CompanyName = styled.h3` 50 | margin: 0; 51 | font-size: 1.25rem; 52 | `; 53 | 54 | const OneLiner = styled.p` 55 | margin: 0.5rem 0; 56 | display: -webkit-box; 57 | -webkit-line-clamp: 2; 58 | -webkit-box-orient: vertical; 59 | overflow: hidden; 60 | `; 61 | 62 | const Meta = styled.div` 63 | display: flex; 64 | gap: 1rem; 65 | font-size: 0.9rem; 66 | color: #555; 67 | `; 68 | 69 | const Tags = styled.div` 70 | margin: 0.5rem 0; 71 | `; 72 | 73 | const Tag = styled.span` 74 | background-color: #f0f0f0; 75 | border-radius: 4px; 76 | padding: 0.25rem 0.5rem; 77 | margin-right: 0.5rem; 78 | `; 79 | 80 | const Link = styled.a` 81 | color: #007bff; 82 | text-decoration: none; 83 | &:hover { 84 | text-decoration: underline; 85 | } 86 | `; 87 | 88 | const KNNWithFaceting = () => { 89 | return ( 90 | 95 | 96 |

    K-Nearest Neighbors Search with Facets

    97 | 106 | 107 | 108 | 119 | 120 | 121 | ( 142 | <> 143 | {data.map((item) => { 144 | const company = item._source || item; 145 | return ( 146 | 147 | 151 | 152 | {company.name} 153 | 154 | {company.one_liner || company.long_description} 155 | 156 | 157 | 158 | {company.team_size} 159 | 160 | 161 | {company.stage} 162 | 163 | 164 | 165 | {company.industries && 166 | company.industries.map((ind) => ( 167 | {ind} 168 | ))} 169 | 170 | 175 | Visit website 176 | 177 | 178 | 179 | ); 180 | })} 181 | 182 | )} 183 | renderNoResults={() =>
    No results found
    } 184 | /> 185 |
    186 |
    187 |
    188 |
    189 | ); 190 | }; 191 | 192 | export default KNNWithFaceting; -------------------------------------------------------------------------------- /stories/reactivesearch/KNNSearch.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | ReactiveBase, 4 | SearchBox, 5 | MultiList, 6 | ReactiveList, 7 | } from "@appbaseio/reactivesearch"; 8 | import styled from "@emotion/styled"; 9 | import { FaUsers, FaBuilding } from "react-icons/fa"; 10 | 11 | const Container = styled.div` 12 | max-width: 1000px; 13 | margin: 0 auto; 14 | padding: 2rem; 15 | font-family: Arial, sans-serif; 16 | `; 17 | 18 | const Layout = styled.div` 19 | display: flex; 20 | gap: 2rem; 21 | `; 22 | 23 | const FacetContainer = styled.div` 24 | width: 250px; 25 | `; 26 | 27 | const ResultsContainer = styled.div` 28 | flex: 1; 29 | `; 30 | 31 | const ResultItem = styled.div` 32 | display: flex; 33 | align-items: flex-start; 34 | padding: 1rem 0; 35 | border-bottom: 1px solid #ddd; 36 | `; 37 | 38 | const Logo = styled.img` 39 | width: 50px; 40 | height: 50px; 41 | object-fit: contain; 42 | margin-right: 1rem; 43 | `; 44 | 45 | const Info = styled.div` 46 | flex: 1; 47 | `; 48 | 49 | const CompanyName = styled.h3` 50 | margin: 0; 51 | font-size: 1.25rem; 52 | `; 53 | 54 | const OneLiner = styled.p` 55 | margin: 0.5rem 0; 56 | display: -webkit-box; 57 | -webkit-line-clamp: 2; 58 | -webkit-box-orient: vertical; 59 | overflow: hidden; 60 | `; 61 | 62 | const Meta = styled.div` 63 | display: flex; 64 | gap: 1rem; 65 | font-size: 0.9rem; 66 | color: #555; 67 | `; 68 | 69 | const Tags = styled.div` 70 | margin: 0.5rem 0; 71 | `; 72 | 73 | const Tag = styled.span` 74 | background-color: #f0f0f0; 75 | border-radius: 4px; 76 | padding: 0.25rem 0.5rem; 77 | margin-right: 0.5rem; 78 | `; 79 | 80 | const Link = styled.a` 81 | color: #007bff; 82 | text-decoration: none; 83 | &:hover { 84 | text-decoration: underline; 85 | } 86 | `; 87 | 88 | const KNNSearchDefault = (props) => { 89 | const { candidates, vectorDataField } = props; 90 | return ( 91 | 97 | 98 |

    K-Nearest Neighbors Search with Facets

    99 | 108 | 109 | 110 | 121 | 122 | 123 | ( 144 | <> 145 | {data.map((item) => { 146 | const company = item._source || item; 147 | return ( 148 | 149 | 153 | 154 | {company.name} 155 | 156 | {company.one_liner || company.long_description} 157 | 158 | 159 | 160 | {company.team_size} 161 | 162 | 163 | {company.stage} 164 | 165 | 166 | 167 | {company.industries && 168 | company.industries.map((ind) => ( 169 | {ind} 170 | ))} 171 | 172 | 177 | Visit website 178 | 179 | 180 | 181 | ); 182 | })} 183 | 184 | )} 185 | renderNoResults={() =>
    No results found
    } 186 | /> 187 |
    188 |
    189 |
    190 |
    191 | ); 192 | }; 193 | 194 | export default KNNSearchDefault; 195 | -------------------------------------------------------------------------------- /stories/reactivesearch/SearchBoxWithCustomAIRender.stories.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { ReactiveBase, SearchBox, SelectedFilters, ReactiveList } from "@appbaseio/reactivesearch"; 3 | import { Remarkable } from 'remarkable'; 4 | import { booksList as BooksList } from "./resultViews"; 5 | 6 | 7 | const md = new Remarkable(); 8 | 9 | md.set({ 10 | html: true, 11 | breaks: true, 12 | xhtmlOut: true, 13 | linkify: true, 14 | linkTarget: '_blank', 15 | }); 16 | 17 | const globalStyles = ` 18 | html, 19 | body, 20 | div, 21 | span, 22 | applet, 23 | object, 24 | iframe, 25 | h1, 26 | h2, 27 | h3, 28 | h4, 29 | h5, 30 | h6, 31 | p, 32 | blockquote, 33 | pre, 34 | a, 35 | abbr, 36 | acronym, 37 | address, 38 | big, 39 | cite, 40 | code, 41 | del, 42 | dfn, 43 | em, 44 | img, 45 | ins, 46 | kbd, 47 | q, 48 | s, 49 | samp, 50 | small, 51 | strike, 52 | strong, 53 | sub, 54 | sup, 55 | tt, 56 | var, 57 | b, 58 | u, 59 | i, 60 | center, 61 | dl, 62 | dt, 63 | dd, 64 | ol, 65 | ul, 66 | li, 67 | fieldset, 68 | form, 69 | label, 70 | legend, 71 | table, 72 | caption, 73 | tbody, 74 | tfoot, 75 | thead, 76 | tr, 77 | th, 78 | td, 79 | article, 80 | aside, 81 | canvas, 82 | details, 83 | embed, 84 | figure, 85 | figcaption, 86 | footer, 87 | header, 88 | hgroup, 89 | menu, 90 | nav, 91 | output, 92 | ruby, 93 | section, 94 | summary, 95 | time, 96 | mark, 97 | audio, 98 | video { 99 | margin: 0; 100 | padding: 0; 101 | border: 0; 102 | font-size: 100%; 103 | font: inherit; 104 | vertical-align: baseline; 105 | } 106 | 107 | pre { 108 | margin: 10px auto; 109 | } 110 | 111 | table { 112 | margin: 10px auto; 113 | border-collapse: collapse; 114 | border-spacing: 0; 115 | } 116 | 117 | tr { 118 | border-bottom: 1px solid #ccc; 119 | } 120 | 121 | th, 122 | td { 123 | text-align: left; 124 | padding: 4px; 125 | border: 1px solid; 126 | border-collapse: collapse; 127 | } 128 | 129 | pre, 130 | code { 131 | padding: 0.6em 0.4em; 132 | /* Insert background color */ 133 | } 134 | 135 | pre { 136 | /* Insert text color */ 137 | white-space: pre-wrap; 138 | } 139 | 140 | code { 141 | line-height: normal; 142 | /* Insert text color */ 143 | border-radius: 3px; 144 | font-size: 85%; 145 | padding: 0.2em 0.4em; 146 | margin-top: 5px; 147 | display: inline-block; 148 | overflow: auto; 149 | width: fit-content; 150 | max-width: 100%; 151 | } 152 | 153 | /* Replace 'props.isSender', 'props.themePreset', and 'props.theme.colors' with actual values */ 154 | 155 | code[class*='language-'], 156 | pre[class*='language-'] { 157 | /* Insert text color */ 158 | text-shadow: none; 159 | } 160 | 161 | ul, 162 | ol { 163 | padding-left: 1rem; 164 | } 165 | 166 | p { 167 | margin: 8px auto; 168 | } 169 | `; 170 | 171 | export const GlobalStyles = () => ( 172 |