diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 1e5180c..e6e00ba 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -40,7 +40,7 @@ jobs: cp -R $GITHUB_WORKSPACE/{*.*,assets,coverage,demo,test} . # Modifications touch .nojekyll - cp assets/gh-pages.html index.html + cp gh-pages.html index.html # Push git config user.name "${GITHUB_ACTOR}" && \ git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" && \ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b909f7a..6c19116 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,45 @@ ## Implementation notes -* The `cache` Map lets us instantly display any previous result in the same browser tab. We follow the [LRU strategy](https://en.wikipedia.org/wiki/Least_recently_used). +* **Group by url_without_anchor**. + + One of the ways in which minibar provides different (and arguably, better) default settings compared to DocSearch.js, is the removal of duplicate results from the same page. + + - [Demo: typesense-minibar](https://jquery.github.io/typesense-minibar/) + - [Demo: Comparison to DocSearch.js](https://jquery.github.io/typesense-minibar/demo/compare--docsearch-3.html) + + In minibar, entering "ajax" returns these 5 results: + + > Low-Level Interface + > + > 1. jQuery.ajax() + > 2. jQuery.ajaxTransport() + > + > Global Event Handlers + > + > 3. ajaxComplete event + > 4. ajaxSuccess event + > 5. ajaxError event + + In DocSearch, entering "ajax" returns these 10 results (the first 7 are visible without scrolling): + + * Documentation + 1. jQuery 3.0 Upgrade Guide + 2. jQuery 3.0 Upgrade Guide + 3. jQuery 3.0 Upgrade Guide + 4. jQuery 3.0 Upgrade Guide + 5. jQuery 3.0 Upgrade Guide + * Global Event Handlers + 6. ajaxComplete event + 7. ajaxComplete event + * Global Event Handlers (contd., after scrolling) + 8. ajaxSuccess event + 9. ajaxSuccess event + 10. ajaxError event + +* **The `cache` Map**. + + This lets us instantly display any previous result in the same browser tab. We follow the [LRU strategy](https://en.wikipedia.org/wiki/Least_recently_used). For example, if you type `a`, `app`, `apple`, `apples`, and backspace to a shorter previous query, we instantly show those previous results. (No time wasted waiting for re-download of the same results. It also saves client bandwidth and server load.) Or, if you think another word might yield better results and replace it with `banana`, and return to `apple` because that had a better one, we respond instantly. @@ -12,7 +50,9 @@ If we only add new results and reuse results as-is ([FIFO strategy](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))), that may delete very recently used data. -* The styles for `typesense-minibar` as web component, and `.tsmb-form` class name are kept independent (that is, the web component does not auto-add the class name, nor does it otherwise rely on styles for the class name, and vice versa). +* **Separate CSS selectors for class and web component**. + + The styles for `typesense-minibar` as web component, and `.tsmb-form` class name are kept independent (that is, the web component does not auto-add the class name, nor does it otherwise rely on styles for the class name, and vice versa). This is done for two reasons: diff --git a/assets/gh-pages.html b/assets/gh-pages.html deleted file mode 100644 index 4eb0ef5..0000000 --- a/assets/gh-pages.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - -typesense-minibar - - - -
-

minibar

-

minibar is a fast 2kB autocomplete search bar. Alternative to Algolia DocSearch, InstantSearch, autocomplete-js, and typesense-js.

-

See also

