Alternatively, you can also copy your app’s credentials from your appbase.io apps dashboard. Hover over your app’s card and make sure to copy the read credentials as we shouldn’t be exposing write credentials publicly.

Alternative to above link: Copy the read credentials from apps dashboard

Gitxplore: After adding our first component ReactiveBase

2. CategorySearch

We’ll be using CategorySearch component to search through repositories categorized on the basis of languages. CategorySearch component creates a category search UI widget with an autosuggest functionality. It lets us search across one or more fields easily. Read more about it in the docs.

<CategorySearch

componentId="repo"

dataField={["name", "description", "name.raw", "fullname", "owner", "topics"]}

categoryField="language.raw"

queryFormat="and"

placeholder="Search Repos"

URLParams={true}

/>

dataField specifies the properties on which you want to perform the search and the searches will be categorized using the categoryField prop which takes in the field name from the data browser.

URLParams prop, when set to true, sets a querystring in the URL for the component with the associated value.

The queryFormat prop can take either an and or an or value, the former sets the filter condition to all entered values being present in the results while the latter sets it to any of the selected values being present in the result. We will go with and here.

* The .raw suffix in a field name implies the use of a non-analyzed field. * Notice the table headers of the data browser (which you earlier cloned into your own app), you can see all the appbase data fields here.

Adding our second component, CategorySearch

3. MultiDropdownList

MultiDropdownList is perfect to filter the results based on our repository language . It creates a multiple select dropdown based list UI widget. You can find more about it in the docs.

<MultiDropdownList

componentId="language"

dataField="language.raw"

title="Language"

size={100}

/>

The dataField maps to the language for this component. We’re passing a size prop to control the maximum number of items to display in the list.

Adding a MultiDropdownList component for language

We can use another MultiDropdownList component for the repo topics .

<MultiDropdownList

componentId="topics"

dataField="topics.raw"

title="Repo Topics"

defaultSelected={this.state.topics}

size={1000}

queryFormat="and"

onValueChange={value => this.resetTopic(value)}

/>

We’re mapping the dataField to topics.raw here.

to here. In the defaultSelected prop, we pass currently selected topics that are set in the state.

prop, we pass currently selected topics that are set in the state. The queryFormat prop can take either an and or an or value, the former sets the filter condition to all selected tags being present in the results while the latter sets it to any of the selected tags being present in the result. We will go with and here.

prop can take either an or an value, the former sets the filter condition to all selected tags being present in the results while the latter sets it to any of the selected tags being present in the result. We will go with here. The onValueChange prop accepts a function which is invoked with the current value of the component on each change. We use this to reset the topics in case the user clears them from the MultiDropdownList UI.

resetTopic(topics) {

const nextTopics = topics || [];

this.setState({

topics: nextTopics

});

}

Adding another MultiDropdownList component for topics

4. SingleDropdownRange

We’ll use a SingleDropdownRange to filter out the repos based on the date they were created and the date last pushed at. This component creates a dropdown UI component which can accept a range associated with a particular filter. Read more about it in the docs here.

Here’s how the component will look like for pushed :

<SingleDropdownRange

componentId="pushed"

dataField="pushed"

title="Last Active"

data={[

{"start": "now-1M", "end": "now", "label": "Last 30 days"},

{"start": "now-6M", "end": "now", "label": "Last 6 months"},

{"start": "now-1y", "end": "now", "label": "Last year"}

]}

/>

For this component we’ve mapped the dataField to pushed . The data props takes an array of objects in which we can set different ranges for different start and end values. Here we’re defining ranges according to the dates to create filters for Last 30 days , Last 6 months and Last year . We can use a very simple date format with the current date as now , a month before as now-1M , a year before as now-1y and so on.

Similarly, we can create another component for created and map the dataField to it:

<SingleDropdownRange

componentId="created"

dataField="created"

title="Created"

data={[

{"start": "now-1y", "end": "now", "label": "Last year"},

{"start": "now-3y", "end": "now", "label": "Last 3 years"},

{"start": "now-10y", "end": "now", "label": "All time"},

]}

/>

Here we’re continuing with the same logic for defining different filters using the data prop. We’ve added the filters for Last year , Last 3 years and All time .

Adding SingleDropdownRange components for pushed and created

5. RangeSlider

We’ll be using RangeSlider components to search the repositories on the basis of the number of stars and forks , defining a starting and ending limit. It creates a numeric range slider UI component, very useful for granular filtering of numeric data. Find out more about it in the docs.

<RangeSlider

componentId="stars"

dataField="stars"

title="Repo Stars"

showHistogram={false}

range={{

"start": 0,

"end": 300000

}}

defaultSelected={{

"start": 0,

"end": 300000

}}

rangeLabels={{

"start": "0 Stars",

"end": "300K Stars"

}}

