From df2bb23af421559dba96cd1f4cc3ae5540988e81 Mon Sep 17 00:00:00 2001 From: Puru Vijay <47742487+PuruVJ@users.noreply.github.com> Date: Thu, 16 Feb 2023 16:37:44 +0530 Subject: [PATCH] feat: search and backlink compatability (#8286) * Copy the right files * Finish search * FIx accessibility issues * Add original site compatibility back in * Remove console.log * Reorganize imports * Minor refactor * Fix undefined heading issue * Replace state on redirect * Don't redirect to docs/introduction from navbar * Cleanup search * Cleanup some more(html entities) * Remove console log * Minor style tweaks * Put search in middle --- sites/svelte.dev/package-lock.json | 35 +- sites/svelte.dev/package.json | 4 +- sites/svelte.dev/src/lib/actions/focus.js | 68 +++ sites/svelte.dev/src/lib/search/Search.svelte | 130 ++++++ .../src/lib/search/SearchBox.svelte | 420 ++++++++++++++++++ .../src/lib/search/SearchResultList.svelte | 163 +++++++ .../src/lib/search/SearchResults.svelte | 28 ++ sites/svelte.dev/src/lib/search/content.js | 133 ++++++ sites/svelte.dev/src/lib/search/search.js | 107 +++++ sites/svelte.dev/src/lib/search/stores.js | 8 + sites/svelte.dev/src/lib/search/types.d.ts | 13 + sites/svelte.dev/src/lib/server/docs/index.js | 52 ++- sites/svelte.dev/src/lib/workers/search.js | 26 ++ sites/svelte.dev/src/routes/+layout.svelte | 47 +- .../src/routes/content.json/+server.js | 11 + .../svelte.dev/src/routes/docs/+layout.svelte | 26 +- sites/svelte.dev/src/routes/docs/+page.js | 8 +- sites/svelte.dev/src/routes/docs/+page.svelte | 25 ++ sites/svelte.dev/src/routes/faq/+page.svelte | 21 +- sites/svelte.dev/src/routes/search/+page.js | 1 + .../src/routes/search/+page.server.js | 21 + .../svelte.dev/src/routes/search/+page.svelte | 49 ++ .../src/routes/tutorial/[slug]/+page.svelte | 1 + sites/svelte.dev/static/icons/link.svg | 20 +- sites/svelte.dev/static/icons/search.svg | 6 + 25 files changed, 1370 insertions(+), 53 deletions(-) create mode 100644 sites/svelte.dev/src/lib/actions/focus.js create mode 100644 sites/svelte.dev/src/lib/search/Search.svelte create mode 100644 sites/svelte.dev/src/lib/search/SearchBox.svelte create mode 100644 sites/svelte.dev/src/lib/search/SearchResultList.svelte create mode 100644 sites/svelte.dev/src/lib/search/SearchResults.svelte create mode 100644 sites/svelte.dev/src/lib/search/content.js create mode 100644 sites/svelte.dev/src/lib/search/search.js create mode 100644 sites/svelte.dev/src/lib/search/stores.js create mode 100644 sites/svelte.dev/src/lib/search/types.d.ts create mode 100644 sites/svelte.dev/src/lib/workers/search.js create mode 100644 sites/svelte.dev/src/routes/content.json/+server.js create mode 100644 sites/svelte.dev/src/routes/docs/+page.svelte create mode 100644 sites/svelte.dev/src/routes/search/+page.js create mode 100644 sites/svelte.dev/src/routes/search/+page.server.js create mode 100644 sites/svelte.dev/src/routes/search/+page.svelte create mode 100644 sites/svelte.dev/static/icons/search.svg diff --git a/sites/svelte.dev/package-lock.json b/sites/svelte.dev/package-lock.json index 3462255899..05781d383c 100644 --- a/sites/svelte.dev/package-lock.json +++ b/sites/svelte.dev/package-lock.json @@ -13,8 +13,10 @@ "cookie": "^0.5.0", "devalue": "^4.3.0", "do-not-zip": "^1.0.0", + "flexsearch": "^0.7.31", "flru": "^1.0.2", - "sourcemap-codec": "^1.4.8" + "sourcemap-codec": "^1.4.8", + "svelte-local-storage-store": "^0.4.0" }, "devDependencies": { "@resvg/resvg-js": "^2.4.0", @@ -2069,6 +2071,11 @@ "node": ">=8" } }, + "node_modules/flexsearch": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.31.tgz", + "integrity": "sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==" + }, "node_modules/flru": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flru/-/flru-1.0.2.tgz", @@ -3554,7 +3561,6 @@ "version": "3.55.1", "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz", "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==", - "dev": true, "engines": { "node": ">= 8" } @@ -3598,6 +3604,17 @@ "resolved": "https://registry.npmjs.org/svelte-json-tree/-/svelte-json-tree-1.0.0.tgz", "integrity": "sha512-scs1OdkC8uFpTN4MX0yKkOzZ1/EG3eP1ARC+xcFthXp2IfcwBaXgab0FqA4Am0vQwffNNB+1Gd1LFkJBlynWTA==" }, + "node_modules/svelte-local-storage-store": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/svelte-local-storage-store/-/svelte-local-storage-store-0.4.0.tgz", + "integrity": "sha512-ctPykTt4S3BE5bF0mfV0jKiUR1qlmqLvnAkQvYHLeb9wRyO1MdIFDVI23X+TZEFleATHkTaOpYZswIvf3b2tWA==", + "engines": { + "node": ">=0.14" + }, + "peerDependencies": { + "svelte": "^3.48.0" + } + }, "node_modules/svelte-preprocess": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.1.tgz", @@ -5496,6 +5513,11 @@ "to-regex-range": "^5.0.1" } }, + "flexsearch": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.31.tgz", + "integrity": "sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==" + }, "flru": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flru/-/flru-1.0.2.tgz", @@ -6576,8 +6598,7 @@ "svelte": { "version": "3.55.1", "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz", - "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==", - "dev": true + "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==" }, "svelte-check": { "version": "3.0.3", @@ -6607,6 +6628,12 @@ "resolved": "https://registry.npmjs.org/svelte-json-tree/-/svelte-json-tree-1.0.0.tgz", "integrity": "sha512-scs1OdkC8uFpTN4MX0yKkOzZ1/EG3eP1ARC+xcFthXp2IfcwBaXgab0FqA4Am0vQwffNNB+1Gd1LFkJBlynWTA==" }, + "svelte-local-storage-store": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/svelte-local-storage-store/-/svelte-local-storage-store-0.4.0.tgz", + "integrity": "sha512-ctPykTt4S3BE5bF0mfV0jKiUR1qlmqLvnAkQvYHLeb9wRyO1MdIFDVI23X+TZEFleATHkTaOpYZswIvf3b2tWA==", + "requires": {} + }, "svelte-preprocess": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.1.tgz", diff --git a/sites/svelte.dev/package.json b/sites/svelte.dev/package.json index 7a3bd573ea..e93bac371e 100644 --- a/sites/svelte.dev/package.json +++ b/sites/svelte.dev/package.json @@ -21,8 +21,10 @@ "cookie": "^0.5.0", "devalue": "^4.3.0", "do-not-zip": "^1.0.0", + "flexsearch": "^0.7.31", "flru": "^1.0.2", - "sourcemap-codec": "^1.4.8" + "sourcemap-codec": "^1.4.8", + "svelte-local-storage-store": "^0.4.0" }, "devDependencies": { "@resvg/resvg-js": "^2.4.0", diff --git a/sites/svelte.dev/src/lib/actions/focus.js b/sites/svelte.dev/src/lib/actions/focus.js new file mode 100644 index 0000000000..b688318bf0 --- /dev/null +++ b/sites/svelte.dev/src/lib/actions/focus.js @@ -0,0 +1,68 @@ +/** @param {HTMLElement} node */ +export function focusable_children(node) { + const nodes = Array.from( + node.querySelectorAll( + 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])' + ) + ); + + const index = nodes.indexOf(document.activeElement); + + const update = (d) => { + let i = index + d; + i += nodes.length; + i %= nodes.length; + + // @ts-expect-error + nodes[i].focus(); + }; + + return { + /** @param {string} [selector] */ + next: (selector) => { + const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)]; + + for (let i = 0; i < reordered.length; i += 1) { + if (!selector || reordered[i].matches(selector)) { + reordered[i].focus(); + return; + } + } + }, + /** @param {string} [selector] */ + prev: (selector) => { + const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)]; + + for (let i = reordered.length - 2; i >= 0; i -= 1) { + if (!selector || reordered[i].matches(selector)) { + reordered[i].focus(); + return; + } + } + }, + update + }; +} + +export function trap(node) { + const handle_keydown = (e) => { + if (e.key === 'Tab') { + e.preventDefault(); + + const group = focusable_children(node); + if (e.shiftKey) { + group.prev(); + } else { + group.next(); + } + } + }; + + node.addEventListener('keydown', handle_keydown); + + return { + destroy: () => { + node.removeEventListener('keydown', handle_keydown); + } + }; +} diff --git a/sites/svelte.dev/src/lib/search/Search.svelte b/sites/svelte.dev/src/lib/search/Search.svelte new file mode 100644 index 0000000000..b501a6f441 --- /dev/null +++ b/sites/svelte.dev/src/lib/search/Search.svelte @@ -0,0 +1,130 @@ + + +
+ { + $searching = true; + $query = e.currentTarget.value; + e.currentTarget.value = ''; + }} + on:mousedown|preventDefault={() => ($searching = true)} + on:touchend|preventDefault={() => ($searching = true)} + type="search" + name="q" + placeholder="Search" + aria-label="Search" + spellcheck="false" + /> + + {#if browser} +
+ {navigator.platform === 'MacIntel' ? '⌘' : 'Ctrl'} K +
+ {/if} +
+ + diff --git a/sites/svelte.dev/src/lib/search/SearchBox.svelte b/sites/svelte.dev/src/lib/search/SearchBox.svelte new file mode 100644 index 0000000000..b456db9464 --- /dev/null +++ b/sites/svelte.dev/src/lib/search/SearchBox.svelte @@ -0,0 +1,420 @@ + + + { + if (e.key === 'k' && (navigator.platform === 'MacIntel' ? e.metaKey : e.ctrlKey)) { + e.preventDefault(); + $query = ''; + + if ($searching) { + close(); + } else { + $searching = true; + } + } + + if (e.code === 'Escape') { + close(); + } + }} +/> + +{#if $searching && ready} +