- -
- diff --git a/demo/demo-theme.css b/demo/demo-theme.css index 1f920ba..330cf54 100644 --- a/demo/demo-theme.css +++ b/demo/demo-theme.css @@ -4,7 +4,7 @@ header { background: #390f39; - color: #fff; + color: #fbdbfb; box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); display: flex; @@ -19,6 +19,7 @@ header > * { .demo-logo { background-image: url(../assets/logo-text-dark.svg); + color: #fff; } header .tsmb-form, @@ -27,7 +28,6 @@ header typesense-minibar { --tsmb-color-base30: var(--tsmb-color-primary90); --tsmb-color-base50: #c090c0; --tsmb-color-base90: #c090c0; - --tsmb-size-listbox-width: calc(min(50rem, 80vw)); } header .tsmb-form, header typesense-minibar form { diff --git a/demo/demo.css b/demo/demo.css index 5e41f48..c1adac2 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -42,6 +42,12 @@ main, text-indent: -9999px; overflow: hidden; } +.demo-caption { + flex-grow: 1; +} +.demo-head-right .demo-caption { + text-align: right; +} /* Layout: Push demo-stylecontrol to the bottom. */ body { @@ -53,14 +59,32 @@ main { flex-grow: 1; } -.demo-custom-form { +.tsmb-form.demo-form-size { --tsmb-size-input: 2rem; - --tsmb-size-sm: 0.5rem; --tsmb-size-radius: 0px; + --tsmb-size-listbox-width: calc(min(50rem, 80vw)); + width: 30rem; +} + +html:has(.demo-form-overlay:focus-within) { + /* disable scrollbar */ + overflow: hidden; +} +typesense-minibar.demo-form-overlay:focus-within { + z-index: 1; + position: fixed; + inset: 0px; + background: #f4f4f4; + padding: var(--tsmb-size-sm) 0; +} +typesense-minibar.demo-form-overlay:focus-within form { width: 30rem; + margin: 0 auto; } -.demo-right-form { + +.demo-head-right typesense-minibar, +.demo-head-right .tsmb-form { margin-left: auto; --tsmb-size-listbox-right: 0; } @@ -68,3 +92,11 @@ main { .demo-stylecontrol fieldset { background: #fff; } +@media (min-height: 600px) { + .demo-stylecontrol { + position: sticky; + bottom: 0; + background: #f4f4f4; + box-shadow: 0 -1px 2px rgba(0,0,0,0.24) + } +} diff --git a/demo/index.html b/demo/index.html index 99c5a4c..7ea1926 100644 --- a/demo/index.html +++ b/demo/index.html @@ -19,42 +19,51 @@ + Themed (Web Component) -
- - - -
-
- - - +
+ + Themed (Web Component, group=true, right aligned) +
+
+ + Themed (HTML class, group=true, right aligned) + +
-

Web Component

- +

Web Component: Default

+
-

HTML class

- -

Style API (customize --tsmb-size-* variables)

- + +

Style API (customized --tsmb-size-* variables)

+ diff --git a/index.html b/index.html new file mode 100644 index 0000000..09c75c5 --- /dev/null +++ b/index.html @@ -0,0 +1,34 @@ + + + + +typesense-minibar + + + + + +
+

minibar

+

minibar is a fast 2kB autocomplete search bar. Alternative to Algolia DocSearch, InstantSearch, autocomplete-js, and typesense-js.

+ + +
+ + +
+
+ +

Pages

+ + +

External link

+ +
+ diff --git a/typesense-minibar.css b/typesense-minibar.css index 52ec1c0..4871178 100644 --- a/typesense-minibar.css +++ b/typesense-minibar.css @@ -24,6 +24,8 @@ typesense-minibar { --tsmb-color-primary30: #390f39; --tsmb-color-primary50: #9c3493; --tsmb-color-primary90: #fbdbfb; + + max-width: 100%; } .tsmb-form, @@ -88,7 +90,7 @@ typesense-minibar form::before { content: ''; background: url("data:image/svg+xml,") 0 50% / contain no-repeat; position: absolute; - top: calc(var(--tsmb-size-sm) + var(--tsmb-size-edge)); + top: calc(var(--tsmb-size-edge) + var(--tsmb-size-sm)); left: var(--tsmb-size-sm); width: var(--tsmb-size-base); height: var(--tsmb-size-input); @@ -104,7 +106,7 @@ typesense-minibar form::before { .tsmb-icon-close { box-sizing: border-box; position: absolute; - top: calc(50% - var(--tsmb-size-base)/2); + top: calc(var(--tsmb-size-edge) + var(--tsmb-size-sm) + (var(--tsmb-size-input)/2) - (var(--tsmb-size-base)/2)); right: var(--tsmb-size-base); width: var(--tsmb-size-base); height: var(--tsmb-size-base); diff --git a/typesense-minibar.js b/typesense-minibar.js index fea6add..f7d88fe 100644 --- a/typesense-minibar.js +++ b/typesense-minibar.js @@ -41,7 +41,6 @@ globalThis.tsminibar = function tsminibar (form, dataset = form.dataset) { const query = state.query = input.value; if (!query) { state.hits = []; - state.cursor = -1; return close(); } const hits = await search(query); @@ -62,7 +61,10 @@ globalThis.tsminibar = function tsminibar (form, dataset = form.dataset) { if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) { if (e.code === 'ArrowDown') moveCursor(1); if (e.code === 'ArrowUp') moveCursor(-1); - if (e.code === 'Escape') close(); + if (e.code === 'Escape') { + close(); + input.blur(); + } if (e.code === 'Enter') { const url = state.hits[state.cursor]?.url; if (url) location.href = url; @@ -72,11 +74,12 @@ globalThis.tsminibar = function tsminibar (form, dataset = form.dataset) { form.addEventListener('submit', function (e) { e.preventDefault(); }); - form.insertAdjacentHTML('beforeend', ''); + form.insertAdjacentHTML('beforeend', ''); form.querySelector('.tsmb-icon-close').addEventListener('click', function () { input.value = ''; + state.hits = []; + close() input.focus(); - close(); }); connect();