From ed42ec389b01575b8820b25a148209893633a6f1 Mon Sep 17 00:00:00 2001 From: upstream Date: Sat, 3 Oct 2026 01:33:54 +0200 Subject: [PATCH] Upstream Pagefind/pagefind state of the touched files before a8baf2727f --- docs/content/docs/components/results.md | 160 +++++++++++++ pagefind_ui/component/components/index.ts | 43 ++++ pagefind_ui/default/svelte/result.svelte | 176 +++++++++++++++ .../default/svelte/result_with_subs.svelte | 211 ++++++++++++++++++ 4 files changed, 590 insertions(+) create mode 100644 docs/content/docs/components/results.md create mode 100644 pagefind_ui/component/components/index.ts create mode 100644 pagefind_ui/default/svelte/result.svelte create mode 100644 pagefind_ui/default/svelte/result_with_subs.svelte diff --git a/docs/content/docs/components/results.md b/docs/content/docs/components/results.md new file mode 100644 index 0000000..c82fafc --- /dev/null +++ b/docs/content/docs/components/results.md @@ -0,0 +1,160 @@ +--- +title: "" +nav_title: "" +nav_section: Components +weight: 20 +--- + +Displays search results with automatic lazy loading. Results load their full data only when scrolled into view. + +
+ +
+ +
+ +
+ +```html + +``` + +## Attributes + +| Attribute | Type | Default | Description | +|-----------|------|---------|-------------| +| `show-images` | boolean | `false` | Show/hide result images | +| `hide-sub-results` | boolean | `false` | Hide anchor-based sub-results | +| `max-sub-results` | number | `3` | Maximum number of sub-results per result | +| `max-results` | number | unlimited | Limit number of results shown | +| `link-target` | string | — | Set `target` attribute on result links (e.g., `_blank` for new tab) | +| `instance` | string | `"default"` | Connect to a specific Pagefind instance | + +## Keyboard Navigation + +When results are focused, use these keys to navigate: + +| Key | Action | +|-----|--------| +| `↓` / `↑` | Navigate between results | +| `Enter` | Follow the selected link | +| `Backspace` | Focus input and delete last character | +| `/` | Focus the search input | +| Any letter | Focus input and type the character | + +## Custom Templates + +Customize result rendering using ` +
+``` + +Your template must have a single root element (e.g. a single `
  • `). Keyboard navigation moves between `` elements in the results list, so ensure your main result link is an `` tag. + +The full templating syntax can be seen in the [adequate-little-templates](https://github.com/bglw/adequate-little-templates?tab=readme-ov-file#syntax) package. + +### Available Data + +Templates receive this data structure: + +| Field | Description | +|-------|-------------| +| `meta.title` | Page title | +| `meta.url` | Page URL | +| `meta.image` | Page image (if set) | +| `meta.image_alt` | Image alt text | +| `meta.*` | Any custom metadata | +| `excerpt` | Search excerpt with `` highlighting (use `{{+ excerpt +}}`) | +| `url` | Fallback URL | +| `sub_results` | Array of matching sections within the page | +| `options.*` | The attributes supplied to the `` element | + +Each sub-result has: `title`, `url`, `excerpt`. + +### Template Filters + +The template engine provides [built-in filters](https://github.com/bglw/adequate-little-templates?tab=readme-ov-file#syntax). The Component UI registers the following additional filters: + +#### `resolveUrl(pageUrl)` + +Resolves a relative URL against a page URL. Absolute URLs are returned unchanged. + +``` +"images/hero.png" | resolveUrl("/blog/post/") → /blog/post/images/hero.png +``` + +### Placeholder Template + +While results are loading, a skeleton placeholder is shown. You can customize it with a second template using `data-template="placeholder"`, as shown in the full example below. The placeholder template receives no data — it is static HTML. + +### Full Template Example + +Here's the built-in template. Copy and customize it: + +```html + + + + +``` + +## Results Layout + +By default, results display in a single column. You can change the layout of the results container using CSS variables, for example a grid layout: + +```css +:root { + --pf-results-display: grid; + --pf-results-columns: repeat(auto-fill, minmax(250px, 1fr)); + --pf-results-gap: 16px; +} +``` + +See [CSS Variables](/docs/css-variables/) for all available layout variables. diff --git a/pagefind_ui/component/components/index.ts b/pagefind_ui/component/components/index.ts new file mode 100644 index 0000000..c9ce0e5 --- /dev/null +++ b/pagefind_ui/component/components/index.ts @@ -0,0 +1,43 @@ +export { getInstanceManager, configureInstance } from "./instance-manager"; + +import { registerFunction } from "adequate-little-templates"; + +registerFunction("resolveUrl", (url, pageUrl) => { + const s = String(url ?? ""); + if (!s || /^[a-z][a-z0-9+.-]*:/i.test(s) || /^\/\//.test(s) || s.startsWith("/")) return s; + try { + return new URL(s, new URL(String(pageUrl ?? "/"), "https://p")).pathname; + } catch { + return s; + } +}); + +import "./pagefind-config"; +import "./pagefind-input"; +import "./pagefind-summary"; +import "./pagefind-results"; +import "./pagefind-filter-pane"; +import "./pagefind-filter-dropdown"; +import "./pagefind-modal"; +import "./pagefind-modal-trigger"; +import "./pagefind-modal-header"; +import "./pagefind-modal-body"; +import "./pagefind-modal-footer"; +import "./pagefind-keyboard-hints"; +import "./pagefind-searchbox"; + +export { PagefindConfig } from "./pagefind-config"; +export { PagefindInput } from "./pagefind-input"; +export { PagefindSummary } from "./pagefind-summary"; +export { PagefindResults } from "./pagefind-results"; +export { PagefindFilterPane } from "./pagefind-filter-pane"; +export { PagefindFilterDropdown } from "./pagefind-filter-dropdown"; +export { PagefindModal } from "./pagefind-modal"; +export { PagefindModalTrigger } from "./pagefind-modal-trigger"; +export { PagefindModalHeader } from "./pagefind-modal-header"; +export { PagefindModalBody } from "./pagefind-modal-body"; +export { PagefindModalFooter } from "./pagefind-modal-footer"; +export { PagefindKeyboardHints } from "./pagefind-keyboard-hints"; +export { PagefindSearchbox } from "./pagefind-searchbox"; + +export { PagefindElement } from "./base-element"; diff --git a/pagefind_ui/default/svelte/result.svelte b/pagefind_ui/default/svelte/result.svelte new file mode 100644 index 0000000..eb44288 --- /dev/null +++ b/pagefind_ui/default/svelte/result.svelte @@ -0,0 +1,176 @@ + + +
  • + {#if data} + {#if show_images} +
    + {#if data.meta.image} + {data.meta?.image_alt + {/if} +
    + {/if} +
    +

    + {data.meta?.title} +

    +

    {@html data.excerpt}

    + {#if meta.length} +
      + {#each meta as [metaTitle, metaValue]} +
    • + {metaTitle.replace(/^(\w)/, (c) => + c.toLocaleUpperCase(), + )}: {metaValue} +
    • + {/each} +
    + {/if} +
    + {:else} + {#if show_images} +
    + {/if} +
    +

    + {placeholder(30)} +

    +

    + {placeholder(40)} +

    +
    + {/if} +
  • + + diff --git a/pagefind_ui/default/svelte/result_with_subs.svelte b/pagefind_ui/default/svelte/result_with_subs.svelte new file mode 100644 index 0000000..5e0cd4d --- /dev/null +++ b/pagefind_ui/default/svelte/result_with_subs.svelte @@ -0,0 +1,211 @@ + + +
  • + {#if data} + {#if show_images} +
    + {#if data.meta.image} + {data.meta?.image_alt + {/if} +
    + {/if} +
    +

    + {data.meta?.title} +

    + {#if has_root_sub_result} +

    {@html data.excerpt}

    + {/if} + + {#each non_root_sub_results as subres} +
    +

    + {subres.title} +

    +

    {@html subres.excerpt}

    +
    + {/each} + + {#if meta.length} +
      + {#each meta as [metaTitle, metaValue]} +
    • + {metaTitle.replace(/^(\w)/, (c) => c.toLocaleUpperCase())}: {metaValue} +
    • + {/each} +
    + {/if} +
    + {:else} + {#if show_images} +
    + {/if} +
    +

    + {placeholder(30)} +

    +

    + {placeholder(40)} +

    +
    + {/if} +
  • + +