Skip to content

Commit

Permalink
Add support for lazy loading (#1)
Browse files Browse the repository at this point in the history
* Add support for lazy loading

* Newline

* Link to rubygems

* Remove debug logging
  • Loading branch information
f-ewald authored Jun 28, 2023
1 parent 0259b98 commit 0e476cd
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 15 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 0.0.3
* Added support for lazy data loading via `lazy` attribute.
* Performance improvement for placeholder calculation

# 0.0.2
* First working release
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A plugin for Jekyll to search for all posts and rank them by relevance. Relevanc

## Installation

Install the plugin via `bundler` in the latest version:
Install the plugin via `bundler` from [rubygems](https://rubygems.org/gems/jekyll_ranked_search) in the latest version:

```shell
bundle add jekyll_ranked_search
Expand All @@ -27,10 +27,31 @@ In your template, add the following lines:
<script type="module" src="/js/search.js"></script>
<!-- Place this line where you want to render the search box -->
<search-box></search-box>
<search-box
lazy="true"></search-box>
```

You only need to add the `<script>` tag once. It is recommended to add it to the header. The search box will fill 100% of the width of its parent container. The `<search-box>` tag is a [WebComponent](-97https://www.webcomponents.org) that uses [Lit](https://lit.dev) and follows W3C standards and are available in all [modern browswers](https://caniuse.com/custom-elementsv1).

## Configuration
No configuration is currently possible.

### Search box
The following settings can be set on the `<search-box>` component on the frontend.

|Parameter|Description |Type |Default |Possible Values |
|---------|-----------------------------------------------|---------|---------|--------------------------------------------|
| `lazy` | Lazy load results on first focus of search box| Boolean | `false` | `false` ,`true` (see important note below) |

Example:

```html
<search-box
lazy="true"></search-box>
```

**Important** The presence of a boolean attribute is interpreted as `true`. Therefore, if you wish to set it to false, remove the attribute completely instead of setting it to `false`.

### Jekyll
The following values can be set in the `_config.yml` file in the Jekyll folder.

*Currently none*
66 changes: 54 additions & 12 deletions lib/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,38 @@ import 'https://cdn.jsdelivr.net/npm/@github/relative-time-element';

class SearchBox extends LitElement {
static properties = {
_data: {state: true, type: Array},
_isLoading: {state: true, type: Boolean},
_data: {state: false, type: Array},
_results : {state: true, type: Array},
_open: {state: true, type: Boolean},
_placeholder: {state: true, type: String},

// Lazy loading
lazy: {type: Boolean, attribute: true},
};

/**
* Constructor. Sets up default values.
*/
constructor() {
super();

// No data initially loaded
this._data = [];

// Results are initially empty
this._results = [];

// Start in closed state, show no results
this._open = false;

// Default to not loading
this._isLoading = false;

// Disable lazy loading by default
this.lazy = false;

this._placeholder = "Search...";
}

static styles = css`
Expand Down Expand Up @@ -77,16 +99,17 @@ class SearchBox extends LitElement {

connectedCallback() {
super.connectedCallback();
this.loadData();

// Load data if lazy loading is disabled
if (!this.lazy) {
this.loadData();
}

document.addEventListener('click', (event) => {
if (!event.composedPath().includes(this) && this._open) {
this.toggle();
}
});

// Register arrow keys

});
}

toggle() {
Expand All @@ -104,12 +127,20 @@ class SearchBox extends LitElement {
}

async loadData() {
// Set state during loading
this._isLoading = true;
this.updatePlaceholder();

const response = await fetch("/search.json");
const jsonData = await response.json();
jsonData.word2doc = new Map(Object.entries(jsonData.word2doc));
jsonData.bow = new Map(Object.entries(jsonData.bow));
jsonData.tfidf = new Map(Object.entries(jsonData.tfidf));
this._data = jsonData;

// Cleanup state
this._isLoading = false;
this.updatePlaceholder();
}

disconnectedCallback() {
Expand Down Expand Up @@ -158,7 +189,6 @@ class SearchBox extends LitElement {
for (const tokenId of tokenIds.slice(1)) {
// Find document candidates
const docCandidates = new Set(this._data.word2doc.get(tokenId.toString()));
// console.log("intersection", docCandidates, docs);
docs = new Set([...docs].filter((x) => docCandidates.has(x)));
}

Expand All @@ -182,21 +212,33 @@ class SearchBox extends LitElement {
this._open = true;
}

placeholder() {
updatePlaceholder() {
if (this._isLoading) {
this._placeholder = "Loading...";
return;
}
if (this._data && this._data.docs && this._data.docs.length > 0) {
let plural = "";
if (this._data.docs.length !== 1) {
plural = "s";
}
return "Search in " + this._data.docs.length + ` post${plural}...`;
} else {
return "Loading...";
this._placeholder = "Search in " + this._data.docs.length + ` post${plural}...`;
return;
}
}

/**
* Event triggered on search box focus.
*/
focus(_) {
if (this.lazy && this._data.length === 0 && !this._isLoading) {
this.loadData();
}
}

render() {
return html`<div>
<input id="q" type="text" placeholder="${this.placeholder()}" @keyup="${this.search}" @click=${this.openIfResults}>
<input id="q" type="text" placeholder="${this._placeholder}" @keyup="${this.search}" @click=${this.openIfResults} @focus=${this.focus}>
${this._open ? html`
<div id="results">
${this._results.map((result) => html`
Expand Down

0 comments on commit 0e476cd

Please sign in to comment.