stepValue={100}

/>

We’re mapping the dataField to stars . showHistogram is a handy prop which also shows a histogram over the range slider, but we won’t be needing it here. range accepts an object to specify start and end values. Similarly, we can pass defaultSelected and rangeLabels for these start and end points.

Using the same RangeSlider component we can also create another component for forks and map the dataField to it:

<RangeSlider

componentId="forks"

dataField="forks"

title="Repo Forks"

showHistogram={false}

range={{

"start": 0,

"end": 180000

}}

defaultSelected={{

"start": 0,

"end": 180000

}}

rangeLabels={{

"start": "0 Forks",

"end": "180K Forks"

}}

stepValue={100}

/>

Adding the RangeSlider components for stars and forks

6. ResultCard

Up until now we’ve been creating search components, to filter out the repositories. Now we’ll need a component to display the results according to the selected filters.

ResultCard component creates a UI component which we’ll use to display the result. We can also pass an onData prop to this component to render out the results in custom styles. This will let us easily add styling to individual result cards. Read more about the component here.

<ResultCard

componentId="SearchResult"

dataField="name"

noResults="No results were found, try clearing all the filters."

pagination={true}

size={6}

onData={(res) => this.onData(res, this.toggleTopic)}

react={{

and: ["repo", "topics", "stars", "description", "forks", "pushed", "created", "language"]

}}

sortOptions={[

{

label: "Best Match",

dataField: "_score",

sortBy: "desc"

},

{

label: "Most Stars",

dataField: "stars",

sortBy: "desc"

},

{

label: "Fewest Stars",

dataField: "stars",

sortBy: "asc"

},

{

label: "Most Forks",

dataField: "forks",

sortBy: "desc"

},

{

label: "Fewest Forks",

dataField: "forks",

sortBy: "asc"

},

{

label: "A to Z",

dataField: "owner.raw",

sortBy: "asc"

},

{

label: "Z to A",

dataField: "owner.raw",

sortBy: "desc"

},

{

label: "Recently Updated",

dataField: "pushed",

sortBy: "desc"

},

{

label: "Least Recently Updated",

dataField: "pushed",

sortBy: "asc"

}

]}

/>

ResultCard accepts some useful props like pagination to easily handle splitting the results into pages, each having the number of cards specified by size . You’ll notice a special prop called react here. This accepts an object with the key and , or , not and an array as value with the componentId of all components that it should reactively update to. Here we want the results to update based on the updates to all our search components that we used before.

react={{

and: ["repo", "topics", "stars", "description", "forks", "pushed", "created", "language"]

}}

Read more about the react prop in the docs.

The onData prop accepts a callback function that returns an object with image , title , desc and url keys to render the view for individual cards. We’re using only desc in the returned object here and adding custom styles to it. A detailed description is available in the docs. The onData function for our app looks like this:

onData(res, toggleTopic) {

const result = {

desc: (

<div className="card-layout">

<div className="card-top">

<div className="card-details">

<div className="card-title">

<img src={res.avatar} className="card-image" />

<a href={res.url} target="_blank">

{res.owner}/{res.owner.length + res.name.length > 27 ? <br /> : ''}{res.name}

</a>

</div>

<div className="card-description">

{res.description}

</div>

{

res.topics.length > 0 ?

<div className="card-tags">

{res.topics.slice(0, 7).map(topic => <span className="card-tag" key={`${res.name}-${topic}`} onClick={() => toggleTopic(topic)}>#{topic}</span>)}

</div> :

null

}

</div>

</div>

<div className="card-bottom">

<a href={res.url} target="_blank">

<div className="card-stars">

<i className="fa fa-star" aria-hidden="true" />{res.stars}

</div>

</a>

<a href={res.url} target="_blank">

<div className="card-stars">

<i className="fa fa-code-fork" aria-hidden="true" />{res.forks}

</div>

</a>

<a href={res.url} target="_blank">

<div className="card-stars">

<i className="fa fa-eye" aria-hidden="true" />{res.watchers}

</div>

</a>

</div>

</div>

)

};

return result;

}

We can also add the method to handle toggling this.state.topics when we click on individual topics on the cards:

toggleTopic(topic) {

const topics = [ ...this.state.topics ];

const index = topics.indexOf(topic);

let nextTopics = [];

if (index === -1) {

nextTopics = [ ...topics, topic ];

} else {

nextTopics = topics.slice(0, index).concat(topics.slice(index + 1));

}

this.setState({

topics: nextTopics

});

}

Here we’re checking if the selected topic is present in the state. If it isn’t we add it otherwise we remove it from the state. This in turn update our MultiDropdownList where we passed the selected topics as this.state.topics in the defaultSelected prop.

Now that we’ve pieced up the final part of our app, you should have your own github repo explorer up and running: