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} +